gql2sql/
lib.rs

1mod consts;
2
3use crate::consts::{
4    BASE, DATA_LABEL, JSONB_BUILD_ARRAY, JSONB_BUILD_OBJECT, JSON_AGG, JSON_BUILD_OBJECT, ON,
5    QUOTE_CHAR, ROOT_LABEL, TO_JSON, TO_JSONB,
6};
7use anyhow::anyhow;
8use async_graphql_parser::{
9    types::{
10        Directive, DocumentOperations, ExecutableDocument, Field, OperationType, Selection,
11        VariableDefinition,
12    },
13    Positioned,
14};
15use async_graphql_value::{Name, Value as GqlValue};
16use indexmap::IndexMap;
17use sqlparser::ast::{
18    Assignment, BinaryOperator, Cte, DataType, Expr, Function, FunctionArg, FunctionArgExpr, Ident,
19    Join, JoinConstraint, JoinOperator, ObjectName, Offset, OffsetRows, OrderByExpr, Query, Select,
20    SelectItem, SetExpr, Statement, TableAlias, TableFactor, TableWithJoins, Value, Values,
21    WildcardAdditionalOptions, With,
22};
23use std::{
24    collections::HashSet,
25    fmt::{Debug, Formatter},
26    iter::zip,
27};
28
29type JsonValue = serde_json::Value;
30type AnyResult<T> = anyhow::Result<T>;
31
32fn get_value<'a>(value: &'a GqlValue, sql_vars: &'a IndexMap<Name, JsonValue>) -> AnyResult<Expr> {
33    match value {
34        GqlValue::Variable(v) => {
35            let index = sql_vars
36                .get_index_of(v)
37                .map(|i| i + 1)
38                .ok_or(anyhow!("variable not found"))?;
39            Ok(Expr::Value(Value::Placeholder(format!("${index}"))))
40        }
41        GqlValue::Null => Ok(Expr::Value(Value::Null)),
42        GqlValue::String(s) => Ok(Expr::Value(Value::SingleQuotedString(s.clone()))),
43        GqlValue::Number(f) => Ok(Expr::Value(Value::Number(f.to_string(), false))),
44        GqlValue::Boolean(b) => Ok(Expr::Value(Value::Boolean(b.to_owned()))),
45        GqlValue::Enum(e) => Ok(Expr::Value(Value::SingleQuotedString(e.as_ref().into()))),
46        GqlValue::Binary(_b) => Err(anyhow!("binary not supported")),
47        GqlValue::List(l) => Ok(Expr::Function(Function {
48            name: ObjectName(vec![Ident::new(JSONB_BUILD_ARRAY)]),
49            args: l
50                .into_iter()
51                .map(|v| {
52                    let value = get_value(v, sql_vars).unwrap();
53                    FunctionArg::Unnamed(FunctionArgExpr::Expr(value))
54                })
55                .collect::<Vec<FunctionArg>>(),
56            over: None,
57            distinct: false,
58            special: false,
59            order_by: vec![],
60        })),
61        GqlValue::Object(o) => {
62            if o.contains_key("_parentRef") {
63                if let Some(GqlValue::String(s)) = o.get("_parentRef") {
64                    return Ok(Expr::CompoundIdentifier(vec![
65                        Ident::with_quote(QUOTE_CHAR, BASE.to_owned()),
66                        Ident::with_quote(QUOTE_CHAR, s),
67                    ]));
68                }
69            }
70            Ok(Expr::Function(Function {
71                name: ObjectName(vec![Ident::new(JSONB_BUILD_OBJECT)]),
72                args: o
73                    .into_iter()
74                    .flat_map(|(k, v)| {
75                        let value = get_value(v, sql_vars).unwrap();
76                        vec![
77                            FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
78                                Value::SingleQuotedString(k.to_string()),
79                            ))),
80                            FunctionArg::Unnamed(FunctionArgExpr::Expr(value)),
81                        ]
82                    })
83                    .collect::<Vec<FunctionArg>>(),
84                over: None,
85                distinct: false,
86                special: false,
87                order_by: vec![],
88            }))
89        }
90    }
91}
92
93fn get_logical_operator(op: &str) -> AnyResult<BinaryOperator> {
94    let value = match op {
95        "AND" => BinaryOperator::And,
96        "OR" => BinaryOperator::Or,
97        _ => {
98            return Err(anyhow!("logical operator not supported: {}", op));
99        }
100    };
101    Ok(value)
102}
103
104fn get_op(op: &str) -> AnyResult<BinaryOperator> {
105    let value = match op {
106        "eq" | "equals" => BinaryOperator::Eq,
107        "neq" | "not_equals" => BinaryOperator::NotEq,
108        "lt" | "less_than" => BinaryOperator::Lt,
109        "lte" | "less_than_or_equals" => BinaryOperator::LtEq,
110        "gt" | "greater_than" => BinaryOperator::Gt,
111        "gte" | "greater_than_or_equals" => BinaryOperator::GtEq,
112        _ => BinaryOperator::Custom(op.to_owned()),
113    };
114    Ok(value)
115}
116
117fn get_expr<'a>(
118    left: Expr,
119    operator: &'a str,
120    value: &'a GqlValue,
121    variables: &'a IndexMap<Name, JsonValue>,
122) -> AnyResult<Option<Expr>> {
123    let right_value = get_value(value, variables)?;
124    match operator {
125        "like" => Ok(Some(Expr::Like {
126            negated: false,
127            expr: Box::new(left),
128            pattern: Box::new(right_value),
129            escape_char: None,
130        })),
131        "ilike" => Ok(Some(Expr::ILike {
132            negated: false,
133            expr: Box::new(left),
134            pattern: Box::new(right_value),
135            escape_char: None,
136        })),
137        _ => {
138            let op = get_op(operator)?;
139            Ok(Some(Expr::BinaryOp {
140                left: Box::new(left),
141                op,
142                right: Box::new(right_value),
143            }))
144        }
145    }
146}
147
148fn get_string_or_variable(
149    value: &GqlValue,
150    variables: &IndexMap<Name, JsonValue>,
151) -> AnyResult<String> {
152    match value {
153        GqlValue::Variable(v) => {
154            if let Some(JsonValue::String(s)) = variables.get(v) {
155                Ok(s.clone())
156            } else {
157                Err(anyhow!("variable not found"))
158            }
159        }
160        GqlValue::String(s) => Ok(s.clone()),
161        _ => Err(anyhow!("value not supported")),
162    }
163}
164
165fn get_filter(
166    args: &IndexMap<Name, GqlValue>,
167    variables: &IndexMap<Name, JsonValue>,
168) -> AnyResult<Option<Expr>> {
169    let field = args
170        .get("field")
171        .map(|v| get_string_or_variable(v, variables))
172        .ok_or(anyhow!("field not found"))??;
173    let operator = args
174        .get("operator")
175        .map(|v| get_string_or_variable(v, variables))
176        .ok_or(anyhow!("operator not found"))??;
177    if let Some(value) = args.get("value") {
178        let left = Expr::Identifier(Ident {
179            value: field,
180            quote_style: Some(QUOTE_CHAR),
181        });
182        let primary = get_expr(left, operator.as_str(), value, variables)?;
183        if args.contains_key("children") {
184            if let Some(GqlValue::List(children)) = args.get("children") {
185                let op = if let Some(GqlValue::String(op)) = args.get("logicalOperator") {
186                    get_logical_operator(op.as_str())?
187                } else {
188                    BinaryOperator::And
189                };
190                if let Some(filters) = children
191                    .iter()
192                    .map(|v| match v {
193                        GqlValue::Object(o) => get_filter(o, variables),
194                        _ => Ok(None),
195                    })
196                    .fold(Ok(primary), |acc: AnyResult<Option<Expr>>, item| {
197                        if let Ok(Some(acc)) = acc {
198                            let item = item?;
199                            let expr = Expr::BinaryOp {
200                                left: Box::new(acc),
201                                op: op.clone(),
202                                right: Box::new(item.ok_or(anyhow!("invalid filter"))?),
203                            };
204                            Ok(Some(expr))
205                        } else if let Ok(None) = acc {
206                            Ok(None)
207                        } else {
208                            Err(anyhow!("invalid filter"))
209                        }
210                    })?
211                {
212                    return Ok(Some(Expr::Nested(Box::new(filters))));
213                }
214                return Ok(None);
215            }
216        } else {
217            return Ok(primary);
218        }
219    }
220    Ok(None)
221}
222
223fn get_agg_query(
224    aggs: Vec<FunctionArg>,
225    from: Vec<TableWithJoins>,
226    selection: Option<Expr>,
227    alias: &str,
228) -> SetExpr {
229    SetExpr::Select(Box::new(Select {
230        distinct: None,
231        named_window: Vec::new(),
232        top: None,
233        into: None,
234        projection: vec![SelectItem::ExprWithAlias {
235            alias: Ident {
236                value: alias.to_string(),
237                quote_style: Some(QUOTE_CHAR),
238            },
239            expr: Expr::Function(Function {
240                order_by: vec![],
241                name: ObjectName(vec![Ident {
242                    value: JSON_BUILD_OBJECT.to_string(),
243                    quote_style: None,
244                }]),
245                args: aggs,
246                over: None,
247                distinct: false,
248                special: false,
249            }),
250        }],
251        from,
252        lateral_views: Vec::new(),
253        selection,
254        group_by: Vec::new(),
255        cluster_by: Vec::new(),
256        distribute_by: Vec::new(),
257        sort_by: Vec::new(),
258        having: None,
259        qualify: None,
260    }))
261}
262
263fn get_root_query(
264    projection: Vec<SelectItem>,
265    from: Vec<TableWithJoins>,
266    selection: Option<Expr>,
267    merges: &[Merge],
268    is_single: bool,
269    alias: &str,
270) -> SetExpr {
271    let mut base = Expr::Function(Function {
272        order_by: vec![],
273        name: ObjectName(vec![Ident {
274            value: TO_JSON.to_string(),
275            quote_style: None,
276        }]),
277        args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Subquery(
278            Box::new(Query {
279                with: None,
280                body: Box::new(SetExpr::Select(Box::new(Select {
281                    distinct: None,
282                    named_window: Vec::new(),
283                    top: None,
284                    projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
285                        value: ROOT_LABEL.to_string(),
286                        quote_style: Some(QUOTE_CHAR),
287                    }))],
288                    into: None,
289                    from: vec![TableWithJoins {
290                        relation: TableFactor::Derived {
291                            lateral: false,
292                            subquery: Box::new(Query {
293                                with: None,
294                                body: Box::new(SetExpr::Select(Box::new(Select {
295                                    distinct: None,
296                                    named_window: vec![],
297                                    top: None,
298                                    projection,
299                                    into: None,
300                                    from: vec![],
301                                    lateral_views: vec![],
302                                    selection: None,
303                                    group_by: vec![],
304                                    cluster_by: vec![],
305                                    distribute_by: vec![],
306                                    sort_by: vec![],
307                                    having: None,
308                                    qualify: None,
309                                }))),
310                                order_by: vec![],
311                                limit: None,
312                                offset: None,
313                                fetch: None,
314                                locks: vec![],
315                            }),
316                            alias: Some(TableAlias {
317                                name: Ident {
318                                    value: ROOT_LABEL.to_string(),
319                                    quote_style: Some(QUOTE_CHAR),
320                                },
321                                columns: vec![],
322                            }),
323                        },
324                        joins: vec![],
325                    }],
326                    lateral_views: vec![],
327                    selection: None,
328                    group_by: vec![],
329                    cluster_by: vec![],
330                    distribute_by: vec![],
331                    sort_by: vec![],
332                    having: None,
333                    qualify: None,
334                }))),
335                order_by: vec![],
336                limit: None,
337                offset: None,
338                fetch: None,
339                locks: vec![],
340            }),
341        )))],
342        over: None,
343        distinct: false,
344        special: false,
345    });
346    if !merges.is_empty() {
347        base = Expr::BinaryOp {
348            left: Box::new(Expr::Cast {
349                expr: Box::new(base),
350                data_type: DataType::Custom(
351                    ObjectName(vec![Ident {
352                        value: "jsonb".to_string(),
353                        quote_style: None,
354                    }]),
355                    vec![],
356                ),
357            }),
358            op: BinaryOperator::StringConcat,
359            right: Box::new(Expr::Case {
360                operand: None,
361                conditions: merges.iter().map(|m| m.condition.clone()).collect(),
362                results: merges.iter().map(|m| m.expr.clone()).collect(),
363                else_result: Some(Box::new(Expr::Function(Function {
364                    order_by: vec![],
365                    name: ObjectName(vec![Ident {
366                        value: "jsonb_build_object".to_string(),
367                        quote_style: None,
368                    }]),
369                    args: vec![],
370                    over: None,
371                    distinct: false,
372                    special: false,
373                }))),
374            }),
375        };
376    }
377    if !is_single {
378        base = Expr::Function(Function {
379            order_by: vec![],
380            over: None,
381            distinct: false,
382            special: false,
383            name: ObjectName(vec![Ident {
384                value: "coalesce".to_string(),
385                quote_style: None,
386            }]),
387            args: vec![
388                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
389                    order_by: vec![],
390                    distinct: false,
391                    over: None,
392                    special: false,
393                    name: ObjectName(vec![Ident {
394                        value: JSON_AGG.to_string(),
395                        quote_style: None,
396                    }]),
397                    args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(base))],
398                }))),
399                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
400                    Value::SingleQuotedString("[]".to_string()),
401                ))),
402            ],
403        });
404    }
405    SetExpr::Select(Box::new(Select {
406        distinct: None,
407        named_window: Vec::new(),
408        top: None,
409        projection: vec![SelectItem::ExprWithAlias {
410            alias: Ident {
411                value: alias.to_string(),
412                quote_style: Some(QUOTE_CHAR),
413            },
414            expr: base,
415        }],
416        into: None,
417        from,
418        lateral_views: Vec::new(),
419        selection,
420        group_by: Vec::new(),
421        cluster_by: Vec::new(),
422        distribute_by: Vec::new(),
423        sort_by: Vec::new(),
424        having: None,
425        qualify: None,
426    }))
427}
428
429fn get_agg_agg_projection(field: &Field, table_name: String) -> Vec<FunctionArg> {
430    let name = field.name.node.as_ref();
431    match name {
432        "__typename" => {
433            vec![
434                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
435                    Value::SingleQuotedString(field.name.node.to_string()),
436                ))),
437                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
438                    order_by: vec![],
439                    name: ObjectName(vec![Ident {
440                        value: "MIN".to_string(),
441                        quote_style: None,
442                    }]),
443                    args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
444                        Value::SingleQuotedString(table_name),
445                    )))],
446                    over: None,
447                    distinct: false,
448                    special: false,
449                }))),
450            ]
451        }
452        "count" => {
453            vec![
454                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
455                    Value::SingleQuotedString(field.name.node.to_string()),
456                ))),
457                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
458                    order_by: vec![],
459                    name: ObjectName(vec![Ident {
460                        value: name.to_uppercase(),
461                        quote_style: None,
462                    }]),
463                    args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)],
464                    over: None,
465                    distinct: false,
466                    special: false,
467                }))),
468            ]
469        }
470        "min" | "max" | "avg" => {
471            let projection = field
472                .selection_set
473                .node
474                .items
475                .iter()
476                .flat_map(|arg| {
477                    if let Selection::Field(field) = &arg.node {
478                        let field = &field.node;
479                        vec![
480                            FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
481                                Value::SingleQuotedString(field.name.node.to_string()),
482                            ))),
483                            FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
484                                order_by: vec![],
485                                name: ObjectName(vec![Ident {
486                                    value: name.to_uppercase(),
487                                    quote_style: None,
488                                }]),
489                                args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
490                                    Expr::Identifier(Ident {
491                                        value: field.name.node.to_string(),
492                                        quote_style: Some(QUOTE_CHAR),
493                                    }),
494                                ))],
495                                over: None,
496                                distinct: false,
497                                special: false,
498                            }))),
499                        ]
500                    } else {
501                        vec![]
502                    }
503                })
504                .collect();
505            vec![
506                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
507                    Value::SingleQuotedString(field.name.node.to_string()),
508                ))),
509                FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
510                    order_by: vec![],
511                    name: ObjectName(vec![Ident {
512                        value: JSON_BUILD_OBJECT.to_string(),
513                        quote_style: None,
514                    }]),
515                    args: projection,
516                    over: None,
517                    distinct: false,
518                    special: false,
519                }))),
520            ]
521        }
522        _ => vec![],
523    }
524}
525
526fn get_aggregate_projection(
527    items: &Vec<Positioned<Selection>>,
528    table_name: String,
529) -> AnyResult<Vec<FunctionArg>> {
530    let mut aggs = Vec::new();
531    for selection in items {
532        match &selection.node {
533            Selection::Field(field) => {
534                aggs.extend(get_agg_agg_projection(&field.node, table_name.clone()));
535            }
536            Selection::FragmentSpread(_) => {
537                return Err(anyhow!(
538                    "Fragment spread is not supported in aggregate query"
539                ));
540            }
541            Selection::InlineFragment(_) => {
542                return Err(anyhow!(
543                    "Inline fragment is not supported in aggregate query"
544                ));
545            }
546        }
547    }
548    Ok(aggs)
549}
550
551fn get_join<'a>(
552    arguments: &'a Vec<(Positioned<Name>, Positioned<GqlValue>)>,
553    directives: &'a Vec<Positioned<Directive>>,
554    selection_items: &'a Vec<Positioned<Selection>>,
555    path: Option<&'a str>,
556    name: &'a str,
557    variables: &'a IndexMap<Name, GqlValue>,
558    sql_vars: &'a IndexMap<Name, JsonValue>,
559    parent: &'a str,
560    tags: &'a mut IndexMap<String, HashSet<Tag>>,
561) -> AnyResult<Join> {
562    let (selection, distinct, distinct_order, order_by, mut first, after, keys) =
563        parse_args(arguments, variables, sql_vars)?;
564    let (relation, fks, pks, is_single, is_aggregate, is_many, schema_name) =
565        get_relation(directives, sql_vars)?;
566    if is_single {
567        first = Some(Expr::Value(Value::Number("1".to_string(), false)));
568    }
569    if let Some(keys) = keys {
570        tags.insert(name.to_owned(), keys.into_iter().collect());
571    } else {
572        tags.insert(name.to_owned(), HashSet::new());
573    };
574
575    let table_name = schema_name.as_ref().map_or_else(
576        || {
577            ObjectName(vec![Ident {
578                value: relation.to_string(),
579                quote_style: Some(QUOTE_CHAR),
580            }])
581        },
582        |schema_name| {
583            ObjectName(vec![
584                Ident {
585                    value: schema_name.clone(),
586                    quote_style: Some(QUOTE_CHAR),
587                },
588                Ident {
589                    value: relation.to_string(),
590                    quote_style: Some(QUOTE_CHAR),
591                },
592            ])
593        },
594    );
595
596    let sub_path = path.map_or_else(|| relation.to_string(), |v| format!("{v}.{relation}"));
597    let mut additional_select_items = Vec::new();
598    let mut join_name = None;
599    if is_many {
600        let (a, b) = if relation.as_str() < parent {
601            (relation.as_str(), parent)
602        } else {
603            (parent, relation.as_str())
604        };
605        join_name = Some(format!("_{a}To{b}"));
606    }
607    let join_filter = join_name.as_ref().map_or_else(
608        || {
609            zip(pks, fks)
610                .map(|(pk, fk)| {
611                    additional_select_items.push(SelectItem::UnnamedExpr(
612                        Expr::CompoundIdentifier(vec![
613                            Ident {
614                                value: sub_path.to_string(),
615                                quote_style: Some(QUOTE_CHAR),
616                            },
617                            Ident {
618                                value: fk.clone(),
619                                quote_style: Some(QUOTE_CHAR),
620                            },
621                        ]),
622                    ));
623                    let mut new_tags = HashSet::new();
624                    if let Some(table_tags) = tags.get(parent) {
625                        for tag in table_tags {
626                            if tag.key == pk {
627                                new_tags.insert(Tag {
628                                    key: fk.clone(),
629                                    value: tag.value.clone(),
630                                });
631                            } else if tag.key == fk {
632                                new_tags.insert(Tag {
633                                    key: pk.clone(),
634                                    value: tag.value.clone(),
635                                });
636                            } else if is_single {
637                                new_tags.insert(Tag {
638                                    key: fk.clone(),
639                                    value: format!("{{{{{fk}}}}}"),
640                                });
641                            } else {
642                                new_tags.insert(Tag {
643                                    key: fk.clone(),
644                                    value: format!("{{{{{fk}}}}}"),
645                                });
646                            }
647                        }
648                    } else if is_single {
649                        new_tags.insert(Tag {
650                            key: fk.clone(),
651                            value: format!("{{{{{fk}}}}}"),
652                        });
653                    } else {
654                        new_tags.insert(Tag {
655                            key: fk.clone(),
656                            value: format!("{{{{{fk}}}}}"),
657                        });
658                    }
659                    if let Some(v) = tags.get_mut(name) {
660                        v.extend(new_tags);
661                    } else {
662                        tags.insert(name.to_string(), new_tags);
663                    };
664                    let mut identifier = vec![
665                        Ident {
666                            value: relation.to_string(),
667                            quote_style: Some(QUOTE_CHAR),
668                        },
669                        Ident {
670                            value: fk,
671                            quote_style: Some(QUOTE_CHAR),
672                        },
673                    ];
674                    if let Some(schema_name) = schema_name.as_ref() {
675                        identifier.insert(
676                            0,
677                            Ident {
678                                value: schema_name.clone(),
679                                quote_style: Some(QUOTE_CHAR),
680                            },
681                        );
682                    }
683                    Expr::BinaryOp {
684                        left: Box::new(Expr::CompoundIdentifier(identifier)),
685                        op: BinaryOperator::Eq,
686                        right: Box::new(Expr::CompoundIdentifier(vec![
687                            Ident {
688                                value: path
689                                    .map_or(BASE.to_string(), std::string::ToString::to_string),
690                                quote_style: Some(QUOTE_CHAR),
691                            },
692                            Ident {
693                                value: pk,
694                                quote_style: Some(QUOTE_CHAR),
695                            },
696                        ])),
697                    }
698                })
699                .reduce(|acc, expr| Expr::BinaryOp {
700                    left: Box::new(acc),
701                    op: BinaryOperator::And,
702                    right: Box::new(expr),
703                })
704        },
705        |join_name| {
706            let (join_col, value_col) = if relation.as_str() < parent {
707                ("A", "B")
708            } else {
709                ("B", "A")
710            };
711            Some(Expr::BinaryOp {
712                left: Box::new(Expr::BinaryOp {
713                    left: Box::new(Expr::CompoundIdentifier(vec![
714                        Ident {
715                            value: join_name.to_string(),
716                            quote_style: Some(QUOTE_CHAR),
717                        },
718                        Ident {
719                            value: join_col.to_string(),
720                            quote_style: Some(QUOTE_CHAR),
721                        },
722                    ])),
723                    op: BinaryOperator::Eq,
724                    right: Box::new(Expr::CompoundIdentifier(vec![
725                        Ident {
726                            value: relation.clone(),
727                            quote_style: Some(QUOTE_CHAR),
728                        },
729                        Ident {
730                            value: "id".to_string(),
731                            quote_style: Some(QUOTE_CHAR),
732                        },
733                    ])),
734                }),
735                op: BinaryOperator::And,
736                right: Box::new(Expr::BinaryOp {
737                    left: Box::new(Expr::CompoundIdentifier(vec![
738                        Ident {
739                            value: join_name.to_string(),
740                            quote_style: Some(QUOTE_CHAR),
741                        },
742                        Ident {
743                            value: value_col.to_string(),
744                            quote_style: Some(QUOTE_CHAR),
745                        },
746                    ])),
747                    op: BinaryOperator::Eq,
748                    right: Box::new(Expr::CompoundIdentifier(vec![
749                        Ident {
750                            value: path.map_or(BASE.to_string(), std::string::ToString::to_string),
751                            quote_style: Some(QUOTE_CHAR),
752                        },
753                        Ident {
754                            value: "id".to_string(),
755                            quote_style: Some(QUOTE_CHAR),
756                        },
757                    ])),
758                }),
759            })
760        },
761    );
762
763    let sub_query = get_filter_query(
764        selection.map_or_else(
765            || join_filter.clone(),
766            |s| {
767                Some(join_filter.clone().map_or_else(
768                    || s.clone(),
769                    |jf| Expr::BinaryOp {
770                        left: Box::new(jf),
771                        op: BinaryOperator::And,
772                        right: Box::new(s.clone()),
773                    },
774                ))
775            },
776        ),
777        order_by,
778        first,
779        after,
780        join_name.map_or_else(
781            || vec![table_name.clone()],
782            |name| {
783                vec![
784                    table_name.clone(),
785                    ObjectName(vec![Ident {
786                        value: name,
787                        quote_style: Some(QUOTE_CHAR),
788                    }]),
789                ]
790            },
791        ),
792        distinct,
793        distinct_order,
794    );
795    if is_aggregate {
796        let aggs = get_aggregate_projection(selection_items, format!("Agg_{name}"))?;
797        Ok(Join {
798            relation: TableFactor::Derived {
799                lateral: true,
800                subquery: Box::new(Query {
801                    with: None,
802                    body: Box::new(get_agg_query(
803                        aggs,
804                        vec![TableWithJoins {
805                            relation: TableFactor::Derived {
806                                lateral: false,
807                                subquery: Box::new(sub_query),
808                                alias: Some(TableAlias {
809                                    name: Ident {
810                                        value: sub_path,
811                                        quote_style: Some(QUOTE_CHAR),
812                                    },
813                                    columns: vec![],
814                                }),
815                            },
816                            joins: vec![],
817                        }],
818                        None,
819                        name,
820                    )),
821                    order_by: vec![],
822                    limit: None,
823                    offset: None,
824                    fetch: None,
825                    locks: vec![],
826                }),
827                alias: Some(TableAlias {
828                    name: Ident {
829                        value: format!("{name}.{relation}"),
830                        quote_style: Some(QUOTE_CHAR),
831                    },
832                    columns: vec![],
833                }),
834            },
835            join_operator: JoinOperator::LeftOuter(JoinConstraint::On(Expr::Nested(Box::new(
836                Expr::Value(Value::SingleQuotedString("true".to_string())),
837            )))),
838        })
839    } else {
840        let (sub_projection, sub_joins, merges) = get_projection(
841            selection_items,
842            &relation,
843            Some(&sub_path),
844            variables,
845            sql_vars,
846            tags,
847        )?;
848        additional_select_items.extend(sub_projection);
849        Ok(Join {
850            relation: TableFactor::Derived {
851                lateral: true,
852                subquery: Box::new(Query {
853                    with: None,
854                    body: Box::new(get_root_query(
855                        additional_select_items,
856                        vec![TableWithJoins {
857                            relation: TableFactor::Derived {
858                                lateral: false,
859                                subquery: Box::new(sub_query),
860                                alias: Some(TableAlias {
861                                    name: Ident {
862                                        value: sub_path,
863                                        quote_style: Some(QUOTE_CHAR),
864                                    },
865                                    columns: vec![],
866                                }),
867                            },
868                            joins: sub_joins,
869                        }],
870                        None,
871                        &merges,
872                        is_single,
873                        name,
874                    )),
875                    order_by: vec![],
876                    limit: None,
877                    offset: None,
878                    fetch: None,
879                    locks: vec![],
880                }),
881                alias: Some(TableAlias {
882                    name: Ident {
883                        value: format!("{name}.{relation}"),
884                        quote_style: Some(QUOTE_CHAR),
885                    },
886                    columns: vec![],
887                }),
888            },
889            join_operator: JoinOperator::LeftOuter(JoinConstraint::On(Expr::Nested(Box::new(
890                Expr::Value(Value::SingleQuotedString("true".to_string())),
891            )))),
892        })
893    }
894}
895
896struct Merge {
897    condition: Expr,
898    expr: Expr,
899}
900
901fn get_static<'a>(
902    name: &'a str,
903    directives: &Vec<Positioned<Directive>>,
904    sql_vars: &'a IndexMap<Name, JsonValue>,
905) -> AnyResult<Option<SelectItem>> {
906    for p_directive in directives {
907        let directive = &p_directive.node;
908        let directive_name: &str = directive.name.node.as_ref();
909        if directive_name == "static" {
910            let (_, value) = directive
911                .arguments
912                .iter()
913                .find(|(name, _)| name.node.as_ref() == "value")
914                .ok_or_else(|| anyhow!("static value not found"))?;
915            let value = match &value.node {
916                GqlValue::String(value) => value.to_string(),
917                GqlValue::Number(value) => value.as_i64().expect("value is not an int").to_string(),
918                GqlValue::Variable(name) => {
919                    if let Some(value) = sql_vars.get(name) {
920                        value.to_string()
921                    } else {
922                        return Err(anyhow!("variable not found: {}", name));
923                    }
924                }
925                GqlValue::Boolean(value) => value.to_string(),
926                _ => {
927                    return Err(anyhow!("static value is not a string"));
928                }
929            };
930            return Ok(Some(SelectItem::ExprWithAlias {
931                expr: Expr::Value(Value::SingleQuotedString(value)),
932                alias: Ident {
933                    value: name.to_string(),
934                    quote_style: Some(QUOTE_CHAR),
935                },
936            }));
937        }
938    }
939    Ok(None)
940}
941
942fn get_projection<'a>(
943    items: &'a Vec<Positioned<Selection>>,
944    relation: &'a str,
945    path: Option<&'a str>,
946    variables: &'a IndexMap<Name, GqlValue>,
947    sql_vars: &'a IndexMap<Name, JsonValue>,
948    tags: &mut IndexMap<String, HashSet<Tag>>,
949) -> AnyResult<(Vec<SelectItem>, Vec<Join>, Vec<Merge>)> {
950    let mut projection = Vec::new();
951    let mut joins = Vec::new();
952    let mut merges = Vec::new();
953    for selection in items {
954        let selection = &selection.node;
955        match selection {
956            Selection::Field(field) => {
957                let field = &field.node;
958                if !field.selection_set.node.items.is_empty() {
959                    let name = format!("join.{}", field.name.node.as_ref());
960                    let join = get_join(
961                        &field.arguments,
962                        &field.directives,
963                        &field.selection_set.node.items,
964                        path,
965                        &name,
966                        variables,
967                        sql_vars,
968                        relation,
969                        tags,
970                    )?;
971                    joins.push(join);
972                    match &field.alias {
973                        Some(alias) => {
974                            projection.push(SelectItem::ExprWithAlias {
975                                expr: Expr::Identifier(Ident {
976                                    value: name,
977                                    quote_style: Some(QUOTE_CHAR),
978                                }),
979                                alias: Ident {
980                                    value: alias.node.to_string(),
981                                    quote_style: Some(QUOTE_CHAR),
982                                },
983                            });
984                        }
985                        None => {
986                            projection.push(SelectItem::ExprWithAlias {
987                                expr: Expr::Identifier(Ident {
988                                    value: name,
989                                    quote_style: Some(QUOTE_CHAR),
990                                }),
991                                alias: Ident {
992                                    value: field.name.node.to_string(),
993                                    quote_style: Some(QUOTE_CHAR),
994                                },
995                            });
996                        }
997                    }
998                } else {
999                    if let Some(value) = get_static(&field.name.node, &field.directives, sql_vars)?
1000                    {
1001                        projection.push(value);
1002                        continue;
1003                    }
1004                    match &field.alias {
1005                        Some(alias) => {
1006                            projection.push(SelectItem::ExprWithAlias {
1007                                expr: path.map_or_else(
1008                                    || {
1009                                        Expr::Identifier(Ident {
1010                                            value: field.name.node.to_string(),
1011                                            quote_style: Some(QUOTE_CHAR),
1012                                        })
1013                                    },
1014                                    |path| {
1015                                        Expr::CompoundIdentifier(vec![
1016                                            Ident {
1017                                                value: path.to_string(),
1018                                                quote_style: Some(QUOTE_CHAR),
1019                                            },
1020                                            Ident {
1021                                                value: field.name.node.to_string(),
1022                                                quote_style: Some(QUOTE_CHAR),
1023                                            },
1024                                        ])
1025                                    },
1026                                ),
1027                                alias: Ident {
1028                                    value: alias.to_string(),
1029                                    quote_style: Some(QUOTE_CHAR),
1030                                },
1031                            });
1032                        }
1033                        None => {
1034                            let name = field.name.node.to_string();
1035                            if name == "__typename" {
1036                                projection.push(SelectItem::ExprWithAlias {
1037                                    alias: Ident {
1038                                        value: name,
1039                                        quote_style: Some(QUOTE_CHAR),
1040                                    },
1041                                    expr: Expr::Value(Value::SingleQuotedString(
1042                                        relation.to_string(),
1043                                    )),
1044                                });
1045                            } else {
1046                                projection.push(SelectItem::UnnamedExpr(path.map_or_else(
1047                                    || {
1048                                        Expr::Identifier(Ident {
1049                                            value: name.clone(),
1050                                            quote_style: Some(QUOTE_CHAR),
1051                                        })
1052                                    },
1053                                    |path| {
1054                                        Expr::CompoundIdentifier(vec![
1055                                            Ident {
1056                                                value: path.to_string(),
1057                                                quote_style: Some(QUOTE_CHAR),
1058                                            },
1059                                            Ident {
1060                                                value: name.clone(),
1061                                                quote_style: Some(QUOTE_CHAR),
1062                                            },
1063                                        ])
1064                                    },
1065                                )));
1066                            }
1067                        }
1068                    }
1069                }
1070            }
1071            Selection::InlineFragment(frag) => {
1072                let frag = &frag.node;
1073                if let Some(type_condition) = &frag.type_condition {
1074                    let name = &type_condition.node.on.node;
1075                    let args = frag
1076                        .directives
1077                        .iter()
1078                        .find(|d| d.node.name.node.as_ref() == "args");
1079                    let (relation, _fks, _pks, _is_single, _is_aggregate, _is_many, schema_name) =
1080                        get_relation(&frag.directives, sql_vars)?;
1081                    let join = get_join(
1082                        args.map_or(&vec![], |dir| &dir.node.arguments),
1083                        &frag.directives,
1084                        &frag.selection_set.node.items,
1085                        path,
1086                        name,
1087                        variables,
1088                        sql_vars,
1089                        &relation,
1090                        tags,
1091                    )?;
1092                    joins.push(join);
1093                    let table_name = schema_name.map_or_else(
1094                        || relation.to_string(),
1095                        |schema_name| schema_name + "." + &relation,
1096                    );
1097                    merges.push(Merge {
1098                        expr: Expr::Function(Function {
1099                            order_by: vec![],
1100                            name: ObjectName(vec![Ident {
1101                                value: TO_JSONB.to_string(),
1102                                quote_style: None,
1103                            }]),
1104                            args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
1105                                Expr::Identifier(Ident {
1106                                    value: name.to_string(),
1107                                    quote_style: Some(QUOTE_CHAR),
1108                                }),
1109                            ))],
1110                            over: None,
1111                            distinct: false,
1112                            special: false,
1113                        }),
1114                        condition: Expr::IsNotNull(Box::new(Expr::CompoundIdentifier(vec![
1115                            Ident {
1116                                value: format!("{name}.{relation}"),
1117                                quote_style: Some(QUOTE_CHAR),
1118                            },
1119                            Ident {
1120                                value: table_name,
1121                                quote_style: Some(QUOTE_CHAR),
1122                            },
1123                        ]))),
1124                    });
1125                }
1126            }
1127            Selection::FragmentSpread(_) => {
1128                return Err(anyhow!("Fragment spread is not supported"));
1129            }
1130        }
1131    }
1132    Ok((projection, joins, merges))
1133}
1134
1135fn value_to_string<'a>(
1136    value: &'a GqlValue,
1137    sql_vars: &'a IndexMap<Name, JsonValue>,
1138) -> AnyResult<String> {
1139    let output = match value {
1140        GqlValue::String(s) => s.clone(),
1141        GqlValue::Number(f) => f.to_string(),
1142        GqlValue::Boolean(b) => b.to_string(),
1143        GqlValue::Enum(e) => e.to_string(),
1144        GqlValue::List(l) => l
1145            .iter()
1146            .map(|l| value_to_string(l, sql_vars))
1147            .collect::<AnyResult<Vec<String>>>()?
1148            .join(","),
1149        GqlValue::Null => "null".to_owned(),
1150        GqlValue::Object(obj) => serde_json::to_string(obj).unwrap(),
1151        GqlValue::Variable(name) => {
1152            if let Some(value) = sql_vars.get(name) {
1153                value.to_string()
1154            } else {
1155                return Err(anyhow!("Variable {} is not defined", name));
1156            }
1157        }
1158        GqlValue::Binary(_) => {
1159            return Err(anyhow!("Binary value is not supported"));
1160        }
1161    };
1162    Ok(output)
1163}
1164
1165fn get_relation<'a>(
1166    directives: &'a [Positioned<Directive>],
1167    sql_vars: &'a IndexMap<Name, JsonValue>,
1168) -> AnyResult<(
1169    String,
1170    Vec<String>,
1171    Vec<String>,
1172    bool,
1173    bool,
1174    bool,
1175    Option<String>,
1176)> {
1177    let mut relation: String = String::new();
1178    let mut fk = vec![];
1179    let mut pk = vec![];
1180    let mut is_single = false;
1181    let mut is_aggregate = false;
1182    let mut is_many = false;
1183    let mut schema_name = None;
1184    if let Some(p_directive) = directives
1185        .iter()
1186        .find(|d| d.node.name.node.as_str() == "relation")
1187    {
1188        let directive = &p_directive.node;
1189        let name = directive.name.node.as_str();
1190        if name == "relation" {
1191            for (name, value) in &directive.arguments {
1192                let name = name.node.as_str();
1193                let value = &value.node;
1194                match name {
1195                    "table" => relation = value_to_string(value, sql_vars)?,
1196                    "schema" => schema_name = Some(value_to_string(value, sql_vars)?),
1197                    "field" | "fields" => {
1198                        fk = match &value {
1199                            GqlValue::String(s) => vec![s.clone()],
1200                            GqlValue::List(e) => e
1201                                .iter()
1202                                .map(|l| value_to_string(l, sql_vars))
1203                                .collect::<AnyResult<Vec<String>>>()?,
1204                            _ => {
1205                                return Err(anyhow!("Invalid value for field in relation"));
1206                            }
1207                        }
1208                    }
1209                    "reference" | "references" => {
1210                        pk = match value {
1211                            GqlValue::String(s) => vec![s.clone()],
1212                            GqlValue::List(e) => e
1213                                .iter()
1214                                .map(|l| value_to_string(l, sql_vars))
1215                                .collect::<AnyResult<Vec<String>>>()?,
1216                            _ => {
1217                                return Err(anyhow!("Invalid value for reference in relation"));
1218                            }
1219                        }
1220                    }
1221                    "single" => {
1222                        if let GqlValue::Boolean(b) = value {
1223                            is_single = *b;
1224                        }
1225                    }
1226                    "aggregate" => {
1227                        if let GqlValue::Boolean(b) = value {
1228                            is_aggregate = *b;
1229                        }
1230                    }
1231                    "many" => {
1232                        if let GqlValue::Boolean(b) = value {
1233                            is_many = *b;
1234                        }
1235                    }
1236                    _ => {}
1237                }
1238            }
1239        }
1240    }
1241    Ok((
1242        relation,
1243        fk,
1244        pk,
1245        is_single,
1246        is_aggregate,
1247        is_many,
1248        schema_name,
1249    ))
1250}
1251
1252fn get_filter_query(
1253    selection: Option<Expr>,
1254    order_by: Vec<OrderByExpr>,
1255    first: Option<Expr>,
1256    after: Option<Offset>,
1257    table_names: Vec<ObjectName>,
1258    distinct: Option<Vec<String>>,
1259    distinct_order: Option<Vec<OrderByExpr>>,
1260) -> Query {
1261    let mut projection = vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())];
1262    let is_distinct = distinct.is_some();
1263    let has_distinct_order = distinct_order.is_some();
1264    let mut distinct_order_by = distinct_order.unwrap_or_else(|| order_by.clone());
1265    if let Some(distinct) = distinct {
1266        let columns = distinct
1267            .into_iter()
1268            .map(|s| Value::DoubleQuotedString(s).to_string())
1269            .collect::<Vec<String>>();
1270        projection = vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
1271            value: ON.to_owned() + " (" + &columns.join(",") + ") *",
1272            quote_style: None,
1273        }))];
1274        columns.into_iter().rev().for_each(|c| {
1275            distinct_order_by.insert(
1276                0,
1277                OrderByExpr {
1278                    expr: Expr::Identifier(Ident {
1279                        value: c,
1280                        quote_style: None,
1281                    }),
1282                    asc: Some(true),
1283                    nulls_first: None,
1284                },
1285            );
1286        });
1287    }
1288    let q = Query {
1289        with: None,
1290        body: Box::new(SetExpr::Select(Box::new(Select {
1291            distinct: if is_distinct {
1292                Some(sqlparser::ast::Distinct::Distinct)
1293            } else {
1294                None
1295            },
1296            named_window: vec![],
1297            top: None,
1298            projection,
1299            into: None,
1300            from: table_names
1301                .into_iter()
1302                .map(|table_name| TableWithJoins {
1303                    relation: TableFactor::Table {
1304                        name: table_name,
1305                        alias: None,
1306                        args: None,
1307                        with_hints: vec![],
1308                    },
1309                    joins: vec![],
1310                })
1311                .collect(),
1312            lateral_views: vec![],
1313            selection: selection.map(|s| {
1314                if let Expr::Nested(nested) = s {
1315                    *nested
1316                } else {
1317                    s
1318                }
1319            }),
1320            group_by: vec![],
1321            cluster_by: vec![],
1322            distribute_by: vec![],
1323            sort_by: vec![],
1324            having: None,
1325            qualify: None,
1326        }))),
1327        order_by: distinct_order_by,
1328        limit: first,
1329        offset: after,
1330        fetch: None,
1331        locks: vec![],
1332    };
1333    if has_distinct_order && !order_by.is_empty() {
1334        Query {
1335            with: None,
1336            body: Box::new(SetExpr::Select(Box::new(Select {
1337                distinct: None,
1338                named_window: vec![],
1339                top: None,
1340                projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())],
1341                into: None,
1342                from: vec![TableWithJoins {
1343                    relation: TableFactor::Derived {
1344                        lateral: false,
1345                        subquery: Box::new(q),
1346                        alias: Some(TableAlias {
1347                            name: Ident {
1348                                value: "sorter".to_string(),
1349                                quote_style: None,
1350                            },
1351                            columns: vec![],
1352                        }),
1353                    },
1354                    joins: vec![],
1355                }],
1356                lateral_views: vec![],
1357                selection: None,
1358                group_by: vec![],
1359                cluster_by: vec![],
1360                distribute_by: vec![],
1361                sort_by: vec![],
1362                having: None,
1363                qualify: None,
1364            }))),
1365            order_by,
1366            limit: None,
1367            offset: None,
1368            fetch: None,
1369            locks: vec![],
1370        }
1371    } else {
1372        q
1373    }
1374}
1375
1376fn get_order<'a>(
1377    order: &IndexMap<Name, GqlValue>,
1378    variables: &'a IndexMap<Name, GqlValue>,
1379    sql_vars: &'a IndexMap<Name, JsonValue>,
1380) -> AnyResult<Vec<OrderByExpr>> {
1381    if order.contains_key("expr") && order.contains_key("dir") {
1382        let mut asc = None;
1383        if let Some(dir) = order.get("dir") {
1384            match dir {
1385                GqlValue::String(s) => {
1386                    asc = Some(s == "ASC");
1387                }
1388                GqlValue::Enum(e) => {
1389                    let s: &str = e.as_ref();
1390                    asc = Some(s == "ASC");
1391                }
1392                GqlValue::Variable(v) => {
1393                    if let Some(JsonValue::String(s)) = sql_vars.get(v) {
1394                        asc = Some(s == "ASC");
1395                    }
1396                }
1397                _ => {
1398                    return Err(anyhow!("Invalid value for order direction"));
1399                }
1400            }
1401        }
1402        if let Some(expr) = order.get("expr") {
1403            match expr {
1404                GqlValue::String(s) => {
1405                    return Ok(vec![OrderByExpr {
1406                        expr: Expr::Identifier(Ident {
1407                            value: s.clone(),
1408                            quote_style: Some(QUOTE_CHAR),
1409                        }),
1410                        asc,
1411                        nulls_first: None,
1412                    }]);
1413                }
1414                GqlValue::Object(args) => {
1415                    if let Some(expression) = get_filter(args, sql_vars)? {
1416                        return Ok(vec![OrderByExpr {
1417                            expr: expression,
1418                            asc,
1419                            nulls_first: None,
1420                        }]);
1421                    }
1422                }
1423                GqlValue::Variable(v) => {
1424                    if let Some(JsonValue::String(s)) = sql_vars.get(v) {
1425                        return Ok(vec![OrderByExpr {
1426                            expr: Expr::Identifier(Ident {
1427                                value: s.clone(),
1428                                quote_style: Some(QUOTE_CHAR),
1429                            }),
1430                            asc,
1431                            nulls_first: None,
1432                        }]);
1433                    }
1434                }
1435                _ => {
1436                    return Err(anyhow!("Invalid value for order expression"));
1437                }
1438            }
1439        }
1440    }
1441    let mut order_by = vec![];
1442    for (key, mut value) in order.iter() {
1443        if let GqlValue::Variable(name) = value {
1444            if let Some(new_value) = variables.get(name) {
1445                value = new_value
1446            }
1447        }
1448        match value {
1449            GqlValue::String(s) => {
1450                order_by.push(OrderByExpr {
1451                    expr: Expr::Identifier(Ident {
1452                        value: key.as_str().to_owned(),
1453                        quote_style: Some(QUOTE_CHAR),
1454                    }),
1455                    asc: Some(s == "ASC"),
1456                    nulls_first: None,
1457                });
1458            }
1459            GqlValue::Enum(e) => {
1460                let s: &str = e.as_ref();
1461                order_by.push(OrderByExpr {
1462                    expr: Expr::Identifier(Ident {
1463                        value: key.as_str().to_owned(),
1464                        quote_style: Some(QUOTE_CHAR),
1465                    }),
1466                    asc: Some(s == "ASC"),
1467                    nulls_first: None,
1468                });
1469            }
1470            GqlValue::Variable(name) => {
1471                if let JsonValue::String(value) = sql_vars
1472                    .get(name)
1473                    .ok_or(anyhow!("Variable {} not found in sql_vars", name.as_str()))?
1474                {
1475                    order_by.push(OrderByExpr {
1476                        expr: Expr::Identifier(Ident {
1477                            value: key.as_str().to_owned(),
1478                            quote_style: Some(QUOTE_CHAR),
1479                        }),
1480                        asc: Some(value == "ASC"),
1481                        nulls_first: None,
1482                    });
1483                }
1484            }
1485            _ => return Err(anyhow!("Invalid value for order expression")),
1486        }
1487    }
1488    Ok(order_by)
1489}
1490
1491fn get_distinct(distinct: Vec<GqlValue>) -> Option<Vec<String>> {
1492    let values: Vec<String> = distinct
1493        .iter()
1494        .filter_map(|v| match v {
1495            GqlValue::String(s) => Some(s.clone()),
1496            _ => None,
1497        })
1498        .collect();
1499
1500    if values.is_empty() {
1501        None
1502    } else {
1503        Some(values)
1504    }
1505}
1506
1507fn flatten(name: Name, value: &JsonValue, sql_vars: &mut IndexMap<Name, JsonValue>) -> GqlValue {
1508    match value {
1509        JsonValue::Null => GqlValue::Null,
1510        JsonValue::Bool(b) => {
1511            sql_vars.insert(name.clone(), JsonValue::Bool(*b));
1512            GqlValue::Variable(name)
1513        }
1514        JsonValue::String(s) => {
1515            if s == "ASC" || s == "DESC" {
1516                return GqlValue::Enum(Name::new(s.clone()));
1517            }
1518            sql_vars.insert(name.clone(), JsonValue::String(s.clone()));
1519            GqlValue::Variable(name)
1520        }
1521        JsonValue::Number(n) => {
1522            sql_vars.insert(name.clone(), JsonValue::Number(n.clone()));
1523            GqlValue::Variable(name)
1524        }
1525        JsonValue::Array(list) => {
1526            let new_list = list
1527                .iter()
1528                .enumerate()
1529                .map(|(i, v)| {
1530                    let new_name = format!("{name}_{i}");
1531                    flatten(Name::new(new_name), v, sql_vars)
1532                })
1533                .collect();
1534            GqlValue::List(new_list)
1535        }
1536        JsonValue::Object(o) => {
1537            let mut out = IndexMap::with_capacity(o.len());
1538            for (k, v) in o {
1539                let new_name = format!("{name}_{k}");
1540                let name = Name::new(new_name);
1541                let key = Name::new(k);
1542                let new_value = flatten(name, v, sql_vars);
1543                out.insert(key, new_value);
1544            }
1545            GqlValue::Object(out)
1546        }
1547    }
1548}
1549
1550fn get_filter_key<'a>(
1551    args: &'a IndexMap<Name, GqlValue>,
1552    variables: &'a IndexMap<Name, JsonValue>,
1553) -> AnyResult<Option<HashSet<Tag>>> {
1554    let mut tags = HashSet::new();
1555    let operator = args
1556        .get("operator")
1557        .map(|v| get_string_or_variable(v, variables))
1558        .ok_or(anyhow!("operator not found"))??;
1559    if operator == "eq" {
1560        let field = args
1561            .get("field")
1562            .map(|v| get_string_or_variable(v, variables))
1563            .ok_or(anyhow!("field not found"))??;
1564        if let Some(value) = args.get("value") {
1565            if let Ok(value) = get_string_or_variable(value, variables) {
1566                tags.insert(Tag { key: field, value });
1567            }
1568        }
1569    }
1570    if args.contains_key("children") {
1571        if let Some(GqlValue::List(children)) = args.get("children") {
1572            children
1573                .iter()
1574                .map(|v| match v {
1575                    GqlValue::Object(o) => get_filter_key(o, variables),
1576                    _ => Ok(None),
1577                })
1578                .filter_map(|v| v.ok().flatten())
1579                .for_each(|v| {
1580                    tags.extend(v);
1581                });
1582        }
1583    }
1584    if !tags.is_empty() {
1585        Ok(Some(tags))
1586    } else {
1587        Ok(None)
1588    }
1589}
1590
1591fn flatten_variables(
1592    variables: &Option<JsonValue>,
1593    definitions: Vec<Positioned<VariableDefinition>>,
1594) -> (IndexMap<Name, GqlValue>, IndexMap<Name, JsonValue>) {
1595    let mut sql_vars = IndexMap::new();
1596    let mut parameters = IndexMap::with_capacity(definitions.len());
1597    if let Some(JsonValue::Object(map)) = variables {
1598        for def in definitions {
1599            let def = def.node;
1600            let name = def.name.node;
1601            if let Some(value) = map.get(name.as_str()) {
1602                let new_value = flatten(name.clone(), value, &mut sql_vars);
1603                parameters.insert(name, new_value);
1604            }
1605        }
1606    }
1607    (parameters, sql_vars)
1608}
1609
1610fn parse_args<'a>(
1611    arguments: &'a Vec<(Positioned<Name>, Positioned<GqlValue>)>,
1612    variables: &'a IndexMap<Name, GqlValue>,
1613    sql_vars: &'a IndexMap<Name, JsonValue>,
1614) -> AnyResult<(
1615    Option<Expr>,
1616    Option<Vec<String>>,
1617    Option<Vec<OrderByExpr>>,
1618    Vec<OrderByExpr>,
1619    Option<Expr>,
1620    Option<Offset>,
1621    Option<HashSet<Tag>>,
1622)> {
1623    let mut selection = None;
1624    let mut order_by = vec![];
1625    let mut distinct = None;
1626    let mut distinct_order = None;
1627    let mut first = None;
1628    let mut after = None;
1629    let mut keys = None;
1630    for argument in arguments {
1631        let (p_key, p_value) = argument;
1632        let key = p_key.node.as_str();
1633        let mut value = p_value.node.clone();
1634        if let GqlValue::Variable(ref name) = value {
1635            if let Some(new_value) = variables.get(name) {
1636                value = new_value.clone();
1637                if let GqlValue::Null = value {
1638                    continue;
1639                }
1640            }
1641        }
1642        match (key, value) {
1643            ("id" | "email", value) => {
1644                selection = get_expr(
1645                    Expr::Identifier(Ident {
1646                        value: key.to_string(),
1647                        quote_style: Some(QUOTE_CHAR),
1648                    }),
1649                    "eq",
1650                    &value,
1651                    sql_vars,
1652                )?;
1653            }
1654            ("filter" | "where", GqlValue::Object(filter)) => {
1655                keys = get_filter_key(&filter, sql_vars)?;
1656                selection = get_filter(&filter, sql_vars)?;
1657            }
1658            ("distinct", GqlValue::Object(d)) => {
1659                if let Some(GqlValue::List(list)) = d.get("on") {
1660                    distinct = get_distinct(list.clone());
1661                }
1662                match d.get("order") {
1663                    Some(GqlValue::Object(order)) => {
1664                        distinct_order = Some(get_order(order, variables, sql_vars)?);
1665                    }
1666                    Some(GqlValue::List(list)) => {
1667                        let order = list
1668                            .iter()
1669                            .filter_map(|v| match v {
1670                                GqlValue::Object(o) => Some(o),
1671                                _ => None,
1672                            })
1673                            .map(|o| get_order(o, variables, sql_vars))
1674                            .collect::<AnyResult<Vec<Vec<OrderByExpr>>>>()?;
1675                        distinct_order = Some(order.into_iter().flatten().collect());
1676                    }
1677                    _ => {
1678                        return Err(anyhow!("Invalid value for distinct order"));
1679                    }
1680                }
1681            }
1682            ("order", GqlValue::Object(order)) => {
1683                order_by = get_order(&order, variables, sql_vars)?;
1684            }
1685            ("order", GqlValue::List(list)) => {
1686                let items = list
1687                    .iter()
1688                    .filter_map(|v| match v {
1689                        GqlValue::Object(o) => Some(o),
1690                        _ => None,
1691                    })
1692                    .map(|o| get_order(o, variables, sql_vars))
1693                    .collect::<AnyResult<Vec<Vec<OrderByExpr>>>>()?;
1694                order_by.append(
1695                    items
1696                        .into_iter()
1697                        .flatten()
1698                        .collect::<Vec<OrderByExpr>>()
1699                        .as_mut(),
1700                );
1701            }
1702            ("first" | "limit", GqlValue::Variable(name)) => {
1703                first = Some(get_value(&GqlValue::Variable(name), sql_vars)?);
1704            }
1705            ("first" | "limit", GqlValue::Number(count)) => {
1706                first = Some(Expr::Value(Value::Number(
1707                    count.as_i64().expect("int to be an i64").to_string(),
1708                    false,
1709                )));
1710            }
1711            ("after" | "offset", GqlValue::Variable(name)) => {
1712                after = Some(Offset {
1713                    value: get_value(&GqlValue::Variable(name), sql_vars)?,
1714                    rows: OffsetRows::None,
1715                });
1716            }
1717            ("after" | "offset", GqlValue::Number(count)) => {
1718                after = Some(Offset {
1719                    value: Expr::Value(Value::Number(
1720                        count.as_i64().expect("int to be an i64").to_string(),
1721                        false,
1722                    )),
1723                    rows: OffsetRows::None,
1724                });
1725            }
1726            _ => {
1727                return Err(anyhow!("Invalid argument for: {}", key));
1728            }
1729        }
1730    }
1731    Ok((
1732        selection,
1733        distinct,
1734        distinct_order,
1735        order_by,
1736        first,
1737        after,
1738        keys,
1739    ))
1740}
1741
1742fn get_mutation_columns<'a>(
1743    arguments: &'a Vec<(Positioned<Name>, Positioned<GqlValue>)>,
1744    variables: &'a IndexMap<Name, GqlValue>,
1745    sql_vars: &'a IndexMap<Name, JsonValue>,
1746) -> AnyResult<(Vec<Ident>, Vec<Vec<Expr>>)> {
1747    let mut columns = vec![];
1748    let mut rows = vec![];
1749    for argument in arguments {
1750        let (key, value) = argument;
1751        let (key, mut value) = (&key.node, &value.node);
1752        if let GqlValue::Variable(name) = value {
1753            if let Some(new_value) = variables.get(name) {
1754                value = new_value;
1755                if let GqlValue::Null = value {
1756                    continue;
1757                }
1758            }
1759        }
1760        match (key.as_ref(), value) {
1761            ("data", GqlValue::Object(data)) => {
1762                let mut row = vec![];
1763                for (key, value) in data.iter() {
1764                    columns.push(Ident {
1765                        value: key.to_string(),
1766                        quote_style: Some(QUOTE_CHAR),
1767                    });
1768                    row.push(get_value(value, sql_vars)?);
1769                }
1770                rows.push(row);
1771            }
1772            ("data", GqlValue::List(list)) => {
1773                if list.is_empty() {
1774                    continue;
1775                }
1776                for (i, item) in list.iter().enumerate() {
1777                    let mut row = vec![];
1778                    if let GqlValue::Object(data) = item {
1779                        for (key, value) in data.iter() {
1780                            if i == 0 {
1781                                columns.push(Ident {
1782                                    value: key.to_string(),
1783                                    quote_style: Some(QUOTE_CHAR),
1784                                });
1785                            }
1786                            row.push(get_value(value, sql_vars)?);
1787                        }
1788                    }
1789                    rows.push(row);
1790                }
1791            }
1792            _ => continue,
1793        }
1794    }
1795    Ok((columns, rows))
1796}
1797
1798fn get_mutation_assignments<'a>(
1799    arguments: &'a Vec<(Positioned<Name>, Positioned<GqlValue>)>,
1800    variables: &'a IndexMap<Name, GqlValue>,
1801    sql_vars: &'a IndexMap<Name, JsonValue>,
1802    has_updated_at_directive: bool,
1803) -> AnyResult<(Option<Expr>, Vec<Assignment>)> {
1804    let mut selection = None;
1805    let mut assignments = vec![];
1806    if has_updated_at_directive {
1807        assignments.push(Assignment {
1808            id: vec![Ident {
1809                value: "updated_at".to_string(),
1810                quote_style: Some(QUOTE_CHAR),
1811            }],
1812            value: Expr::Function(Function {
1813                order_by: vec![],
1814                name: ObjectName(vec![Ident {
1815                    value: "now".to_string(),
1816                    quote_style: None,
1817                }]),
1818                special: false,
1819                args: vec![],
1820                over: None,
1821                distinct: false,
1822            }),
1823        });
1824    }
1825    for argument in arguments {
1826        let (p_key, p_value) = argument;
1827        let (key, mut value) = (&p_key.node, &p_value.node);
1828        if let GqlValue::Variable(name) = value {
1829            if let Some(new_value) = variables.get(name) {
1830                value = new_value;
1831                if let GqlValue::Null = value {
1832                    continue;
1833                }
1834            }
1835        }
1836        match (key.as_ref(), value) {
1837            ("id" | "email", value) => {
1838                selection = get_expr(
1839                    Expr::Identifier(Ident {
1840                        value: key.to_string(),
1841                        quote_style: Some(QUOTE_CHAR),
1842                    }),
1843                    "eq",
1844                    value,
1845                    sql_vars,
1846                )?;
1847            }
1848            ("filter" | "where", GqlValue::Object(filter)) => {
1849                selection = get_filter(filter, sql_vars)?;
1850            }
1851            ("set", GqlValue::Object(data)) => {
1852                for (key, value) in data.iter() {
1853                    assignments.push(Assignment {
1854                        id: vec![Ident {
1855                            value: key.to_string(),
1856                            quote_style: Some(QUOTE_CHAR),
1857                        }],
1858                        value: get_value(value, sql_vars)?,
1859                    });
1860                }
1861            }
1862            ("inc" | "increment", GqlValue::Object(data)) => {
1863                for (key, value) in data.iter() {
1864                    let column_ident = Ident {
1865                        value: key.to_string(),
1866                        quote_style: Some(QUOTE_CHAR),
1867                    };
1868                    assignments.push(Assignment {
1869                        id: vec![column_ident.clone()],
1870                        value: Expr::BinaryOp {
1871                            left: Box::new(Expr::Identifier(column_ident)),
1872                            op: BinaryOperator::Plus,
1873                            right: Box::new(get_value(value, sql_vars)?),
1874                        },
1875                    });
1876                }
1877            }
1878            _ => return Err(anyhow!("Invalid argument for update at: {}", key)),
1879        }
1880    }
1881    Ok((selection, assignments))
1882}
1883
1884pub fn parse_query_meta(field: &Field) -> AnyResult<(&str, &str, bool, bool, Option<&str>)> {
1885    let mut is_aggregate = false;
1886    let mut is_single = false;
1887    let mut name = field.name.node.as_str();
1888    let mut schema_name = None;
1889    let key = field
1890        .alias
1891        .as_ref()
1892        .map_or_else(|| field.name.node.as_str(), |alias| alias.node.as_str());
1893
1894    if name.ends_with("_aggregate") {
1895        name = &name[..name.len() - 10];
1896        is_aggregate = true;
1897    } else if name.ends_with("_one") {
1898        name = &name[..name.len() - 4];
1899        is_single = true;
1900    }
1901
1902    if let Some(p_directive) = field
1903        .directives
1904        .iter()
1905        .find(|directive| directive.node.name.node.as_str() == "meta")
1906    {
1907        let directive = &p_directive.node;
1908        directive.arguments.iter().for_each(|(arg_name, argument)| {
1909            let arg_name = arg_name.node.as_str();
1910            if arg_name == "table" {
1911                if let GqlValue::String(table) = &argument.node {
1912                    name = table.as_ref();
1913                }
1914            } else if arg_name == "aggregate" {
1915                if let GqlValue::Boolean(aggregate) = &argument.node {
1916                    is_aggregate = *aggregate;
1917                }
1918            } else if arg_name == "single" {
1919                if let GqlValue::Boolean(single) = &argument.node {
1920                    is_single = *single;
1921                }
1922            } else if arg_name == "schema" {
1923                if let GqlValue::String(schema) = &argument.node {
1924                    schema_name = Some(schema.as_ref());
1925                }
1926            }
1927        });
1928    }
1929
1930    if is_aggregate && is_single {
1931        return Err(anyhow!("Query cannot be both aggregate and single"));
1932    }
1933
1934    Ok((name, key, is_aggregate, is_single, schema_name))
1935}
1936
1937#[must_use]
1938pub fn parse_mutation_meta(
1939    field: &Field,
1940) -> AnyResult<(&str, &str, bool, bool, bool, bool, Option<&str>)> {
1941    let mut is_insert = false;
1942    let mut is_update = false;
1943    let mut is_delete = false;
1944    let mut is_single = false;
1945    let mut schema_name = None;
1946    let mut name = field.name.node.as_ref();
1947    let key = field
1948        .alias
1949        .as_ref()
1950        .map_or_else(|| field.name.node.as_str(), |alias| alias.node.as_str());
1951
1952    if name.starts_with("insert_") {
1953        name = &name[7..];
1954        is_insert = true;
1955    } else if name.starts_with("update_") {
1956        name = &name[7..];
1957        is_update = true;
1958    } else if name.starts_with("delete_") {
1959        name = &name[7..];
1960        is_delete = true;
1961    }
1962
1963    if let Some(p_directive) = field
1964        .directives
1965        .iter()
1966        .find(|directive| directive.node.name.node.as_str() == "meta")
1967    {
1968        let directive = &p_directive.node;
1969        directive.arguments.iter().for_each(|(arg_name, argument)| {
1970            let arg_name = arg_name.node.as_str();
1971            if arg_name == "table" {
1972                if let GqlValue::String(table) = &argument.node {
1973                    name = table.as_ref();
1974                }
1975            } else if arg_name == "insert" {
1976                if let GqlValue::Boolean(insert) = &argument.node {
1977                    is_insert = *insert;
1978                }
1979            } else if arg_name == "update" {
1980                if let GqlValue::Boolean(update) = &argument.node {
1981                    is_update = *update;
1982                }
1983            } else if arg_name == "delete" {
1984                if let GqlValue::Boolean(delete) = &argument.node {
1985                    is_delete = *delete;
1986                }
1987            } else if arg_name == "single" {
1988                if let GqlValue::Boolean(delete) = &argument.node {
1989                    is_single = *delete;
1990                }
1991            } else if arg_name == "schema" {
1992                if let GqlValue::String(schema) = &argument.node {
1993                    schema_name = Some(schema.as_ref());
1994                }
1995            }
1996        });
1997    }
1998
1999    if is_insert && is_update {
2000        return Err(anyhow!("Mutation cannot be both insert and update"));
2001    } else if is_insert && is_delete {
2002        return Err(anyhow!("Mutation cannot be both insert and delete"));
2003    } else if is_update && is_delete {
2004        return Err(anyhow!("Mutation cannot be both update and delete"));
2005    }
2006
2007    Ok((
2008        name,
2009        key,
2010        is_insert,
2011        is_update,
2012        is_delete,
2013        is_single,
2014        schema_name,
2015    ))
2016}
2017
2018#[must_use]
2019pub fn wrap_mutation(key: &str, value: Statement, is_single: bool) -> Statement {
2020    let mut base = Expr::Function(Function {
2021        order_by: vec![],
2022        over: None,
2023        distinct: false,
2024        special: false,
2025        name: ObjectName(vec![Ident {
2026            value: "coalesce".to_string(),
2027            quote_style: None,
2028        }]),
2029        args: vec![
2030            FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(Function {
2031                order_by: vec![],
2032                name: ObjectName(vec![Ident {
2033                    value: JSON_AGG.to_string(),
2034                    quote_style: None,
2035                }]),
2036                args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
2037                    Expr::Identifier(Ident {
2038                        value: "result".to_string(),
2039                        quote_style: Some(QUOTE_CHAR),
2040                    }),
2041                ))],
2042                over: None,
2043                distinct: false,
2044                special: false,
2045            }))),
2046            FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
2047                Value::SingleQuotedString("[]".to_string()),
2048            ))),
2049        ],
2050    });
2051    if is_single {
2052        base = Expr::BinaryOp {
2053            left: Box::new(base),
2054            op: BinaryOperator::Custom("->".to_string()),
2055            right: Box::new(Expr::Value(Value::Number("0".to_string(), false))),
2056        }
2057    }
2058    Statement::Query(Box::new(Query {
2059        with: Some(With {
2060            cte_tables: vec![Cte {
2061                alias: TableAlias {
2062                    name: Ident {
2063                        value: "result".to_string(),
2064                        quote_style: Some(QUOTE_CHAR),
2065                    },
2066                    columns: vec![],
2067                },
2068                query: Box::new(Query {
2069                    with: None,
2070                    body: Box::new(SetExpr::Insert(value)),
2071                    order_by: vec![],
2072                    limit: None,
2073                    offset: None,
2074                    fetch: None,
2075                    locks: vec![],
2076                }),
2077                from: None,
2078            }],
2079            recursive: false,
2080        }),
2081        body: Box::new(SetExpr::Select(Box::new(Select {
2082            distinct: None,
2083            named_window: vec![],
2084            top: None,
2085            into: None,
2086            projection: vec![SelectItem::ExprWithAlias {
2087                expr: Expr::Function(Function {
2088                    order_by: vec![],
2089                    name: ObjectName(vec![Ident {
2090                        value: JSON_BUILD_OBJECT.to_string(),
2091                        quote_style: None,
2092                    }]),
2093                    args: vec![
2094                        FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
2095                            Value::SingleQuotedString(key.to_string()),
2096                        ))),
2097                        FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Subquery(Box::new(
2098                            Query {
2099                                with: None,
2100                                body: Box::new(SetExpr::Select(Box::new(Select {
2101                                    distinct: None,
2102                                    named_window: vec![],
2103                                    top: None,
2104                                    projection: vec![SelectItem::UnnamedExpr(base)],
2105                                    into: None,
2106                                    from: vec![TableWithJoins {
2107                                        relation: TableFactor::Table {
2108                                            name: ObjectName(vec![Ident {
2109                                                value: "result".to_string(),
2110                                                quote_style: Some(QUOTE_CHAR),
2111                                            }]),
2112                                            alias: None,
2113                                            args: None,
2114                                            with_hints: vec![],
2115                                        },
2116                                        joins: vec![],
2117                                    }],
2118                                    lateral_views: Vec::new(),
2119                                    selection: None,
2120                                    group_by: Vec::new(),
2121                                    cluster_by: Vec::new(),
2122                                    distribute_by: Vec::new(),
2123                                    sort_by: Vec::new(),
2124                                    having: None,
2125                                    qualify: None,
2126                                }))),
2127                                order_by: vec![],
2128                                limit: None,
2129                                offset: None,
2130                                fetch: None,
2131                                locks: vec![],
2132                            },
2133                        )))),
2134                    ],
2135                    over: None,
2136                    distinct: false,
2137                    special: false,
2138                }),
2139                alias: Ident {
2140                    value: DATA_LABEL.to_string(),
2141                    quote_style: Some(QUOTE_CHAR),
2142                },
2143            }],
2144            from: vec![],
2145            lateral_views: Vec::new(),
2146            selection: None,
2147            group_by: Vec::new(),
2148            cluster_by: Vec::new(),
2149            distribute_by: Vec::new(),
2150            sort_by: Vec::new(),
2151            having: None,
2152            qualify: None,
2153        }))),
2154        order_by: vec![],
2155        limit: None,
2156        offset: None,
2157        fetch: None,
2158        locks: vec![],
2159    }))
2160}
2161
2162#[derive(PartialEq, Eq, Hash)]
2163struct Tag {
2164    key: String,
2165    value: String,
2166}
2167
2168impl Debug for Tag {
2169    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2170        write!(f, "{}:{}", self.key, self.value)
2171    }
2172}
2173
2174impl ToString for Tag {
2175    fn to_string(&self) -> String {
2176        format!("{}:{}", self.key, self.value)
2177    }
2178}
2179
2180pub fn gql2sql<'a>(
2181    ast: ExecutableDocument,
2182    variables: &Option<JsonValue>,
2183    operation_name: Option<String>,
2184) -> AnyResult<(Statement, Option<Vec<JsonValue>>, Option<Vec<String>>)> {
2185    let mut statements = Vec::new();
2186    let operation = match ast.operations {
2187        DocumentOperations::Single(operation) => operation.node,
2188        DocumentOperations::Multiple(map) => {
2189            if let Some(name) = operation_name {
2190                map.get(name.as_str())
2191                    .ok_or_else(|| anyhow::anyhow!("Operation {} not found in the document", name))?
2192                    .node
2193                    .clone()
2194            } else {
2195                map.values()
2196                    .next()
2197                    .ok_or_else(|| {
2198                        anyhow::anyhow!("No operation found in the document, please specify one")
2199                    })?
2200                    .node
2201                    .clone()
2202            }
2203        }
2204    };
2205
2206    let (variables, sql_vars) = flatten_variables(variables, operation.variable_definitions);
2207    let mut tags: IndexMap<String, HashSet<Tag>> = IndexMap::new();
2208
2209    match operation.ty {
2210        OperationType::Query => {
2211            for selection in &operation.selection_set.node.items {
2212                match &selection.node {
2213                    Selection::Field(p_field) => {
2214                        let field = &p_field.node;
2215                        let (name, key, is_aggregate, is_single, schema_name) =
2216                            parse_query_meta(field)?;
2217                        let (selection, distinct, distinct_order, order_by, mut first, after, keys) =
2218                            parse_args(&field.arguments, &variables, &sql_vars)?;
2219                        if is_single {
2220                            first = Some(Expr::Value(Value::Number("1".to_string(), false)));
2221                        }
2222                        if let Some(keys) = keys {
2223                            tags.insert(name.to_string(), keys.into_iter().collect());
2224                        } else {
2225                            tags.insert(name.to_string(), HashSet::new());
2226                        };
2227                        let table_name = schema_name.map_or_else(
2228                            || {
2229                                ObjectName(vec![Ident {
2230                                    value: name.to_string(),
2231                                    quote_style: Some(QUOTE_CHAR),
2232                                }])
2233                            },
2234                            |schema_name| {
2235                                ObjectName(vec![
2236                                    Ident {
2237                                        value: schema_name.to_string(),
2238                                        quote_style: Some(QUOTE_CHAR),
2239                                    },
2240                                    Ident {
2241                                        value: name.to_string(),
2242                                        quote_style: Some(QUOTE_CHAR),
2243                                    },
2244                                ])
2245                            },
2246                        );
2247                        let base_query = get_filter_query(
2248                            selection,
2249                            order_by,
2250                            first,
2251                            after,
2252                            vec![table_name],
2253                            distinct,
2254                            distinct_order,
2255                        );
2256                        if is_aggregate {
2257                            let aggs = get_aggregate_projection(
2258                                &field.selection_set.node.items,
2259                                format!("Agg_{name}"),
2260                            )?;
2261                            statements.push((
2262                                key,
2263                                Query {
2264                                    with: None,
2265                                    body: Box::new(get_agg_query(
2266                                        aggs,
2267                                        vec![TableWithJoins {
2268                                            relation: TableFactor::Derived {
2269                                                lateral: false,
2270                                                subquery: Box::new(base_query),
2271                                                alias: Some(TableAlias {
2272                                                    name: Ident {
2273                                                        value: BASE.to_string(),
2274                                                        quote_style: Some(QUOTE_CHAR),
2275                                                    },
2276                                                    columns: vec![],
2277                                                }),
2278                                            },
2279                                            joins: vec![],
2280                                        }],
2281                                        None,
2282                                        ROOT_LABEL,
2283                                    )),
2284                                    order_by: vec![],
2285                                    limit: None,
2286                                    offset: None,
2287                                    fetch: None,
2288                                    locks: vec![],
2289                                },
2290                            ));
2291                        } else {
2292                            let (projection, joins, merges) = get_projection(
2293                                &field.selection_set.node.items,
2294                                name,
2295                                Some(BASE),
2296                                &variables,
2297                                &sql_vars,
2298                                &mut tags,
2299                            )?;
2300                            let root_query = get_root_query(
2301                                projection,
2302                                vec![TableWithJoins {
2303                                    relation: TableFactor::Derived {
2304                                        lateral: false,
2305                                        subquery: Box::new(base_query),
2306                                        alias: Some(TableAlias {
2307                                            name: Ident {
2308                                                value: BASE.to_string(),
2309                                                quote_style: Some(QUOTE_CHAR),
2310                                            },
2311                                            columns: vec![],
2312                                        }),
2313                                    },
2314                                    joins,
2315                                }],
2316                                None,
2317                                &merges,
2318                                is_single,
2319                                ROOT_LABEL,
2320                            );
2321                            statements.push((
2322                                key,
2323                                Query {
2324                                    with: None,
2325                                    body: Box::new(root_query),
2326                                    order_by: vec![],
2327                                    limit: None,
2328                                    offset: None,
2329                                    fetch: None,
2330                                    locks: vec![],
2331                                },
2332                            ));
2333                        };
2334                    }
2335                    Selection::FragmentSpread(_) => {
2336                        return Err(anyhow::anyhow!("Fragment not supported"))
2337                    }
2338                    Selection::InlineFragment(_) => {
2339                        return Err(anyhow::anyhow!("Fragment not supported"))
2340                    }
2341                }
2342            }
2343            let statement = Statement::Query(Box::new(Query {
2344                with: None,
2345                body: Box::new(SetExpr::Select(Box::new(Select {
2346                    distinct: None,
2347                    named_window: vec![],
2348                    top: None,
2349                    into: None,
2350                    projection: vec![SelectItem::ExprWithAlias {
2351                        alias: Ident {
2352                            value: DATA_LABEL.into(),
2353                            quote_style: Some(QUOTE_CHAR),
2354                        },
2355                        expr: Expr::Function(Function {
2356                            order_by: vec![],
2357                            name: ObjectName(vec![Ident {
2358                                value: JSON_BUILD_OBJECT.to_string(),
2359                                quote_style: None,
2360                            }]),
2361                            args: statements
2362                                .into_iter()
2363                                .flat_map(|(key, query)| {
2364                                    vec![
2365                                        FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
2366                                            Value::SingleQuotedString(key.to_string()),
2367                                        ))),
2368                                        FunctionArg::Unnamed(FunctionArgExpr::Expr(
2369                                            Expr::Subquery(Box::new(query)),
2370                                        )),
2371                                    ]
2372                                })
2373                                .collect(),
2374                            over: None,
2375                            distinct: false,
2376                            special: false,
2377                        }),
2378                    }],
2379                    from: vec![],
2380                    lateral_views: Vec::new(),
2381                    selection: None,
2382                    group_by: Vec::new(),
2383                    cluster_by: Vec::new(),
2384                    distribute_by: Vec::new(),
2385                    sort_by: Vec::new(),
2386                    having: None,
2387                    qualify: None,
2388                }))),
2389                order_by: vec![],
2390                limit: None,
2391                offset: None,
2392                fetch: None,
2393                locks: vec![],
2394            }));
2395            let params = if sql_vars.is_empty() {
2396                None
2397            } else {
2398                Some(sql_vars.into_values().collect())
2399            };
2400            if tags.is_empty() {
2401                return Ok((statement, params, None));
2402            }
2403            let mut sub_tags = tags
2404                .into_iter()
2405                .flat_map(|(key, values)| {
2406                    if values.is_empty() {
2407                        return vec![format!("type:{key}")];
2408                    }
2409                    values
2410                        .into_iter()
2411                        .map(|v| format!("type:{key}:{}", v.to_string()))
2412                        .collect::<Vec<_>>()
2413                })
2414                .collect::<Vec<String>>();
2415            sub_tags.sort_unstable();
2416            return Ok((statement, params, Some(sub_tags)));
2417        }
2418        OperationType::Mutation => {
2419            for selection in operation.selection_set.node.items {
2420                match &selection.node {
2421                    Selection::Field(p_field) => {
2422                        let field = &p_field.node;
2423                        let (name, key, is_insert, is_update, is_delete, is_single, schema_name) =
2424                            parse_mutation_meta(field)?;
2425
2426                        let table_name = schema_name.map_or_else(
2427                            || {
2428                                ObjectName(vec![Ident {
2429                                    value: name.to_string(),
2430                                    quote_style: Some(QUOTE_CHAR),
2431                                }])
2432                            },
2433                            |schema_name| {
2434                                ObjectName(vec![
2435                                    Ident {
2436                                        value: schema_name.to_string(),
2437                                        quote_style: Some(QUOTE_CHAR),
2438                                    },
2439                                    Ident {
2440                                        value: name.to_string(),
2441                                        quote_style: Some(QUOTE_CHAR),
2442                                    },
2443                                ])
2444                            },
2445                        );
2446                        if is_insert {
2447                            let (columns, rows) =
2448                                get_mutation_columns(&field.arguments, &variables, &sql_vars)?;
2449                            let (projection, _, _) = get_projection(
2450                                &field.selection_set.node.items,
2451                                name,
2452                                None,
2453                                &variables,
2454                                &sql_vars,
2455                                &mut tags,
2456                            )?;
2457                            let params = if sql_vars.is_empty() {
2458                                None
2459                            } else {
2460                                Some(sql_vars.into_values().collect())
2461                            };
2462                            return Ok((
2463                                wrap_mutation(
2464                                    key,
2465                                    Statement::Insert {
2466                                        or: None,
2467                                        into: true,
2468                                        table_name,
2469                                        columns,
2470                                        overwrite: false,
2471                                        source: Box::new(Query {
2472                                            with: None,
2473                                            body: Box::new(SetExpr::Values(Values {
2474                                                explicit_row: false,
2475                                                rows,
2476                                            })),
2477                                            order_by: vec![],
2478                                            limit: None,
2479                                            offset: None,
2480                                            fetch: None,
2481                                            locks: vec![],
2482                                        }),
2483                                        partitioned: None,
2484                                        after_columns: vec![],
2485                                        table: false,
2486                                        on: None,
2487                                        returning: Some(projection),
2488                                    },
2489                                    is_single,
2490                                ),
2491                                params,
2492                                None,
2493                            ));
2494                        } else if is_update {
2495                            let has_updated_at_directive = field
2496                                .directives
2497                                .iter()
2498                                .any(|d| d.node.name.node == "updatedAt");
2499                            let (projection, _, _) = get_projection(
2500                                &field.selection_set.node.items,
2501                                name,
2502                                None,
2503                                &variables,
2504                                &sql_vars,
2505                                &mut tags,
2506                            )?;
2507                            let (selection, assignments) = get_mutation_assignments(
2508                                &field.arguments,
2509                                &variables,
2510                                &sql_vars,
2511                                has_updated_at_directive,
2512                            )?;
2513                            let params = if sql_vars.is_empty() {
2514                                None
2515                            } else {
2516                                Some(sql_vars.into_values().collect())
2517                            };
2518                            return Ok((
2519                                wrap_mutation(
2520                                    key,
2521                                    Statement::Update {
2522                                        table: TableWithJoins {
2523                                            relation: TableFactor::Table {
2524                                                name: table_name,
2525                                                alias: None,
2526                                                args: None,
2527                                                with_hints: vec![],
2528                                            },
2529                                            joins: vec![],
2530                                        },
2531                                        assignments,
2532                                        from: None,
2533                                        selection,
2534                                        returning: Some(projection),
2535                                    },
2536                                    is_single,
2537                                ),
2538                                params,
2539                                None,
2540                            ));
2541                        } else if is_delete {
2542                            let (projection, _, _) = get_projection(
2543                                &field.selection_set.node.items,
2544                                name,
2545                                None,
2546                                &variables,
2547                                &sql_vars,
2548                                &mut tags,
2549                            )?;
2550                            let (selection, _) = get_mutation_assignments(
2551                                &field.arguments,
2552                                &variables,
2553                                &sql_vars,
2554                                false,
2555                            )?;
2556                            let params = if sql_vars.is_empty() {
2557                                None
2558                            } else {
2559                                Some(sql_vars.into_values().collect())
2560                            };
2561                            return Ok((
2562                                wrap_mutation(
2563                                    key,
2564                                    Statement::Delete {
2565                                        tables: vec![],
2566                                        from: vec![TableWithJoins {
2567                                            relation: TableFactor::Table {
2568                                                name: table_name,
2569                                                alias: None,
2570                                                args: None,
2571                                                with_hints: vec![],
2572                                            },
2573                                            joins: vec![],
2574                                        }],
2575                                        using: None,
2576                                        selection,
2577                                        returning: Some(projection),
2578                                    },
2579                                    is_single,
2580                                ),
2581                                params,
2582                                None,
2583                            ));
2584                        }
2585                    }
2586                    Selection::FragmentSpread(_) => {
2587                        return Err(anyhow::anyhow!("Fragment not supported"))
2588                    }
2589                    Selection::InlineFragment(_) => {
2590                        return Err(anyhow::anyhow!("Fragment not supported"))
2591                    }
2592                }
2593            }
2594        }
2595        OperationType::Subscription => return Err(anyhow::anyhow!("Subscription not supported")),
2596    }
2597    Err(anyhow!("No operation found"))
2598}
2599
2600#[cfg(test)]
2601mod tests {
2602    use super::*;
2603    use async_graphql_parser::parse_query;
2604
2605    use insta::assert_snapshot;
2606    use serde_json::json;
2607    use sqlparser::dialect::PostgreSqlDialect;
2608    use sqlparser::parser::Parser;
2609
2610    #[test]
2611    fn simple() -> Result<(), anyhow::Error> {
2612        let gqlast = parse_query(
2613            r#"query App {
2614                app(filter: { field: "id", operator: "eq", value: "345810043118026832" }, order: { name: ASC }) @meta(table: "App") {
2615                    id
2616                    components @relation(table: "Component", field: ["appId"], references: ["id"]) {
2617                        id
2618                        pageMeta @relation(table: "PageMeta", field: ["componentId"], references: ["id"], single: true) {
2619                          id
2620                          path
2621                        }
2622                        elements(order: { order: ASC }) @relation(table: "Element", field: ["componentParentId"], references: ["id"]) {
2623                            id
2624                            name
2625                        }
2626                    }
2627                }
2628                Component_aggregate(filter: { field: "appId", operator: "eq", value: "345810043118026832" }) {
2629                  count
2630                  min {
2631                    createdAt
2632                  }
2633                }
2634            }
2635            query Another {
2636                Component_aggregate(filter: { field: "appId", operator: "eq", value: "345810043118026832" }) {
2637                  count
2638                  min {
2639                    createdAt
2640                  }
2641                }
2642            }
2643        "#,
2644        )?;
2645        let (statement, _params, _tags) = gql2sql(gqlast, &None, Some("App".to_owned()))?;
2646        assert_snapshot!(statement.to_string());
2647        Ok(())
2648    }
2649
2650    #[test]
2651    fn mutation_insert() -> Result<(), anyhow::Error> {
2652        let gqlast = parse_query(
2653            r#"mutation insertVillains($data: [Villain_insert_input!]!) {
2654                insert(data: $data) @meta(table: "Villain", insert: true, schema: "auth") { id name }
2655            }"#,
2656        )?;
2657        let (statement, _params, _tags) = gql2sql(
2658            gqlast,
2659            &Some(json!({
2660                "data": [
2661                    { "name": "Ronan the Accuser" },
2662                    { "name": "Red Skull" },
2663                    { "name": "The Vulture" }
2664                ]
2665            })),
2666            None,
2667        )?;
2668        assert_snapshot!(statement.to_string());
2669        Ok(())
2670    }
2671
2672    #[test]
2673    fn mutation_update() -> Result<(), anyhow::Error> {
2674        let gqlast = parse_query(
2675            r#"mutation updateHero {
2676                update(
2677                    filter: { field: "secret_identity", operator: "eq", value: "Sam Wilson" },
2678                    set: {
2679                        name: "Captain America",
2680                    }
2681                    increment: {
2682                        number_of_movies: 1
2683                    }
2684                ) @meta(table: "Hero", update: true, schema: "auth") @updatedAt {
2685                    id
2686                    name
2687                    secret_identity
2688                    number_of_movies
2689                }
2690            }"#,
2691        )?;
2692        let (statement, _params, _tags) = gql2sql(gqlast, &None, None)?;
2693        assert_snapshot!(statement.to_string());
2694        Ok(())
2695    }
2696
2697    #[test]
2698    fn query_mega() -> Result<(), anyhow::Error> {
2699        let gqlast = parse_query(
2700            r#"query GetApp($orgId: String!, $appId: String!, $branch: String!) {
2701      app: App_one(
2702        filter: {
2703          field: "orgId",
2704          operator: "eq",
2705          value: $orgId,
2706          logicalOperator: "AND",
2707          children: [
2708            { field: "id", operator: "eq", value: $appId },
2709            { field: "branch", operator: "eq", value: $branch }
2710          ]
2711        }
2712      ) {
2713        orgId
2714        id
2715        branch
2716        name
2717        description
2718        theme
2719        favicon
2720        customCSS
2721        analytics
2722        customDomain
2723        components
2724          @relation(
2725            table: "Component"
2726            field: ["appId", "branch"]
2727            references: ["id", "branch"]
2728          ) {
2729          id
2730          branch
2731          ... on PageMeta
2732            @relation(
2733              table: "PageMeta"
2734              field: ["componentId", "branch"]
2735              references: ["id", "branch"]
2736              single: true
2737            ) {
2738            title
2739            description
2740            path
2741            socialImage
2742            urlParams
2743            loader
2744            protection
2745            maxAge
2746            sMaxAge
2747            staleWhileRevalidate
2748          }
2749          ... on ComponentMeta
2750            @relation(
2751              table: "ComponentMeta"
2752              field: ["componentId", "branch"]
2753              references: ["id", "branch"]
2754              single: true
2755            ) {
2756            title
2757            sources
2758              @relation(
2759                table: "Source"
2760                field: ["componentId", "branch"]
2761                references: ["id", "branch"]
2762              ) {
2763              id
2764              branch
2765              name
2766              provider
2767              description
2768              template
2769              instanceTemplate
2770              outputType
2771              source
2772              sourceProp
2773              componentId
2774              utilityId
2775              component(order: { order: ASC })
2776                @relation(
2777                  table: "Element"
2778                  field: ["id", "branch"]
2779                  references: ["componentId", "branch"]
2780                  single: true
2781                ) {
2782                id
2783                branch
2784                name
2785                kind
2786                source
2787                styles
2788                props
2789                order
2790                conditions
2791              }
2792              utility
2793                @relation(
2794                  table: "Utility"
2795                  field: ["id", "branch"]
2796                  references: ["componentId", "branch"]
2797                  single: true
2798                ) {
2799                id
2800                branch
2801                name
2802                kind
2803                kindId
2804                data
2805              }
2806            }
2807            events @relation(table: "Event", field: ["componentMetaId", "branch"], references: ["id", "branch"]) {
2808                id
2809                branch
2810                name
2811                label
2812                help
2813                type
2814            }
2815          }
2816        }
2817        connections @relation(table: "Connection", field: ["appId", "branch"], references: ["id", "branch"]) {
2818          id
2819          branch
2820          name
2821          kind
2822          prodUrl
2823          mutationSchema @relation(table: "Schema", field: ["mutationConnectionId", "branch"], references: ["id", "branch"], single: true) {
2824            id
2825            branch
2826            schema
2827          }
2828          endpoints @relation(table: "Endpoint", field: ["connectionId", "branch"], references: ["id", "branch"]) {
2829            id
2830            branch
2831            name
2832            method
2833            path
2834            responseSchemaId
2835            headers @relation(table: "Header", field: ["parentEndpointId", "branch"], references: ["id", "branch"]) {
2836              id
2837              branch
2838              key
2839              value
2840              dynamic
2841            }
2842            search @relation(table: "Search", field: ["endpointId", "branch"], references: ["id", "branch"]) {
2843              id
2844              branch
2845              key
2846              value
2847              dynamic
2848            }
2849          }
2850          headers @relation(table: "Header", field: ["parentConnectionId", "branch"], references: ["id", "branch"]) {
2851            id
2852            branch
2853            key
2854            value
2855            dynamic
2856          }
2857        }
2858        layouts @relation(table: "Layout", field: ["appId", "branch"], references: ["id", "branch"]) {
2859          id
2860          branch
2861          name
2862          source
2863          kind
2864          styles
2865          props
2866        }
2867        plugins @relation(table: "Plugin", field: ["appId", "branch"], references: ["id", "branch"]) {
2868          instanceId
2869          kind
2870        }
2871        schemas @relation(table: "Schema", field: ["appId", "branch"], references: ["id", "branch"]) {
2872          id
2873          branch
2874          schema
2875        }
2876        styles @relation(table: "Style", field: ["appId", "branch"], references: ["id", "branch"]) {
2877          id
2878          branch
2879          name
2880          kind
2881          styles
2882          isDefault
2883        }
2884        workflows @relation(table: "Workflow", field: ["appId", "branch"], references: ["id", "branch"]) {
2885          id
2886          branch
2887          name
2888          args
2889          steps(order: { order: ASC }) @relation(table: "Step", field: ["workflowId", "branch"], references: ["id", "branch"]) {
2890            id
2891            branch
2892            parentId
2893            kind
2894            kindId
2895            data
2896            order
2897          }
2898        }
2899      }
2900    }
2901"#,
2902        )?;
2903        let (statement, _params, _tags) = gql2sql(
2904            gqlast,
2905            &Some(json!({
2906                "orgId": "org",
2907                "appId": "app",
2908                "branch": "branch"
2909            })),
2910            None,
2911        )?;
2912        assert_snapshot!(statement.to_string());
2913        Ok(())
2914    }
2915
2916    #[test]
2917    fn query_frag() -> Result<(), anyhow::Error> {
2918        let gqlast = parse_query(
2919            r#"query GetApp($componentId: String!, $branch: String!) {
2920                component: Component_one(filter: { field: "id", operator: "eq", value: $componentId }) {
2921                   id
2922                   branch
2923                   ... on ComponentMeta @relation(
2924                        table: "ComponentMeta"
2925                        field: ["componentId"]
2926                        references: ["id"]
2927                        single: true
2928                    ) @args(
2929                        filter: {
2930                          field: "branch"
2931                          operator: "eq",
2932                          value: $branch,
2933                          logicalOperator: "OR",
2934                          children: [
2935                            { field: "branch", operator: "eq", value: "main" }
2936                          ]
2937                        }
2938                    ) {
2939                     title
2940                   }
2941                }
2942            }"#,
2943        )?;
2944        let (statement, _params, _tags) = gql2sql(
2945            gqlast,
2946            &Some(json!({
2947                "componentId": "comp",
2948                "branch": "branch"
2949            })),
2950            None,
2951        )?;
2952        assert_snapshot!(statement.to_string());
2953        Ok(())
2954    }
2955
2956    #[test]
2957    fn query_static() -> Result<(), anyhow::Error> {
2958        let gqlast = parse_query(
2959            r#"query GetApp($componentId: String!) {
2960                component: Component_one(filter: { field: "id", operator: "eq", value: $componentId }) {
2961                   id
2962                   branch
2963                   kind @static(value: "page")
2964                }
2965            }"#,
2966        )?;
2967        let (statement, _params, _tags) = gql2sql(
2968            gqlast,
2969            &Some(json!({
2970                "componentId": "fake"
2971            })),
2972            None,
2973        )?;
2974        assert_snapshot!(statement.to_string());
2975        Ok(())
2976    }
2977
2978    #[test]
2979    fn query_distinct() -> Result<(), anyhow::Error> {
2980        let gqlast = parse_query(
2981            r#"query GetApp($componentId: String!, $branch: String!) {
2982                component: Component_one(
2983                    filter: {
2984                        field: "id",
2985                        operator: "eq",
2986                        value: $componentId
2987                        logicalOperator: "AND",
2988                        children: [
2989                            { field: "branch", operator: "eq", value: $branch, logicalOperator: "OR", children: [
2990                                { field: "branch", operator: "eq", value: "main" }
2991                            ]}
2992                        ]
2993                    },
2994                    order: [
2995                        { orderKey: ASC }
2996                    ],
2997                    distinct: { on: ["id"], order: [{ expr: { field: "branch", operator: "eq", value: $branch }, dir: DESC }] }
2998                ) {
2999                   id
3000                   branch
3001                   kind @static(value: "page")
3002                   stuff(filter: { field: "componentId", operator: "eq", value: { _parentRef: "id" } }) @relation(table: "Stuff") {
3003                     id
3004                   }
3005                }
3006            }"#,
3007        )?;
3008        let (statement, _params, _tags) = gql2sql(
3009            gqlast,
3010            &Some(json!({
3011                "componentId": "fake",
3012                "branch": "branch",
3013            })),
3014            None,
3015        )?;
3016        assert_snapshot!(statement.to_string());
3017        Ok(())
3018    }
3019
3020    #[test]
3021    fn query_ast() -> Result<(), anyhow::Error> {
3022        let sql = r#"
3023            SELECT
3024            DISTINCT ON (column1)
3025            column2
3026            FROM
3027            table_name
3028            WHERE
3029            column1 = 'value'
3030            AND (column2 = 'value' OR column3 = 'value')
3031            ORDER BY
3032            column1,
3033            column2;
3034        "#;
3035        let dialect = PostgreSqlDialect {};
3036        let _sqlast = Parser::parse_sql(&dialect, sql)?;
3037        Ok(())
3038    }
3039
3040    #[test]
3041    fn query_sub_agg() -> Result<(), anyhow::Error> {
3042        let gqlast = parse_query(
3043            r#"query GetData {
3044                testing @meta(table: "UcwtYEtmmpXagcpcRiYKC") {
3045                    id
3046                    created_at
3047                    updated_at
3048                    anothers @relation(table: "N8Ag4Vgad4rYwcRmMJhGR", fields: ["id"], reference:["xb8nemrkchVQgxkXkCPhE"], aggregate: true) {
3049                        count
3050                        avg {
3051                          value
3052                        }
3053                    }
3054                    stuff @relation(table: "iYrk3kyTqaDQrLgjDaE9n", fields: ["eT86hgrpFB49r7N6AXz63"], references: ["id"], single: true) {
3055                        id
3056                    }
3057                }
3058            }"#,
3059        )?;
3060        let (statement, _params, _tags) = gql2sql(gqlast, &None, None)?;
3061        assert_snapshot!(statement.to_string());
3062        Ok(())
3063    }
3064
3065    #[test]
3066    fn query_schema_arg() -> Result<(), anyhow::Error> {
3067        let gqlast = parse_query(
3068            r#"
3069              query GetSession($sessionToken: String!) {
3070    session(
3071        filter: {
3072            field: "sessionToken"
3073            operator: "eq"
3074            value: $sessionToken
3075        }
3076    ) @meta(table: "sessions", single: true, schema: "auth") {
3077        sessionToken
3078        userId
3079        expires
3080        user2: user
3081            @relation(
3082                table: "users"
3083                field: ["id"]
3084                references: ["userId"]
3085                single: true
3086                schema: "auth"
3087            ) {
3088            id
3089            name
3090            email
3091            emailVerified
3092            image
3093        }
3094    }
3095}
3096            "#,
3097        )?;
3098        let (statement, _params, _tags) = gql2sql(
3099            gqlast,
3100            &Some(json!({
3101              "sessionToken": "fake"
3102            })),
3103            None,
3104        )?;
3105        assert_snapshot!(statement.to_string());
3106        Ok(())
3107    }
3108
3109    #[test]
3110    fn query_wrap_arg() -> Result<(), anyhow::Error> {
3111        let gqlast = parse_query(
3112            r#"
3113                mutation CreateVerificationToken($data: [VerificationToken!]!) {
3114                    insert(data: $data)
3115                        @meta(table: "verification_tokens", insert: true, schema: "auth", single: true) {
3116                        identifier
3117                        token
3118                        expires
3119                    }
3120                }
3121            "#,
3122        )?;
3123        let (statement, _params, _tags) = gql2sql(
3124            gqlast,
3125            &Some(json!({
3126            "data": [{
3127                "identifier": "nick@brevity.io",
3128                "token": "da978cc2c1e0e7b61e1be31b2e3979af576e494d68bd6f5dc156084d9924ee12",
3129                "expires": "2023-04-26T21:38:26"
3130                }]
3131            })),
3132            None,
3133        )?;
3134        assert_snapshot!(statement.to_string());
3135        Ok(())
3136    }
3137
3138    #[test]
3139    fn query_json_arg() -> Result<(), anyhow::Error> {
3140        let gqlast = parse_query(
3141            r#"
3142                query BrevityQuery($order_getTodoList: tXY7bJTNXP7RAhLFGybN4d_Order, $filter: tXY7bJTNXP7RAhLFGybN4d_Filter) {
3143                getTodoList(order: $order_getTodoList, filter: $filter) @meta(table: "tXY7bJTNXP7RAhLFGybN4d") {
3144                    id
3145                    cJ9jmpnjfYhRbCQBpWAzB8
3146                    cPQdcYiWcPWWVeKVniUMjy
3147                }
3148                }
3149            "#,
3150        )?;
3151        // let sql = r#""#;
3152        let (_statement, _params, _tags) = gql2sql(
3153            gqlast,
3154            &Some(json!({
3155                "order_getTodoList": {
3156                    "cPQdcYiWcPWWVeKVniUMjy": "ASC"
3157                },
3158                "filter": null
3159            })),
3160            None,
3161        )?;
3162        // assert_eq!(statement.to_string(), sql);
3163        Ok(())
3164    }
3165
3166    #[test]
3167    fn query_simple_filter() -> Result<(), anyhow::Error> {
3168        let gqlast = parse_query(
3169            r#"
3170                query Test($id: String!) {
3171                    record(id: $id) @meta(table: "Record") {
3172                        id
3173                        name
3174                        age
3175                    }
3176                }
3177            "#,
3178        )?;
3179        let (statement, _params, _tags) = gql2sql(
3180            gqlast,
3181            &Some(json!({
3182                "id": "fake"
3183            })),
3184            None,
3185        )?;
3186        assert_snapshot!(statement.to_string());
3187        Ok(())
3188    }
3189
3190    #[test]
3191    fn query_many_to_many() -> Result<(), anyhow::Error> {
3192        let gqlast = parse_query(
3193            r#"
3194                query ManyToMany($id: String!) {
3195                    currentUser(id: $id) @meta(table: "User") {
3196                        id
3197                        lists @relation(table: "wrHJEgwMUmdJ3eWtPLPk8", many: true) {
3198                            id
3199                        }
3200                    }
3201                }
3202            "#,
3203        )?;
3204        let (statement, _params, _tags) = gql2sql(
3205            gqlast,
3206            &Some(json!({
3207                "id": "fake"
3208            })),
3209            None,
3210        )?;
3211        assert_snapshot!(statement.to_string());
3212        Ok(())
3213    }
3214
3215    #[test]
3216    fn query_andre() -> Result<(), anyhow::Error> {
3217        let gqlast = parse_query(
3218            r#"
3219            query BrevityQuery($id_getH33iDwNVqqMxAnVEgPaThById: ID) {
3220            getH33iDwNVqqMxAnVEgPaThById(id: $id_getH33iDwNVqqMxAnVEgPaThById)
3221                @meta(table: "H33iDwNVqqMxAnVEgPaTh", single: true) {
3222                d8GJJg9DjNehPAeJcpTjM
3223                Fjjm3XAhyDmbhzymrrkRT_Aggregate
3224                @relation(
3225                    table: "Fjjm3XAhyDmbhzymrrkRT"
3226                    fields: ["id"]
3227                    aggregate: true
3228                    references: ["TbFeY8XVMaYnkQjDPWMkb_id"]
3229                ) {
3230                avg {
3231                    XF4f6Qrhk86AX6dFWjYDt
3232                }
3233                }
3234                q6pJYTjmbprTNRdqG9Jrw
3235                egeyQ33H3z4EqzcRVFchV
3236                HYWfawTyxPNUf9a4DAH79
3237                H33iDwNVqqMxAnVEgPaTh_by_MdYg7jdht8ByhnKdfXBAb
3238                @relation(
3239                    table: "MdYg7jdht8ByhnKdfXBAb"
3240                    fields: ["id"]
3241                    single: true
3242                    references: ["MiyNcUJzKGJgQ9BERD8fr_id"]
3243                ) {
3244                H6hp6JGhzgPTYmLYwLk8P
3245                id
3246                }
3247                zFjEBPkLYmEAxLHrt3N4B
3248                LJDX6neXAYeXt9aVWxTRk
3249                FwpKpCegQH4EkzbjbNqVn
3250                ayipLT8iKHNTdhmiVqmxq
3251                Mr3R877DKbWTNWRzmEjxE_Aggregate
3252                @relation(many: true, table: "Mr3R877DKbWTNWRzmEjxE", aggregate: true) {
3253                count
3254                }
3255                r7xwAFrckDaVLwPzUAADB
3256                H33iDwNVqqMxAnVEgPaTh_by_User
3257                @relation(
3258                    table: "User"
3259                    fields: ["id"]
3260                    single: true
3261                    references: ["Gb8jAGqGDbYqfeqDDxKUF_id"]
3262                ) {
3263                gnHezR9MdBFH9kCthN3aB
3264                created_at
3265                id
3266                }
3267                id
3268            }
3269            }
3270            "#,
3271        )?;
3272        let (statement, params, _tags) = gql2sql(
3273            gqlast,
3274            &Some(json!({
3275              "id_getH33iDwNVqqMxAnVEgPaThById": "HAzqFfhQGbaB6WKBr6LA7"
3276            })),
3277            None,
3278        )?;
3279        assert_snapshot!(statement.to_string());
3280        assert_snapshot!(serde_json::to_string_pretty(&params)?);
3281        Ok(())
3282    }
3283
3284    #[test]
3285    fn mutation_delete() -> Result<(), anyhow::Error> {
3286        let gqlast = parse_query(
3287            r#"
3288            mutation DeleteVerificationToken(
3289                $identifier: String!
3290                $token: String!
3291                ) {
3292                delete(
3293                    filter: {
3294                    field: "identifier"
3295                    operator: "eq"
3296                    value: $identifier
3297                    logicalOperator: "AND"
3298                    children: [{ field: "token", operator: "eq", value: $token }]
3299                    }
3300                ) @meta(table: "verification_tokens", delete: true, schema: "auth") {
3301                    identifier
3302                    token
3303                    expires
3304                }
3305            }
3306            "#,
3307        )?;
3308        let (statement, _params, _tags) = gql2sql(
3309            gqlast,
3310            &Some(json!({ "token": "12345", "identifier": "fake@email.com" })),
3311            None,
3312        )?;
3313        assert_snapshot!(statement.to_string());
3314        Ok(())
3315    }
3316
3317    #[test]
3318    fn mutation_image() -> Result<(), anyhow::Error> {
3319        let gqlast = parse_query(
3320            r#"
3321            mutation Update($id: String!, $set: dogUpdateInput!) {
3322                update(
3323                  filter: {
3324                    field: "id"
3325                    operator: "eq"
3326                    value: $id
3327                  }
3328                  set: $set
3329                ) @meta(table: "WFqGH6dk8MpxfpHXh7awi", update: true) {
3330                  id
3331                }
3332              }
3333            "#,
3334        )?;
3335        let (statement, params, _tags) = gql2sql(
3336            gqlast,
3337            &Some(
3338                json!({"id":"ffj9ACLQqpzjyh8yNFeQ6","set":{"updated_at":"2023-06-06T19:41:47+00:00","ynWfqMzGjjVQYzbKx4rMX":"DOGGY","QYtpTcmJCe6zfCHWwpNjR":"MYDOG","a8heQgUMyFync44JACwKA":{"src":"https://assets.brevity.io/uploads/jwy1g8rs7bxr9ptkaf6sy/lp_image-1685987665741.png","width":588,"height":1280}}}),
3339            ),
3340            None,
3341        )?;
3342        assert_snapshot!(statement.to_string());
3343        assert_snapshot!(serde_json::to_string_pretty(&params)?);
3344        Ok(())
3345    }
3346    #[test]
3347    fn nested_query() -> Result<(), anyhow::Error> {
3348        let gqlast = parse_query(
3349            r#"
3350                query BrevityQuery($id_getU7BBKiUwTgwiWMcgUYA4CById: ID) {
3351                getU7BBKiUwTgwiWMcgUYA4CById(id: $id_getU7BBKiUwTgwiWMcgUYA4CById) @meta(table: "U7BBKiUwTgwiWMcgUYA4C", single: true) {
3352                    BtaHL8fRtKFw8gDJULFYp
3353                    WFqGH6dk8MpxfpHXh7awi_by_U7BBKiUwTgwiWMcgUYA4C @relation(table: "WFqGH6dk8MpxfpHXh7awi", fields: ["MHPB9NP84gr3eXBmBfbxh_id"], references: ["id"]) {
3354                    ynWfqMzGjjVQYzbKx4rMX
3355                    QYtpTcmJCe6zfCHWwpNjR
3356                    MHPB9NP84gr3eXBmBfbxh_id @relation(table: "U7BBKiUwTgwiWMcgUYA4C", fields: ["id"], single: true, references: ["MHPB9NP84gr3eXBmBfbxh_id"]) {
3357                        id
3358                        __typename
3359                    }
3360                    id
3361                    }
3362                    id
3363                }
3364                }
3365            "#,
3366        )?;
3367        let (statement, params, _tags) = gql2sql(
3368            gqlast,
3369            &Some(json!({ "id_getU7BBKiUwTgwiWMcgUYA4CById": "piWkMrFFXgdQBBkzf84MD" })),
3370            None,
3371        )?;
3372        assert_snapshot!(statement.to_string());
3373        assert_snapshot!(serde_json::to_string_pretty(&params)?);
3374        Ok(())
3375    }
3376}