toql_core/backend/
insert.rs

1use crate::{
2    alias_translator::AliasTranslator,
3    backend::{map, Backend},
4    error::ToqlError,
5    parameter_map::ParameterMap,
6    query::field_path::FieldPath,
7    result::Result,
8    sql::Sql,
9    sql_builder::{sql_builder_error::SqlBuilderError, SqlBuilder},
10    sql_expr::resolver::Resolver,
11    table_mapper::{mapped::Mapped, TableMapper},
12    toql_api::{insert::Insert, paths::Paths},
13    tree::{
14        tree_identity::{IdentityAction, TreeIdentity},
15        tree_insert::TreeInsert,
16    },
17};
18use std::{
19    borrow::BorrowMut,
20    cell::RefCell,
21    collections::{HashMap, HashSet},
22};
23
24pub async fn insert<B, Q, T, R, E>(
25    backend: &mut B,
26    entities: &mut [Q],
27    paths: Paths,
28) -> std::result::Result<(), E>
29where
30    Q: BorrowMut<T>,
31    T: Insert,
32    B: Backend<R, E>,
33    E: From<ToqlError>,
34{
35    {
36        let registry = &mut *backend.registry_mut()?;
37        map::map::<T>(registry)?;
38    }
39
40    // Build up execution tree
41    // Path `a_b_merge1_c_d_merge2_e` becomes
42    // [j0] = [a, c, e]
43    // [j1] = [a_b, c_d]
44    // [m] = [merge1, merge2]
45    // [p0] = [a, c, e]
46    // [p1] = [a_b, c_d]
47    // Then execution order is [j1], [j0], [m], [p0], [p1]
48
49    let mut joins: Vec<HashSet<String>> = Vec::new();
50    let mut partials: Vec<HashSet<String>> = Vec::new();
51    let mut merges: HashSet<String> = HashSet::new();
52
53    plan_insert_order::<T, _>(
54        &backend.registry()?.mappers,
55        paths.list.as_ref(),
56        &mut joins,
57        &mut merges,
58        &mut partials,
59    )?;
60
61    // Insert joins from bottom to top
62    for l in (0..joins.len()).rev() {
63        for p in joins.get(l).unwrap() {
64            let path = FieldPath::from(&p);
65
66            let sql = build_insert_sql(
67                backend,
68                entities,
69                &path,
70                &mut std::iter::repeat(&true),
71                "",
72                "",
73            )?;
74            insert_sql(backend, path, sql, entities).await?;
75        }
76    }
77
78    // Insert root
79    let home_path = FieldPath::default();
80    let sql = build_insert_sql(
81        backend,
82        entities,
83        &home_path,
84        &mut std::iter::repeat(&true),
85        "",
86        "",
87    )?;
88    insert_sql(backend, home_path, sql, entities).await?;
89
90    // Insert merges
91    for p in &merges {
92        let path = FieldPath::from(&p);
93
94        let sql = build_insert_sql(
95            backend,
96            entities,
97            &path,
98            &mut std::iter::repeat(&true),
99            "",
100            "",
101        )?;
102
103        insert_sql(backend, path, sql, entities).await?;
104    }
105
106    // Insert partials from top to bottom
107    for l in 0..partials.len() {
108        for p in partials.get(l).unwrap() {
109            // Ensure not already inserted (unsure if needed)
110            if merges.contains(p) {
111                continue;
112            }
113
114            let path = FieldPath::from(&p);
115
116            let sql = build_insert_sql(
117                backend,
118                entities,
119                &path,
120                &mut std::iter::repeat(&true),
121                "",
122                "",
123            )?;
124
125            insert_sql(backend, path, sql, entities).await?;
126        }
127    }
128    Ok(())
129}
130
131pub(crate) async fn insert_sql<'a, Q, B, T, R, E>(
132    backend: &mut B,
133    path: FieldPath<'_>,
134    sql: Option<Sql>,
135    entities: &mut [Q],
136) -> std::result::Result<(), E>
137where
138    B: Backend<R, E>,
139    Q: BorrowMut<T>,
140    T: TreeIdentity,
141    E: From<ToqlError>,
142{
143    if sql.is_none() {
144        return Ok(());
145    }
146    let sql = sql.unwrap();
147
148    let descendents = path.children();
149    if <T as TreeIdentity>::auto_id(descendents)? {
150        let ids = backend.insert_sql(sql).await?;
151        set_tree_identity(
152            IdentityAction::Set(RefCell::new(ids)),
153            entities,
154            path.children(),
155        )?;
156    } else {
157        backend.execute_sql(sql).await?;
158    }
159    Ok(())
160}
161
162pub(crate) fn set_tree_identity<'a, T, Q, I>(
163    action: IdentityAction,
164    entities: &mut [Q],
165    descendents: I,
166) -> Result<()>
167where
168    T: TreeIdentity,
169    Q: BorrowMut<T>,
170    I: Iterator<Item = FieldPath<'a>> + Clone,
171{
172    for e in entities.iter_mut() {
173        let e_mut = e.borrow_mut();
174        <T as TreeIdentity>::set_id(e_mut, descendents.clone(), &action)?;
175    }
176
177    Ok(())
178}
179
180pub(crate) fn build_insert_sql<'a, T, Q, B, R, E, J>(
181    backend: &mut B,
182    entities: &[Q],
183    query_path: &FieldPath,
184    inserts: &mut J,
185    _modifier: &str,
186    _extra: &str,
187) -> Result<Option<Sql>>
188where
189    B: Backend<R, E>,
190    T: Mapped + TreeInsert,
191    Q: BorrowMut<T>,
192    E: From<ToqlError>,
193    J: Iterator<Item = &'a bool>,
194{
195    use crate::sql_expr::SqlExpr;
196
197    let ty = <T as Mapped>::type_name();
198
199    let mut values_expr = SqlExpr::new();
200
201    let mut d = query_path.children();
202    let columns_expr = <T as TreeInsert>::columns(&mut d)?;
203    for e in entities {
204        //let mut d = query_path.children();
205        <T as TreeInsert>::values(
206            e.borrow(),
207            query_path.children(),
208            backend.roles(),
209            inserts,
210            &mut values_expr,
211        )?;
212    }
213    if values_expr.is_empty() {
214        return Ok(None);
215    }
216
217    let mut alias_translator = AliasTranslator::new(backend.alias_format());
218
219    let registry = &*backend.registry()?;
220    let sql_builder = SqlBuilder::new(&ty, registry);
221    let mapper = sql_builder.mapper_for_query_path(query_path)?;
222    let canonical_table_alias = &mapper.canonical_table_alias;
223    let table_name = &mapper.table_name;
224
225    let aux_params = [backend.aux_params()];
226    let aux_params_map = ParameterMap::new(&aux_params);
227    let resolver = Resolver::new()
228        .with_aux_params(&aux_params_map)
229        .with_self_alias(&canonical_table_alias);
230    let columns_sql = resolver
231        .to_sql(&columns_expr, &mut alias_translator)
232        .map_err(ToqlError::from)?;
233    let values_sql = resolver
234        .to_sql(&values_expr, &mut alias_translator)
235        .map_err(ToqlError::from)?;
236
237    let mut insert_stmt = String::from("INSERT INTO ");
238    insert_stmt.push_str(&table_name);
239    insert_stmt.push(' ');
240    insert_stmt.push_str(&columns_sql.0);
241    insert_stmt.push_str(" VALUES ");
242    insert_stmt.push_str(&values_sql.0);
243
244    insert_stmt.pop(); // Remove ', '
245    insert_stmt.pop();
246
247    Ok(Some(Sql(insert_stmt, values_sql.1)))
248}
249
250pub fn plan_insert_order<T, S: AsRef<str>>(
251    mappers: &HashMap<String, TableMapper>,
252    paths: &[S],
253    joins: &mut Vec<HashSet<String>>,
254    merges: &mut HashSet<String>,
255    partials: &mut Vec<HashSet<String>>,
256) -> Result<()>
257where
258    T: Mapped,
259{
260    let ty = <T as Mapped>::type_name();
261    // Add partials for root
262    insert_partial_tables_order(mappers, &ty, 0, &FieldPath::default(), partials)?;
263
264    for path in paths {
265        let field_path = FieldPath::from(path.as_ref().trim_end_matches('_'));
266        let steps = field_path.step_down();
267        let children = field_path.children();
268        let mut level = 0;
269        let mut mapper = mappers
270            .get(&ty)
271            .ok_or_else(|| ToqlError::MapperMissing(ty.to_owned()))?;
272
273        for (d, c) in steps.zip(children) {
274            if let Some(j) = mapper.joined_mapper(c.as_str()) {
275                if joins.len() <= level {
276                    joins.push(HashSet::new());
277                }
278                if partials.len() <= level {
279                    partials.push(HashSet::new());
280                }
281                if !mapper.is_partial_join(c.as_str()) {
282                    joins.get_mut(level).unwrap().insert(d.as_str().to_string());
283                } else {
284                    partials
285                        .get_mut(level)
286                        .unwrap()
287                        .insert(d.as_str().to_string());
288                    insert_partial_tables_order(mappers, &j, level + 1, &d, partials)?;
289                }
290
291                level += 1;
292                mapper = mappers
293                    .get(&j)
294                    .ok_or_else(|| ToqlError::MapperMissing(j.to_owned()))?;
295            } else if let Some(m) = mapper.merged_mapper(c.as_str()) {
296                level = 0;
297                merges.insert(d.as_str().to_string());
298                mapper = mappers
299                    .get(&m)
300                    .ok_or_else(|| ToqlError::MapperMissing(m.to_owned()))?;
301                insert_partial_tables_order(mappers, &m, level, &d, partials)?;
302            } else {
303                return Err(SqlBuilderError::JoinMissing(
304                    c.as_str().to_owned(),
305                    mapper.table_name.to_string(),
306                )
307                .into());
308            }
309        }
310    }
311
312    Ok(())
313}
314
315fn insert_partial_tables_order(
316    mappers: &HashMap<String, TableMapper>,
317    mapper_name: &str,
318    level: usize,
319    query_path: &FieldPath,
320    joins_or_merges: &mut Vec<HashSet<String>>,
321) -> Result<()> {
322    let mapper = mappers
323        .get(mapper_name)
324        .ok_or_else(|| ToqlError::MapperMissing(mapper_name.to_owned()))?;
325    let partial_joins: Vec<(String, String)> = mapper.joined_partial_mappers();
326
327    for (path, mapper_name) in &partial_joins {
328        let qp = query_path.append(path);
329        if joins_or_merges.len() <= level {
330            joins_or_merges.push(HashSet::new())
331        }
332        insert_partial_tables_order(&mappers, &mapper_name, level + 1, &qp, joins_or_merges)?;
333        joins_or_merges
334            .get_mut(level)
335            .unwrap()
336            .insert(qp.as_str().to_string());
337    }
338
339    Ok(())
340}