good_ormning/sqlite/
mod.rs

1use {
2    self::{
3        graph::{
4            constraint::NodeConstraint_,
5            field::NodeField_,
6            index::NodeIndex_,
7            table::NodeTable_,
8            utils::MigrateNode,
9            GraphId,
10            Node,
11        },
12        query::{
13            delete::Delete,
14            expr::Expr,
15            insert::{
16                Insert,
17                InsertConflict,
18            },
19            select::Select,
20            select_body::{
21                Join,
22                JoinSource,
23                NamedSelectSource,
24                Order,
25                Returning,
26            },
27            update::Update,
28            utils::{
29                QueryBody,
30                SqliteQueryCtx,
31            },
32        },
33        schema::{
34            constraint::{
35                Constraint,
36                ConstraintType,
37                Constraint_,
38                SchemaConstraintId,
39            },
40            field::{
41                Field,
42                FieldType,
43                Field_,
44                SchemaFieldId,
45            },
46            index::{
47                Index,
48                Index_,
49                SchemaIndexId,
50            },
51            table::{
52                SchemaTableId,
53                Table,
54                Table_,
55            },
56        },
57        types::{
58            SimpleSimpleType,
59            SimpleType,
60        },
61    },
62    crate::{
63        sqlite::{
64            graph::utils::SqliteMigrateCtx,
65            query::expr::Binding,
66            types::{
67                to_rust_types,
68                Type,
69            },
70        },
71        utils::{
72            sanitize_ident,
73            Errs,
74        },
75    },
76    proc_macro2::{
77        Ident,
78        TokenStream,
79    },
80    query::{
81        select_body::{
82            SelectBody,
83            SelectJunction,
84        },
85        utils::With,
86    },
87    quote::{
88        format_ident,
89        quote,
90        ToTokens,
91    },
92    std::{
93        collections::{
94            BTreeMap,
95            HashMap,
96            HashSet,
97        },
98        fs,
99        path::Path,
100        rc::Rc,
101    },
102};
103
104pub mod types;
105pub mod query;
106pub mod schema;
107pub mod graph;
108
109/// The number of results this query returns. This determines if the return type is
110/// void, `Option`, the value directly, or a `Vec`. It must be a valid value per
111/// the query body (e.g. select can't have `None` res count).
112#[derive(Debug, Clone)]
113pub enum QueryResCount {
114    None,
115    MaybeOne,
116    One,
117    Many,
118}
119
120/// See Insert for field descriptions. Call `build()` to get a finished query
121/// object.
122pub struct InsertBuilder {
123    pub q: Insert,
124}
125
126impl InsertBuilder {
127    // Add a `WITH`/CTE to the query.
128    pub fn with(mut self, w: With) -> Self {
129        self.q.with = Some(w);
130        return self;
131    }
132
133    pub fn on_conflict(mut self, v: InsertConflict) -> Self {
134        self.q.on_conflict = Some(v);
135        self
136    }
137
138    pub fn return_(mut self, v: Expr) -> Self {
139        self.q.returning.push(Returning {
140            e: v,
141            rename: None,
142        });
143        self
144    }
145
146    pub fn return_named(mut self, name: impl ToString, v: Expr) -> Self {
147        self.q.returning.push(Returning {
148            e: v,
149            rename: Some(name.to_string()),
150        });
151        self
152    }
153
154    pub fn return_field(mut self, f: &Field) -> Self {
155        self.q.returning.push(Returning {
156            e: Expr::Binding(Binding::field(f)),
157            rename: None,
158        });
159        self
160    }
161
162    pub fn return_fields(mut self, f: &[&Field]) -> Self {
163        for f in f {
164            self.q.returning.push(Returning {
165                e: Expr::Binding(Binding::field(f)),
166                rename: None,
167            });
168        }
169        self
170    }
171
172    pub fn returns_from_iter(mut self, f: impl Iterator<Item = Returning>) -> Self {
173        self.q.returning.extend(f);
174        self
175    }
176
177    /// Produce a migration for use in version pre/post-migration.
178    pub fn build_migration(self) -> Insert {
179        self.q
180    }
181
182    /// Produce a query object.
183    ///
184    /// # Arguments
185    ///
186    /// * `name` - This is used as the name of the rust function.
187    pub fn build_query(self, name: impl ToString, res_count: QueryResCount) -> Query {
188        Query {
189            name: name.to_string(),
190            body: Box::new(self.q),
191            res_count: res_count,
192            res_name: None,
193        }
194    }
195
196    /// Same as `build_query`, but specify a name for the result structure. Only valid
197    /// if result is a record (not a single value).
198    pub fn build_query_named_res(self, name: impl ToString, res_count: QueryResCount, res_name: impl ToString) -> Query {
199        Query {
200            name: name.to_string(),
201            body: Box::new(self.q),
202            res_count: res_count,
203            res_name: Some(res_name.to_string()),
204        }
205    }
206}
207
208/// See Select for field descriptions. Call `build()` to get a finished query
209/// object.
210pub struct SelectBuilder {
211    pub q: Select,
212}
213
214impl SelectBuilder {
215    pub fn distinct(mut self) -> Self {
216        self.q.body.distinct = true;
217        return self;
218    }
219
220    // Add a `WITH`/CTE to the query.
221    pub fn with(mut self, w: With) -> Self {
222        self.q.with = Some(w);
223        return self;
224    }
225
226    pub fn return_(mut self, v: Expr) -> Self {
227        self.q.body.returning.push(Returning {
228            e: v,
229            rename: None,
230        });
231        self
232    }
233
234    pub fn return_named(mut self, name: impl ToString, v: Expr) -> Self {
235        self.q.body.returning.push(Returning {
236            e: v,
237            rename: Some(name.to_string()),
238        });
239        self
240    }
241
242    pub fn return_field(mut self, f: &Field) -> Self {
243        self.q.body.returning.push(Returning {
244            e: Expr::Binding(Binding::field(f)),
245            rename: None,
246        });
247        self
248    }
249
250    pub fn return_fields(mut self, f: &[&Field]) -> Self {
251        for f in f {
252            self.q.body.returning.push(Returning {
253                e: Expr::Binding(Binding::field(f)),
254                rename: None,
255            });
256        }
257        self
258    }
259
260    pub fn returns_from_iter(mut self, f: impl Iterator<Item = Returning>) -> Self {
261        self.q.body.returning.extend(f);
262        self
263    }
264
265    pub fn join(mut self, join: Join) -> Self {
266        self.q.body.join.push(join);
267        self
268    }
269
270    pub fn where_(mut self, predicate: Expr) -> Self {
271        self.q.body.where_ = Some(predicate);
272        self
273    }
274
275    pub fn group(mut self, clauses: Vec<Expr>) -> Self {
276        self.q.body.group = clauses;
277        self
278    }
279
280    pub fn order(mut self, expr: Expr, order: Order) -> Self {
281        self.q.body.order.push((expr, order));
282        self
283    }
284
285    pub fn order_from_iter(mut self, clauses: impl Iterator<Item = (Expr, Order)>) -> Self {
286        self.q.body.order.extend(clauses);
287        self
288    }
289
290    /// Sets `LIMIT`. `v` must evaluate to a number.
291    pub fn limit(mut self, v: Expr) -> Self {
292        self.q.body.limit = Some(v);
293        self
294    }
295
296    /// Add a UNION/INTERSECT/EXCEPT junction to the query.
297    pub fn junction(mut self, j: SelectJunction) -> Self {
298        self.q.body_junctions.push(j);
299        return self;
300    }
301
302    /// Produce a migration for use in version pre/post-migration.
303    pub fn build_migration(self) -> Select {
304        self.q
305    }
306
307    /// Produce a query object.
308    ///
309    /// # Arguments
310    ///
311    /// * `name` - This is used as the name of the rust function.
312    pub fn build_query(self, name: impl ToString, res_count: QueryResCount) -> Query {
313        Query {
314            name: name.to_string(),
315            body: Box::new(self.q),
316            res_count: res_count,
317            res_name: None,
318        }
319    }
320
321    // Same as `build_query`, but specify a name for the result structure. Only valid
322    // if result is a record (not a single value).
323    pub fn build_query_named_res(self, name: impl ToString, res_count: QueryResCount, res_name: impl ToString) -> Query {
324        Query {
325            name: name.to_string(),
326            body: Box::new(self.q),
327            res_count: res_count,
328            res_name: Some(res_name.to_string()),
329        }
330    }
331}
332
333/// See SelectBody for field descriptions. Call `build()` to get a finished query
334/// object.
335pub struct SelectBodyBuilder {
336    pub q: SelectBody,
337}
338
339impl SelectBodyBuilder {
340    pub fn distinct(mut self) -> Self {
341        self.q.distinct = true;
342        return self;
343    }
344
345    pub fn return_(mut self, v: Expr) -> Self {
346        self.q.returning.push(Returning {
347            e: v,
348            rename: None,
349        });
350        self
351    }
352
353    pub fn return_named(mut self, name: impl ToString, v: Expr) -> Self {
354        self.q.returning.push(Returning {
355            e: v,
356            rename: Some(name.to_string()),
357        });
358        self
359    }
360
361    pub fn return_field(mut self, f: &Field) -> Self {
362        self.q.returning.push(Returning {
363            e: Expr::Binding(Binding::field(f)),
364            rename: None,
365        });
366        self
367    }
368
369    pub fn return_fields(mut self, f: &[&Field]) -> Self {
370        for f in f {
371            self.q.returning.push(Returning {
372                e: Expr::Binding(Binding::field(f)),
373                rename: None,
374            });
375        }
376        self
377    }
378
379    pub fn returns_from_iter(mut self, f: impl Iterator<Item = Returning>) -> Self {
380        self.q.returning.extend(f);
381        self
382    }
383
384    pub fn join(mut self, join: Join) -> Self {
385        self.q.join.push(join);
386        self
387    }
388
389    pub fn where_(mut self, predicate: Expr) -> Self {
390        self.q.where_ = Some(predicate);
391        self
392    }
393
394    pub fn group(mut self, clauses: Vec<Expr>) -> Self {
395        self.q.group = clauses;
396        self
397    }
398
399    pub fn order(mut self, expr: Expr, order: Order) -> Self {
400        self.q.order.push((expr, order));
401        self
402    }
403
404    pub fn order_from_iter(mut self, clauses: impl Iterator<Item = (Expr, Order)>) -> Self {
405        self.q.order.extend(clauses);
406        self
407    }
408
409    /// Sets `LIMIT`. `v` must evaluate to a number.
410    pub fn limit(mut self, v: Expr) -> Self {
411        self.q.limit = Some(v);
412        self
413    }
414
415    /// Produce a select body object.
416    pub fn build(self) -> SelectBody {
417        return self.q;
418    }
419}
420
421/// See Update for field descriptions. Call `build()` to get a finished query
422/// object.
423pub struct UpdateBuilder {
424    pub q: Update,
425}
426
427impl UpdateBuilder {
428    // Add a `WITH`/CTE to the query.
429    pub fn with(mut self, w: With) -> Self {
430        self.q.with = Some(w);
431        return self;
432    }
433
434    pub fn where_(mut self, v: Expr) -> Self {
435        self.q.where_ = Some(v);
436        self
437    }
438
439    pub fn return_(mut self, v: Expr) -> Self {
440        self.q.returning.push(Returning {
441            e: v,
442            rename: None,
443        });
444        self
445    }
446
447    pub fn return_named(mut self, name: impl ToString, v: Expr) -> Self {
448        self.q.returning.push(Returning {
449            e: v,
450            rename: Some(name.to_string()),
451        });
452        self
453    }
454
455    pub fn return_field(mut self, f: &Field) -> Self {
456        self.q.returning.push(Returning {
457            e: Expr::Binding(Binding::field(f)),
458            rename: None,
459        });
460        self
461    }
462
463    pub fn return_fields(mut self, f: &[&Field]) -> Self {
464        for f in f {
465            self.q.returning.push(Returning {
466                e: Expr::Binding(Binding::field(f)),
467                rename: None,
468            });
469        }
470        self
471    }
472
473    pub fn returns_from_iter(mut self, f: impl Iterator<Item = Returning>) -> Self {
474        self.q.returning.extend(f);
475        self
476    }
477
478    // Produce a migration for use in version pre/post-migration.
479    pub fn build_migration(self) -> Update {
480        self.q
481    }
482
483    // Produce a query object.
484    //
485    // # Arguments
486    //
487    // * `name` - This is used as the name of the rust function.
488    pub fn build_query(self, name: impl ToString, res_count: QueryResCount) -> Query {
489        Query {
490            name: name.to_string(),
491            body: Box::new(self.q),
492            res_count: res_count,
493            res_name: None,
494        }
495    }
496
497    // Same as `build_query`, but specify a name for the result structure. Only valid
498    // if result is a record (not a single value).
499    pub fn build_query_named_res(self, name: impl ToString, res_count: QueryResCount, res_name: impl ToString) -> Query {
500        Query {
501            name: name.to_string(),
502            body: Box::new(self.q),
503            res_count: res_count,
504            res_name: Some(res_name.to_string()),
505        }
506    }
507}
508
509/// See Delete for field descriptions. Call `build()` to get a finished query
510/// object.
511pub struct DeleteBuilder {
512    pub q: Delete,
513}
514
515impl DeleteBuilder {
516    // Add a `WITH`/CTE to the query.
517    pub fn with(mut self, w: With) -> Self {
518        self.q.with = Some(w);
519        return self;
520    }
521
522    pub fn where_(mut self, v: Expr) -> Self {
523        self.q.where_ = Some(v);
524        self
525    }
526
527    pub fn return_(mut self, v: Expr) -> Self {
528        self.q.returning.push(Returning {
529            e: v,
530            rename: None,
531        });
532        self
533    }
534
535    pub fn return_named(mut self, name: impl ToString, v: Expr) -> Self {
536        self.q.returning.push(Returning {
537            e: v,
538            rename: Some(name.to_string()),
539        });
540        self
541    }
542
543    pub fn return_field(mut self, f: &Field) -> Self {
544        self.q.returning.push(Returning {
545            e: Expr::Binding(Binding::field(f)),
546            rename: None,
547        });
548        self
549    }
550
551    pub fn return_fields(mut self, f: &[&Field]) -> Self {
552        for f in f {
553            self.q.returning.push(Returning {
554                e: Expr::Binding(Binding::field(f)),
555                rename: None,
556            });
557        }
558        self
559    }
560
561    pub fn returns_from_iter(mut self, f: impl Iterator<Item = Returning>) -> Self {
562        self.q.returning.extend(f);
563        self
564    }
565
566    // Produce a migration for use in version pre/post-migration.
567    pub fn build_migration(self) -> Delete {
568        self.q
569    }
570
571    // Produce a query object.
572    //
573    // # Arguments
574    //
575    // * `name` - This is used as the name of the rust function.
576    pub fn build_query(self, name: impl ToString, res_count: QueryResCount) -> Query {
577        Query {
578            name: name.to_string(),
579            body: Box::new(self.q),
580            res_count: res_count,
581            res_name: None,
582        }
583    }
584
585    // Same as `build_query`, but specify a name for the result structure. Only valid
586    // if result is a record (not a single value).
587    pub fn build_query_named_res(self, name: impl ToString, res_count: QueryResCount, res_name: impl ToString) -> Query {
588        Query {
589            name: name.to_string(),
590            body: Box::new(self.q),
591            res_count: res_count,
592            res_name: Some(res_name.to_string()),
593        }
594    }
595}
596
597/// This represents an SQL query. A function will be generated which accepts a db
598/// connection and query parameters, and returns the query results. Call the
599/// `new_*` functions to get a builder.
600pub struct Query {
601    pub name: String,
602    pub body: Box<dyn QueryBody>,
603    pub res_count: QueryResCount,
604    pub res_name: Option<String>,
605}
606
607/// Get a builder for an INSERT query.
608///
609/// # Arguments
610///
611/// * `values` - The fields to insert and their corresponding values
612pub fn new_insert(table: &Table, values: Vec<(Field, Expr)>) -> InsertBuilder {
613    let mut unique = HashSet::new();
614    for v in &values {
615        if !unique.insert(&v.0) {
616            panic!("Duplicate field {} in insert", v.0);
617        }
618    }
619    InsertBuilder { q: Insert {
620        with: None,
621        table: table.clone(),
622        values: values,
623        on_conflict: None,
624        returning: vec![],
625    } }
626}
627
628/// Get a builder for a SELECT query.
629pub fn new_select(table: &Table) -> SelectBuilder {
630    SelectBuilder { q: Select {
631        with: None,
632        body: SelectBody {
633            distinct: false,
634            table: NamedSelectSource {
635                source: JoinSource::Table(table.clone()),
636                alias: None,
637            },
638            returning: vec![],
639            join: vec![],
640            where_: None,
641            group: vec![],
642            order: vec![],
643            limit: None,
644        },
645        body_junctions: vec![],
646    } }
647}
648
649/// Get a builder for a SELECT query. This allows advanced sources (like selecting
650/// from a synthetic table).
651pub fn new_select_from(source: NamedSelectSource) -> SelectBuilder {
652    SelectBuilder { q: Select {
653        with: None,
654        body: SelectBody {
655            distinct: false,
656            table: source,
657            returning: vec![],
658            join: vec![],
659            where_: None,
660            group: vec![],
661            order: vec![],
662            limit: None,
663        },
664        body_junctions: vec![],
665    } }
666}
667
668/// Get a builder for an inner SELECT, such as in a CTE, subquery, JOIN, etc.
669pub fn new_select_body(table: &Table) -> SelectBodyBuilder {
670    SelectBodyBuilder { q: SelectBody {
671        distinct: false,
672        table: NamedSelectSource {
673            source: JoinSource::Table(table.clone()),
674            alias: None,
675        },
676        returning: vec![],
677        join: vec![],
678        where_: None,
679        group: vec![],
680        order: vec![],
681        limit: None,
682    } }
683}
684
685/// Get a builder for an UPDATE query.
686///
687/// # Arguments
688///
689/// * `values` - The fields to update and their corresponding values
690pub fn new_update(table: &Table, values: Vec<(Field, Expr)>) -> UpdateBuilder {
691    let mut unique = HashSet::new();
692    for v in &values {
693        if !unique.insert(&v.0) {
694            panic!("Duplicate field {} in update", v.0);
695        }
696    }
697    UpdateBuilder { q: Update {
698        with: None,
699        table: table.clone(),
700        values: values,
701        where_: None,
702        returning: vec![],
703    } }
704}
705
706/// Get a builder for a DELETE query.
707///
708/// # Arguments
709///
710/// * `name` - This becomes the name of the generated rust function.
711pub fn new_delete(table: &Table) -> DeleteBuilder {
712    DeleteBuilder { q: Delete {
713        with: None,
714        table: table.clone(),
715        returning: vec![],
716        where_: None,
717    } }
718}
719
720/// The version represents the state of a schema at a point in time.
721#[derive(Default)]
722pub struct Version {
723    schema: BTreeMap<GraphId, MigrateNode>,
724    pre_migration: Vec<Box<dyn QueryBody>>,
725    post_migration: Vec<Box<dyn QueryBody>>,
726}
727
728impl Version {
729    /// Define a table in this version
730    pub fn table(&mut self, schema_id: &str, id: &str) -> Table {
731        let out = Table(Rc::new(Table_ {
732            schema_id: SchemaTableId(schema_id.into()),
733            id: id.into(),
734        }));
735        if self.schema.insert(GraphId::Table(out.schema_id.clone()), MigrateNode::new(vec![], Node::table(NodeTable_ {
736            def: out.clone(),
737            fields: vec![],
738            constraints: vec![],
739        }))).is_some() {
740            panic!("Table with schema id {} already exists", out.schema_id);
741        };
742        out
743    }
744
745    /// Add a query to execute before before migrating to this schema (applied
746    /// immediately before migration).  Note that these may not run on new databases or
747    /// if you later delete early migrations, so these should only modify existing data
748    /// and not create new data (singleton rows, etc).  If you need those, do it with a
749    /// normal query executed manually against the latest version.
750    pub fn pre_migration(&mut self, q: impl QueryBody + 'static) {
751        self.pre_migration.push(Box::new(q));
752    }
753
754    /// Add a query to execute after migrating to this schema version (applied
755    /// immediately after migration). See other warnings from `pre_migration`.
756    pub fn post_migration(&mut self, q: impl QueryBody + 'static) {
757        self.post_migration.push(Box::new(q));
758    }
759}
760
761impl Table {
762    /// Define a field
763    pub fn field(&self, v: &mut Version, schema_id: impl ToString, id: impl ToString, type_: FieldType) -> Field {
764        let out = Field(Rc::new(Field_ {
765            table: self.clone(),
766            schema_id: SchemaFieldId(schema_id.to_string()),
767            id: id.to_string(),
768            type_: type_,
769        }));
770        if &out.id == "rowid" {
771            panic!("Use rowid_field to define a rowid field");
772        }
773        if v
774            .schema
775            .insert(
776                GraphId::Field(self.schema_id.clone(), out.schema_id.clone()),
777                MigrateNode::new(
778                    vec![GraphId::Table(self.schema_id.clone())],
779                    Node::field(NodeField_ { def: out.clone() }),
780                ),
781            )
782            .is_some() {
783            panic!("Field with schema id {}.{} already exists", self.schema_id, out.schema_id);
784        };
785        out
786    }
787
788    pub fn rowid_field(&self, v: &mut Version, custom_type: Option<String>) -> Field {
789        let out = Field(Rc::new(Field_ {
790            table: self.clone(),
791            schema_id: SchemaFieldId("rowid".into()),
792            id: "rowid".into(),
793            type_: FieldType {
794                type_: Type {
795                    type_: SimpleType {
796                        type_: SimpleSimpleType::I64,
797                        custom: custom_type,
798                    },
799                    opt: false,
800                    array: false,
801                },
802                migration_default: None,
803            },
804        }));
805        if v
806            .schema
807            .insert(
808                GraphId::Field(self.schema_id.clone(), out.schema_id.clone()),
809                MigrateNode::new(
810                    vec![GraphId::Table(self.schema_id.clone())],
811                    Node::field(NodeField_ { def: out.clone() }),
812                ),
813            )
814            .is_some() {
815            panic!("Field with schema id {}.{} already exists", self.schema_id, out.schema_id);
816        };
817        out
818    }
819
820    /// Define a constraint
821    pub fn constraint(&self, v: &mut Version, schema_id: impl ToString, id: impl ToString, type_: ConstraintType) {
822        let out = Constraint(Rc::new(Constraint_ {
823            table: self.clone(),
824            schema_id: SchemaConstraintId(schema_id.to_string()),
825            id: id.to_string(),
826            type_: type_,
827        }));
828        let mut deps = vec![GraphId::Table(self.schema_id.clone())];
829        match &out.type_ {
830            ConstraintType::PrimaryKey(x) => {
831                for f in &x.fields {
832                    if &f.table != self {
833                        panic!(
834                            "Field {} in primary key constraint {} is in table {}, but constraint is in table {}",
835                            f,
836                            out.id,
837                            f.table,
838                            self
839                        );
840                    }
841                    deps.push(GraphId::Field(self.schema_id.clone(), f.schema_id.clone()));
842                }
843            },
844            ConstraintType::ForeignKey(x) => {
845                let mut last_foreign_table: Option<Field> = None;
846                for f in &x.fields {
847                    if &f.0.table != self {
848                        panic!(
849                            "Local field {} in foreign key constraint {} is in table {}, but constraint is in table {}",
850                            f.0,
851                            out.id,
852                            f.0.table,
853                            self
854                        );
855                    }
856                    deps.push(GraphId::Field(f.0.table.schema_id.clone(), f.0.schema_id.clone()));
857                    if let Some(t) = last_foreign_table.take() {
858                        if t.table != f.1.table {
859                            panic!(
860                                "Foreign field {} in foreign key constraint {} is in table {}, but constraint is in table {}",
861                                f.1,
862                                out.id,
863                                f.1.table,
864                                self
865                            );
866                        }
867                    }
868                    last_foreign_table = Some(f.1.clone());
869                    deps.push(GraphId::Field(f.1.table.schema_id.clone(), f.1.schema_id.clone()));
870                }
871            },
872        }
873        if v
874            .schema
875            .insert(
876                GraphId::Constraint(self.schema_id.clone(), out.schema_id.clone()),
877                MigrateNode::new(deps, Node::table_constraint(NodeConstraint_ { def: out.clone() })),
878            )
879            .is_some() {
880            panic!("Constraint with schema id {}.{} aleady exists", self.schema_id, out.schema_id)
881        };
882    }
883
884    /// Define an index
885    pub fn index(&self, schema_id: impl ToString, id: impl ToString, fields: &[&Field]) -> IndexBuilder {
886        IndexBuilder {
887            table: self.clone(),
888            schema_id: schema_id.to_string(),
889            id: id.to_string(),
890            fields: fields.iter().map(|e| (*e).clone()).collect(),
891            unique: false,
892        }
893    }
894}
895
896pub struct IndexBuilder {
897    table: Table,
898    schema_id: String,
899    id: String,
900    fields: Vec<Field>,
901    unique: bool,
902}
903
904impl IndexBuilder {
905    pub fn unique(mut self) -> Self {
906        self.unique = true;
907        self
908    }
909
910    pub fn build(self, v: &mut Version) -> Index {
911        let mut deps = vec![GraphId::Table(self.table.schema_id.clone())];
912        for field in &self.fields {
913            deps.push(GraphId::Field(field.table.schema_id.clone(), field.schema_id.clone()));
914        }
915        let out = Index(Rc::new(Index_ {
916            table: self.table,
917            schema_id: SchemaIndexId(self.schema_id),
918            id: self.id,
919            fields: self.fields,
920            unique: self.unique,
921        }));
922        if v
923            .schema
924            .insert(
925                GraphId::Index(out.table.schema_id.clone(), out.schema_id.clone()),
926                MigrateNode::new(deps, Node::table_index(NodeIndex_ { def: out.clone() })),
927            )
928            .is_some() {
929            panic!("Index with schema id {}.{} already exists", out.table.schema_id, out.schema_id);
930        };
931        out
932    }
933}
934
935/// Generate Rust code for migrations and queries.
936///
937/// # Arguments
938///
939/// * `output` - the path to a single rust source file where the output will be written
940///
941/// * `versions` - a list of database version ids and schema versions. The ids must be
942///   consecutive but can start from any number. Once a version has been applied to a
943///   production database it shouldn't be modified again (modifications should be done
944///   in a new version).
945///
946///   These will be turned into migrations as part of the `migrate` function.
947///
948/// * `queries` - a list of queries against the schema in the latest version. These
949///   will be turned into functions.
950///
951/// # Returns
952///
953/// * Error - a list of validation or generation errors that occurred
954pub fn generate(output: &Path, versions: Vec<(usize, Version)>, queries: Vec<Query>) -> Result<(), Vec<String>> {
955    {
956        let mut prev_relations: HashMap<&String, String> = HashMap::new();
957        let mut prev_fields = HashMap::new();
958        let mut prev_constraints = HashMap::new();
959        for (v_i, v) in &versions {
960            let mut relations = HashMap::new();
961            let mut fields = HashMap::new();
962            let mut constraints = HashMap::new();
963            for n in v.schema.values() {
964                match &n.body {
965                    Node::Table(t) => {
966                        let id = &t.def.id;
967                        let comp_id = format!("table {}", t.def.schema_id);
968                        if relations.insert(id, comp_id.clone()).is_some() {
969                            panic!("Duplicate table id {} -- {}", t.def.id, t.def);
970                        }
971                        if let Some(schema_id) = prev_relations.get(id) {
972                            if schema_id != &comp_id {
973                                panic!(
974                                    "Table {} id in version {} swapped with another relation since previous version; unsupported",
975                                    t.def,
976                                    v_i
977                                );
978                            }
979                        }
980                    },
981                    Node::Field(f) => {
982                        let id = (&f.def.table.schema_id, &f.def.id);
983                        if fields.insert(id, f.def.schema_id.clone()).is_some() {
984                            panic!("Duplicate field id {} -- {}", f.def.id, f.def);
985                        }
986                        if let Some(schema_id) = prev_fields.get(&id) {
987                            if schema_id != &f.def.schema_id {
988                                panic!(
989                                    "Field {} id in version {} swapped with another field since previous version; unsupported",
990                                    f.def,
991                                    v_i
992                                );
993                            }
994                        }
995                    },
996                    Node::Constraint(c) => {
997                        let id = (&c.def.table.schema_id, &c.def.id);
998                        if constraints.insert(id, c.def.schema_id.clone()).is_some() {
999                            panic!("Duplicate constraint id {} -- {}", c.def.id, c.def);
1000                        }
1001                        if let Some(schema_id) = prev_constraints.get(&id) {
1002                            if schema_id != &c.def.schema_id {
1003                                panic!(
1004                                    "Constraint {} id in version {} swapped with another constraint since previous version; unsupported",
1005                                    c.def,
1006                                    v_i
1007                                );
1008                            }
1009                        }
1010                    },
1011                    Node::Index(i) => {
1012                        let id = &i.def.id;
1013                        let comp_id = format!("index {}", i.def.schema_id);
1014                        if relations.insert(id, comp_id.clone()).is_some() {
1015                            panic!("Duplicate index id {} -- {}", i.def.id, i.def);
1016                        }
1017                        if let Some(schema_id) = prev_relations.get(&id) {
1018                            if schema_id != &comp_id {
1019                                panic!(
1020                                    "Index {} id in version {} swapped with another relation since previous version; unsupported",
1021                                    i.def,
1022                                    v_i
1023                                );
1024                            }
1025                        }
1026                    },
1027                }
1028            }
1029            prev_relations = relations;
1030            prev_fields = fields;
1031            prev_constraints = constraints;
1032        }
1033    }
1034    let mut errs = Errs::new();
1035    let mut migrations = vec![];
1036    let mut prev_version: Option<Version> = None;
1037    let mut prev_version_i: Option<i64> = None;
1038    let mut field_lookup = HashMap::new();
1039    for (version_i, version) in versions {
1040        let path = rpds::vector![format!("Migration to {}", version_i)];
1041        let mut migration = vec![];
1042
1043        fn do_migration_query(
1044            errs: &mut Errs,
1045            path: &rpds::Vector<String>,
1046            migration: &mut Vec<TokenStream>,
1047            field_lookup: &HashMap<Table, HashSet<Field>>,
1048            q: &dyn QueryBody,
1049        ) {
1050            let mut qctx = SqliteQueryCtx::new(errs.clone(), field_lookup.clone());
1051            let e_res = q.build(&mut qctx, path, QueryResCount::None);
1052            if !qctx.rust_args.is_empty() {
1053                qctx.errs.err(path, format!("Migration statements can't receive arguments"));
1054            }
1055            let statement = e_res.1.to_string();
1056            let args = qctx.query_args;
1057            migration.push(quote!{
1058                {
1059                    let query = #statement;
1060                    txn.execute(query, rusqlite::params![#(#args,) *]).to_good_error_query(query)?
1061                };
1062            });
1063        }
1064
1065        // Do pre-migrations
1066        for (i, q) in version.pre_migration.iter().enumerate() {
1067            do_migration_query(
1068                &mut errs,
1069                &path.push_back(format!("Pre-migration statement {}", i)),
1070                &mut migration,
1071                &field_lookup,
1072                q.as_ref(),
1073            );
1074        }
1075
1076        // Prep for current version
1077        field_lookup.clear();
1078        let version_i = version_i as i64;
1079        if let Some(i) = prev_version_i {
1080            if version_i != i as i64 + 1 {
1081                errs.err(
1082                    &path,
1083                    format!(
1084                        "Version numbers are not consecutive ({} to {}) - was an intermediate version deleted?",
1085                        i,
1086                        version_i
1087                    ),
1088                );
1089            }
1090        }
1091
1092        // Gather tables for lookup during query generation and check duplicates
1093        for v in version.schema.values() {
1094            match &v.body {
1095                Node::Field(f) => {
1096                    match field_lookup.entry(f.def.table.clone()) {
1097                        std::collections::hash_map::Entry::Occupied(_) => { },
1098                        std::collections::hash_map::Entry::Vacant(e) => {
1099                            e.insert(HashSet::new());
1100                        },
1101                    };
1102                    let table = field_lookup.get_mut(&f.def.table).unwrap();
1103                    table.insert(f.def.clone());
1104                },
1105                _ => { },
1106            };
1107        }
1108
1109        // Main migrations
1110        {
1111            let mut state = SqliteMigrateCtx::new(errs.clone());
1112            crate::graphmigrate::migrate(&mut state, prev_version.take().map(|s| s.schema), &version.schema);
1113            for statement in &state.statements {
1114                migration.push(quote!{
1115                    {
1116                        let query = #statement;
1117                        txn.execute(query, ()).to_good_error_query(query)?
1118                    };
1119                });
1120            }
1121        }
1122
1123        // Post-migration
1124        for (i, q) in version.post_migration.iter().enumerate() {
1125            do_migration_query(
1126                &mut errs,
1127                &path.push_back(format!("Post-migration statement {}", i)),
1128                &mut migration,
1129                &field_lookup,
1130                q.as_ref(),
1131            );
1132        }
1133
1134        // Build migration
1135        migrations.push(quote!{
1136            if version < #version_i {
1137                #(#migration) *
1138            }
1139        });
1140
1141        // Next iter prep
1142        prev_version = Some(version);
1143        prev_version_i = Some(version_i);
1144    }
1145
1146    // Generate queries
1147    let mut db_others = Vec::new();
1148    {
1149        let mut res_type_idents: HashMap<String, Ident> = HashMap::new();
1150        for q in queries {
1151            let path = rpds::vector![format!("Query {}", q.name)];
1152            let mut ctx = SqliteQueryCtx::new(errs.clone(), field_lookup.clone());
1153            let res = QueryBody::build(q.body.as_ref(), &mut ctx, &path, q.res_count.clone());
1154            let ident = format_ident!("{}", q.name);
1155            let q_text = res.1.to_string();
1156            let args = ctx.rust_args.split_off(0);
1157            let args_forward = ctx.query_args.split_off(0);
1158            drop(ctx);
1159            let (res_ident, res_def, unforward_res) = {
1160                fn convert_one_res(
1161                    errs: &mut Errs,
1162                    path: &rpds::Vector<String>,
1163                    i: usize,
1164                    k: &Binding,
1165                    v: &Type,
1166                ) -> Option<(Ident, TokenStream, TokenStream)> {
1167                    if k.id.is_empty() {
1168                        errs.err(
1169                            path,
1170                            format!("Result element {} has no name; name it using `rename` if this is intentional", i),
1171                        );
1172                        return None;
1173                    }
1174                    let rust_types = to_rust_types(&v.type_.type_);
1175                    let custom_trait_ident = rust_types.custom_trait;
1176                    let mut ident = rust_types.ret_type;
1177                    if v.opt {
1178                        ident = quote!(Option < #ident >);
1179                    }
1180                    let mut unforward = match v.type_.type_ {
1181                        types::SimpleSimpleType::U32 |
1182                        types::SimpleSimpleType::I32 |
1183                        types::SimpleSimpleType::I64 |
1184                        types::SimpleSimpleType::F32 |
1185                        types::SimpleSimpleType::F64 |
1186                        types::SimpleSimpleType::Bool |
1187                        types::SimpleSimpleType::String |
1188                        types::SimpleSimpleType::Bytes => {
1189                            quote!{
1190                                let x: #ident = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1191                            }
1192                        },
1193                        #[cfg(feature = "chrono")]
1194                        types::SimpleSimpleType::UtcTimeSChrono => {
1195                            quote!{
1196                                let x: i64 = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1197                                let x = chrono::TimeZone::timestamp_opt(&chrono::Utc, x, 0).unwrap();
1198                            }
1199                        },
1200                        #[cfg(feature = "chrono")]
1201                        types::SimpleSimpleType::UtcTimeMsChrono => {
1202                            quote!{
1203                                let x: String = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1204                                let x =
1205                                    chrono::DateTime::<chrono::Utc>::from(
1206                                        chrono::DateTime::<chrono::FixedOffset>::parse_from_rfc3339(
1207                                            &x,
1208                                        ).to_good_error(|| format!("Getting result {}", #i))?,
1209                                    );
1210                            }
1211                        },
1212                        #[cfg(feature = "chrono")]
1213                        types::SimpleSimpleType::FixedOffsetTimeMsChrono => {
1214                            quote!{
1215                                let x: String = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1216                                let x =
1217                                    chrono::DateTime::<chrono::FixedOffset>::from(
1218                                        chrono::DateTime::<chrono::FixedOffset>::parse_from_rfc3339(
1219                                            &x,
1220                                        ).to_good_error(|| format!("Getting result {}", #i))?,
1221                                    );
1222                            }
1223                        },
1224                        #[cfg(feature = "jiff")]
1225                        types::SimpleSimpleType::UtcTimeSJiff => {
1226                            quote!{
1227                                let x: i64 = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1228                                let x = jiff::Timestamp::from_second(x).unwrap();
1229                            }
1230                        },
1231                        #[cfg(feature = "jiff")]
1232                        types::SimpleSimpleType::UtcTimeMsJiff => {
1233                            quote!{
1234                                let x: String = r.get(#i).to_good_error(|| format!("Getting result {}", #i)) ?;
1235                                let x =
1236                                    <jiff::Timestamp as std::str::FromStr>::from_str(
1237                                        &x,
1238                                    ).to_good_error(|| format!("Getting result {}", #i))?;
1239                            }
1240                        },
1241                    };
1242                    if let Some(custom) = &v.type_.custom {
1243                        ident = match syn::parse_str::<syn::Path>(&custom) {
1244                            Ok(i) => i.to_token_stream(),
1245                            Err(e) => {
1246                                errs.err(
1247                                    path,
1248                                    format!(
1249                                        "Couldn't parse provided custom type name [{}] as identifier path: {:?}",
1250                                        custom,
1251                                        e
1252                                    ),
1253                                );
1254                                return None;
1255                            },
1256                        };
1257                        if v.opt {
1258                            unforward = quote!{
1259                                #unforward let x = if let Some(x) = x {
1260                                    Some(
1261                                        < #ident as #custom_trait_ident < #ident >>:: from_sql(
1262                                            x
1263                                        ).to_good_error(|| format!("Parsing result {}", #i)) ?
1264                                    )
1265                                }
1266                                else {
1267                                    None
1268                                };
1269                            };
1270                            ident = quote!(Option < #ident >);
1271                        } else {
1272                            unforward = quote!{
1273                                #unforward let x =< #ident as #custom_trait_ident < #ident >>:: from_sql(
1274                                    x
1275                                ).to_good_error(|| format!("Parsing result {}", #i)) ?;
1276                            };
1277                        }
1278                    }
1279                    return Some((format_ident!("{}", sanitize_ident(&k.id).1), ident, quote!({
1280                        #unforward x
1281                    })));
1282                }
1283
1284                if res.0.0.len() == 1 {
1285                    let e = &res.0.0[0];
1286                    let (_, type_ident, unforward) = match convert_one_res(&mut errs, &path, 0, &e.0, &e.1) {
1287                        None => {
1288                            continue;
1289                        },
1290                        Some(x) => x,
1291                    };
1292                    (type_ident, None, unforward)
1293                } else {
1294                    let mut fields = vec![];
1295                    let mut unforward_fields = vec![];
1296                    for (i, (k, v)) in res.0.0.into_iter().enumerate() {
1297                        let (k_ident, type_ident, unforward) = match convert_one_res(&mut errs, &path, i, &k, &v) {
1298                            Some(x) => x,
1299                            None => continue,
1300                        };
1301                        fields.push(quote!{
1302                            pub #k_ident: #type_ident
1303                        });
1304                        unforward_fields.push(quote!{
1305                            #k_ident: #unforward
1306                        });
1307                    }
1308                    let body = quote!({
1309                        #(#fields,) *
1310                    });
1311                    let res_type_count = res_type_idents.len();
1312                    let (res_ident, res_def) = match res_type_idents.entry(body.to_string()) {
1313                        std::collections::hash_map::Entry::Occupied(e) => {
1314                            (e.get().clone(), None)
1315                        },
1316                        std::collections::hash_map::Entry::Vacant(e) => {
1317                            let ident = if let Some(name) = q.res_name {
1318                                format_ident!("{}", name)
1319                            } else {
1320                                format_ident!("DbRes{}", res_type_count)
1321                            };
1322                            e.insert(ident.clone());
1323                            let res_def = quote!(pub struct #ident #body);
1324                            (ident, Some(res_def))
1325                        },
1326                    };
1327                    let unforward = quote!(#res_ident {
1328                        #(#unforward_fields,) *
1329                    });
1330                    (res_ident.to_token_stream(), res_def, unforward)
1331                }
1332            };
1333            let db_arg = quote!(db:& rusqlite:: Connection);
1334            match q.res_count {
1335                QueryResCount::None => {
1336                    db_others.push(quote!{
1337                        pub fn #ident(#db_arg, #(#args,) *) -> Result <(),
1338                        GoodError > {
1339                            let query = #q_text;
1340                            db.execute(query, rusqlite::params![#(#args_forward,) *]).to_good_error_query(query)?;
1341                            Ok(())
1342                        }
1343                    });
1344                },
1345                QueryResCount::MaybeOne => {
1346                    if let Some(res_def) = res_def {
1347                        db_others.push(res_def);
1348                    }
1349                    db_others.push(quote!{
1350                        pub fn #ident(#db_arg, #(#args,) *) -> Result < Option < #res_ident >,
1351                        GoodError > {
1352                            let query = #q_text;
1353                            let mut stmt = db.prepare(query).to_good_error_query(query)?;
1354                            let mut rows =
1355                                stmt.query(rusqlite::params![#(#args_forward,) *]).to_good_error_query(query)?;
1356                            let r = rows.next().to_good_error(|| format!("Getting row in query [{}]", query))?;
1357                            if let Some(r) = r {
1358                                return Ok(Some(#unforward_res));
1359                            }
1360                            Ok(None)
1361                        }
1362                    });
1363                },
1364                QueryResCount::One => {
1365                    if let Some(res_def) = res_def {
1366                        db_others.push(res_def);
1367                    }
1368                    db_others.push(quote!{
1369                        pub fn #ident(#db_arg, #(#args,) *) -> Result < #res_ident,
1370                        GoodError > {
1371                            let query = #q_text;
1372                            let mut stmt = db.prepare(query).to_good_error_query(query)?;
1373                            let mut rows =
1374                                stmt.query(rusqlite::params![#(#args_forward,) *]).to_good_error_query(query)?;
1375                            let r =
1376                                rows
1377                                    .next()
1378                                    .to_good_error(|| format!("Getting row in query [{}]", query))?
1379                                    .ok_or_else(
1380                                        || GoodError(
1381                                            format!(
1382                                                "Expected to return one row but returned no rows in query [{}]",
1383                                                query
1384                                            ),
1385                                        ),
1386                                    )?;
1387                            Ok(#unforward_res)
1388                        }
1389                    });
1390                },
1391                QueryResCount::Many => {
1392                    if let Some(res_def) = res_def {
1393                        db_others.push(res_def);
1394                    }
1395                    db_others.push(quote!{
1396                        pub fn #ident(#db_arg, #(#args,) *) -> Result < Vec < #res_ident >,
1397                        GoodError > {
1398                            let mut out = vec![];
1399                            let query = #q_text;
1400                            let mut stmt = db.prepare(query).to_good_error_query(query)?;
1401                            let mut rows =
1402                                stmt.query(rusqlite::params![#(#args_forward,) *]).to_good_error_query(query)?;
1403                            while let Some(
1404                                r
1405                            ) = rows.next().to_good_error(|| format!("Getting row in query [{}]", query)) ? {
1406                                out.push(#unforward_res);
1407                            }
1408                            Ok(out)
1409                        }
1410                    });
1411                },
1412            }
1413        }
1414    }
1415
1416    // Compile, output
1417    let last_version_i = prev_version_i.unwrap() as i64;
1418    let tokens = quote!{
1419        use good_ormning_runtime::GoodError;
1420        use good_ormning_runtime::ToGoodError;
1421        pub fn migrate(db:& mut rusqlite:: Connection) -> Result <(),
1422        GoodError > {
1423            rusqlite::vtab::array::load_module(
1424                &db,
1425            ).to_good_error(|| "Error loading array extension for array values".to_string())?;
1426            {
1427                let query =
1428                    "create table if not exists __good_version (rid int primary key, version bigint not null, lock int not null);";
1429                db.execute(query, ()).to_good_error_query(query)?;
1430            }
1431            {
1432                let query =
1433                    "insert into __good_version (rid, version, lock) values (0, -1, 0) on conflict do nothing;";
1434                db.execute(query, ()).to_good_error_query(query)?;
1435            }
1436            loop {
1437                let txn = db.transaction().to_good_error(|| "Starting transaction".to_string())?;
1438                match(|| {
1439                    let query = "update __good_version set lock = 1 where rid = 0 and lock = 0 returning version";
1440                    let mut stmt = txn.prepare(query).to_good_error_query(query)?;
1441                    let mut rows = stmt.query(()).to_good_error_query(query)?;
1442                    let version = match rows.next().to_good_error_query(query)? {
1443                        Some(r) => {
1444                            let ver: i64 = r.get(0usize).to_good_error_query(query)?;
1445                            ver
1446                        },
1447                        None => return Ok(false),
1448                    };
1449                    drop(rows);
1450                    stmt.finalize().to_good_error_query(query)?;
1451                    if version > #last_version_i {
1452                        return Err(
1453                            GoodError(
1454                                format!(
1455                                    "The latest known version is {}, but the schema is at unknown version {}",
1456                                    #last_version_i,
1457                                    version
1458                                ),
1459                            ),
1460                        );
1461                    }
1462                    #(#migrations) * let query = "update __good_version set version = $1, lock = 0";
1463                    txn.execute(query, rusqlite::params![#last_version_i]).to_good_error_query(query)?;
1464                    let out: Result < bool,
1465                    GoodError >= Ok(true);
1466                    out
1467                })() {
1468                    Err(e) => {
1469                        match txn.rollback() {
1470                            Err(e1) => {
1471                                return Err(
1472                                    GoodError(
1473                                        format!(
1474                                            "{}\n\nRolling back the transaction due to the above also failed: {}",
1475                                            e,
1476                                            e1
1477                                        ),
1478                                    ),
1479                                );
1480                            },
1481                            Ok(_) => {
1482                                return Err(e);
1483                            },
1484                        };
1485                    }
1486                    Ok(migrated) => {
1487                        match txn.commit() {
1488                            Err(e) => {
1489                                return Err(GoodError(format!("Error committing the migration transaction: {}", e)));
1490                            },
1491                            Ok(_) => {
1492                                if migrated {
1493                                    return Ok(())
1494                                } else {
1495                                    std::thread::sleep(std::time::Duration::from_millis(5 * 1000));
1496                                }
1497                            },
1498                        };
1499                    }
1500                }
1501            }
1502        }
1503        #(#db_others) *
1504    };
1505    if let Some(p) = output.parent() {
1506        if let Err(e) = fs::create_dir_all(&p) {
1507            errs.err(
1508                &rpds::vector![],
1509                format!("Error creating output parent directories {}: {:?}", p.to_string_lossy(), e),
1510            );
1511        }
1512    }
1513    match genemichaels_lib::format_str(&tokens.to_string(), &genemichaels_lib::FormatConfig::default()) {
1514        Ok(src) => {
1515            match fs::write(output, src.rendered.as_bytes()) {
1516                Ok(_) => { },
1517                Err(e) => errs.err(
1518                    &rpds::vector![],
1519                    format!("Failed to write generated code to {}: {:?}", output.to_string_lossy(), e),
1520                ),
1521            };
1522        },
1523        Err(e) => {
1524            errs.err(&rpds::vector![], format!("Error formatting generated code: {:?}\n{}", e, tokens));
1525        },
1526    };
1527    errs.raise()?;
1528    Ok(())
1529}
1530
1531#[cfg(test)]
1532mod test {
1533    use std::{
1534        path::PathBuf,
1535        str::FromStr,
1536    };
1537    use crate::sqlite::{
1538        new_select,
1539        QueryResCount,
1540        new_insert,
1541    };
1542    use super::{
1543        schema::field::{
1544            field_str,
1545            field_i32,
1546        },
1547        generate,
1548        Version,
1549        query::expr::Expr,
1550    };
1551
1552    #[test]
1553    #[should_panic]
1554    fn test_add_field_dup_bad() {
1555        generate(&PathBuf::from_str("/dev/null").unwrap(), vec![
1556            // Versions (previous)
1557            (0usize, {
1558                let mut v = Version::default();
1559                let bananna = v.table("zPAO2PJU4", "bananna");
1560                bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1561                v
1562            }),
1563            (1usize, {
1564                let mut v = Version::default();
1565                let bananna = v.table("zQZQ8E2WD", "bananna");
1566                bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1567                bananna.field(&mut v, "z437INV6D", "zomzom", field_i32().build());
1568                v
1569            })
1570        ], vec![]).unwrap();
1571    }
1572
1573    #[test]
1574    #[should_panic]
1575    fn test_add_table_dup_bad() {
1576        generate(&PathBuf::from_str("/dev/null").unwrap(), vec![
1577            // Versions (previous)
1578            (0usize, {
1579                let mut v = Version::default();
1580                let bananna = v.table("zSNS34DYI", "bananna");
1581                bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1582                v
1583            }),
1584            (1usize, {
1585                let mut v = Version::default();
1586                let bananna = v.table("zSNS34DYI", "bananna");
1587                bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1588                let bananna = v.table("zSNS34DYI", "bananna");
1589                bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1590                v
1591            })
1592        ], vec![]).unwrap();
1593    }
1594
1595    #[test]
1596    fn test_res_count_none_bad() {
1597        let mut v = Version::default();
1598        let bananna = v.table("z5S18LWQE", "bananna");
1599        let hizat = bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1600        assert!(
1601            generate(
1602                &PathBuf::from_str("/dev/null").unwrap(),
1603                vec![(0usize, v)],
1604                vec![new_select(&bananna).return_field(&hizat).build_query("x", QueryResCount::None)],
1605            ).is_err()
1606        );
1607    }
1608
1609    #[test]
1610    fn test_select_nothing_bad() {
1611        let mut v = Version::default();
1612        let bananna = v.table("zOOR88EQ9", "bananna");
1613        bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1614        assert!(
1615            generate(
1616                &PathBuf::from_str("/dev/null").unwrap(),
1617                vec![(0usize, v)],
1618                vec![new_select(&bananna).build_query("x", QueryResCount::None)],
1619            ).is_err()
1620        );
1621    }
1622
1623    #[test]
1624    fn test_returning_none_bad() {
1625        let mut v = Version::default();
1626        let bananna = v.table("zZPD1I2EF", "bananna");
1627        let hizat = bananna.field(&mut v, "z437INV6D", "hizat", field_str().build());
1628        assert!(
1629            generate(
1630                &PathBuf::from_str("/dev/null").unwrap(),
1631                vec![(0usize, v)],
1632                vec![
1633                    new_insert(&bananna, vec![(hizat.clone(), Expr::LitString("hoy".into()))])
1634                        .return_field(&hizat)
1635                        .build_query("x", QueryResCount::None)
1636                ],
1637            ).is_err()
1638        );
1639    }
1640}