Skip to main content

prqlc/sql/
gen_query.rs

1//! This module is responsible for translating PRQL AST to sqlparser AST, and
2//! then to a String. We use sqlparser because it's trivial to create the string
3//! once it's in their AST (it's just `.to_string()`). It also lets us support a
4//! few dialects of SQL immediately.
5use itertools::Itertools;
6use regex::Regex;
7use sqlparser::ast::{
8    self as sql_ast, Join, JoinConstraint, JoinOperator, Select, SelectItem, SetExpr, TableAlias,
9    TableFactor, TableWithJoins,
10};
11
12use super::gen_expr::*;
13use super::gen_projection::*;
14use super::operators::translate_operator;
15use super::pq::ast::{Cte, CteKind, RelationExpr, RelationExprKind, SqlRelation, SqlTransform};
16use super::{Context, Dialect};
17use crate::debug;
18use crate::ir::pl::{JoinSide, Literal};
19use crate::ir::rq::{CId, Expr, ExprKind, RelationLiteral, RelationalQuery};
20use crate::utils::{BreakUp, Pluck};
21use crate::{Error, Result, WithErrorInfo};
22use prqlc_parser::generic::InterpolateItem;
23
24type Transform = SqlTransform<RelationExpr, ()>;
25
26pub fn translate_query(query: RelationalQuery, dialect: Option<Dialect>) -> Result<sql_ast::Query> {
27    // compile from RQ to PQ
28    let (pq_query, mut ctx) = super::pq::compile_query(query, dialect)?;
29
30    debug::log_stage(debug::Stage::Sql(debug::StageSql::Main));
31    let mut query = translate_relation(pq_query.main_relation, &mut ctx)?;
32
33    if !pq_query.ctes.is_empty() {
34        // attach CTEs
35        let mut cte_tables = Vec::new();
36        let mut recursive = false;
37        for cte in pq_query.ctes {
38            let (cte, rec) = translate_cte(cte, &mut ctx)?;
39            cte_tables.push(cte);
40            recursive = recursive || rec;
41        }
42        query.with = Some(sql_ast::With {
43            recursive,
44            cte_tables,
45            with_token: sqlparser::ast::helpers::attached_token::AttachedToken::empty(),
46        });
47    }
48
49    debug::log_entry(|| debug::DebugEntryKind::ReprSqlParser(Box::new(query.clone())));
50    Ok(query)
51}
52
53fn translate_relation(relation: SqlRelation, ctx: &mut Context) -> Result<sql_ast::Query> {
54    match relation {
55        SqlRelation::AtomicPipeline(pipeline) => translate_pipeline(pipeline, ctx),
56        SqlRelation::Literal(data) => translate_relation_literal(data, ctx),
57        SqlRelation::SString(items) => translate_query_sstring(items, ctx),
58        SqlRelation::Operator { name, args } => translate_query_operator(name, args, ctx),
59    }
60}
61
62fn translate_pipeline(pipeline: Vec<Transform>, ctx: &mut Context) -> Result<sql_ast::Query> {
63    use SqlTransform::*;
64
65    let (select, set_ops) =
66        pipeline.break_up(|t| matches!(t, Union { .. } | Except { .. } | Intersect { .. }));
67
68    let select = translate_select_pipeline(select, ctx)?;
69
70    translate_set_ops_pipeline(select, set_ops, ctx)
71}
72
73fn translate_select_pipeline(
74    mut pipeline: Vec<Transform>,
75    ctx: &mut Context,
76) -> Result<sql_ast::Query> {
77    let table_count = count_tables(&pipeline);
78    log::debug!("atomic query contains {table_count} tables");
79    ctx.push_query();
80    ctx.query.omit_ident_prefix = table_count == 1;
81    ctx.query.pre_projection = true;
82
83    let mut from: Vec<_> = pipeline
84        .pluck(|t| t.into_from())
85        .into_iter()
86        .map(|source| -> Result<TableWithJoins> {
87            Ok(TableWithJoins {
88                relation: translate_relation_expr(source, ctx)?,
89                joins: vec![],
90            })
91        })
92        .try_collect()?;
93
94    let joins = pipeline
95        .pluck(|t| t.into_join())
96        .into_iter()
97        .map(|j| translate_join(j, ctx))
98        .collect::<Result<Vec<_>>>()?;
99    if !joins.is_empty() {
100        if let Some(from) = from.last_mut() {
101            from.joins = joins;
102        } else {
103            unreachable!()
104        }
105    }
106
107    let projection = pipeline
108        .pluck(|t| t.into_select())
109        .into_iter()
110        .exactly_one()
111        .unwrap();
112    let projection = translate_wildcards(&ctx.anchor, projection);
113    let mut projection = translate_select_items(projection.0, projection.1, ctx)?;
114
115    let order_by = pipeline.pluck(|t| t.into_sort());
116    let takes = pipeline.pluck(|t| t.into_take());
117    let is_distinct = pipeline.iter().any(|t| matches!(t, SqlTransform::Distinct));
118    let distinct_ons = pipeline.pluck(|t| t.into_distinct_on());
119    let distinct = if is_distinct {
120        Some(sql_ast::Distinct::Distinct)
121    } else if !distinct_ons.is_empty() {
122        Some(sql_ast::Distinct::On(
123            distinct_ons
124                .into_iter()
125                .exactly_one()
126                .unwrap()
127                .into_iter()
128                .map(|id| translate_cid(id, ctx).map(|x| x.into_ast()))
129                .collect::<Result<Vec<_>>>()?,
130        ))
131    } else {
132        None
133    };
134
135    // When we have DISTINCT ON, we must have at least a wildcard in the projection
136    // (PostgreSQL requires DISTINCT ON to have a non-empty SELECT list)
137    // Replace NULL placeholder with wildcard if present, or add wildcard if empty
138    if matches!(distinct, Some(sql_ast::Distinct::On(_))) {
139        if projection.len() == 1 {
140            if let SelectItem::UnnamedExpr(sql_ast::Expr::Value(ref v)) = projection[0] {
141                if matches!(v.value, sql_ast::Value::Null) {
142                    projection[0] =
143                        SelectItem::Wildcard(sql_ast::WildcardAdditionalOptions::default());
144                }
145            }
146        } else if projection.is_empty() {
147            projection.push(SelectItem::Wildcard(
148                sql_ast::WildcardAdditionalOptions::default(),
149            ));
150        }
151    }
152
153    // Split the pipeline into before & after the aggregate
154    let (mut before_agg, mut after_agg) =
155        pipeline.break_up(|t| matches!(t, Transform::Aggregate { .. } | Transform::Union { .. }));
156
157    // WHERE and HAVING
158    let where_ = filter_of_conditions(before_agg.pluck(|t| t.into_filter()), ctx)?;
159    let having = filter_of_conditions(after_agg.pluck(|t| t.into_filter()), ctx)?;
160
161    // GROUP BY
162    let aggregate = after_agg.pluck(|t| t.into_aggregate()).into_iter().next();
163    let group_by: Vec<CId> = aggregate.map(|(part, _)| part).unwrap_or_default();
164    ctx.query.allow_stars = ctx.dialect.stars_in_group();
165    let group_by = sql_ast::GroupByExpr::Expressions(try_into_exprs(group_by, ctx, None)?, vec![]);
166    ctx.query.allow_stars = true;
167
168    ctx.query.pre_projection = false;
169
170    let ranges = takes.into_iter().map(|x| x.range).collect();
171    let take = range_of_ranges(ranges)?;
172    let offset = take.start.map(|s| s - 1).unwrap_or(0);
173    let limit = take.end.map(|e| e - offset);
174
175    let mut offset = if offset == 0 {
176        None
177    } else {
178        let kind = ExprKind::Literal(Literal::Integer(offset));
179        let expr = Expr { kind, span: None };
180        Some(sqlparser::ast::Offset {
181            value: translate_expr(expr, ctx)?.into_ast(),
182            rows: if ctx.dialect.use_fetch() {
183                sqlparser::ast::OffsetRows::Rows
184            } else {
185                sqlparser::ast::OffsetRows::None
186            },
187        })
188    };
189
190    // Use sorting from the frame
191    let mut order_by: Vec<sql_ast::OrderByExpr> = order_by
192        .last()
193        .map(|sorts| {
194            sorts
195                .iter()
196                .map(|s| translate_column_sort(s, ctx))
197                .try_collect()
198        })
199        .transpose()?
200        .unwrap_or_default();
201
202    let (fetch, limit) = if ctx.dialect.use_fetch() {
203        (limit.map(|l| fetch_of_i64(l, ctx)), None)
204    } else {
205        (None, limit.map(expr_of_i64))
206    };
207
208    // If we have a FETCH we need to make sure that:
209    // - we have an OFFSET (set to 0)
210    // - we have an ORDER BY (see https://stackoverflow.com/a/44919325)
211    if fetch.is_some() {
212        if offset.is_none() {
213            let kind = ExprKind::Literal(Literal::Integer(0));
214            let expr = Expr { kind, span: None };
215            offset = Some(sqlparser::ast::Offset {
216                value: translate_expr(expr, ctx)?.into_ast(),
217                rows: sqlparser::ast::OffsetRows::Rows,
218            })
219        }
220        if order_by.is_empty() {
221            // When DISTINCT is used, MSSQL requires ORDER BY items to appear
222            // in the SELECT list. Use the first column from the projection
223            // instead of (SELECT NULL).
224            let order_expr = is_distinct
225                .then(|| first_expr_from_projection(&projection))
226                .flatten()
227                .unwrap_or_else(|| {
228                    sql_ast::Expr::Value(
229                        sql_ast::Value::Placeholder("(SELECT NULL)".to_string()).into(),
230                    )
231                });
232            order_by.push(sql_ast::OrderByExpr {
233                expr: order_expr,
234                options: sqlparser::ast::OrderByOptions {
235                    asc: None,
236                    nulls_first: None,
237                },
238                with_fill: None,
239            });
240        }
241    }
242
243    ctx.pop_query();
244
245    Ok(sql_ast::Query {
246        order_by: if order_by.is_empty() {
247            None
248        } else {
249            Some(sql_ast::OrderBy {
250                kind: sqlparser::ast::OrderByKind::Expressions(order_by),
251                interpolate: None,
252            })
253        },
254        limit_clause: if limit.is_some() || offset.is_some() {
255            Some(sql_ast::LimitClause::LimitOffset {
256                limit,
257                offset,
258                limit_by: Vec::new(),
259            })
260        } else {
261            None
262        },
263        fetch,
264        ..default_query(SetExpr::Select(Box::new(Select {
265            distinct,
266            projection,
267            from,
268            selection: where_,
269            group_by,
270            having,
271            ..default_select()
272        })))
273    })
274}
275
276fn translate_set_ops_pipeline(
277    mut top: sql_ast::Query,
278    mut pipeline: Vec<Transform>,
279    context: &mut Context,
280) -> Result<sql_ast::Query> {
281    // reverse, so it's easier (and O(1)) to pop
282    pipeline.reverse();
283
284    while let Some(transform) = pipeline.pop() {
285        use SqlTransform::*;
286
287        let op = match &transform {
288            Union { .. } => sql_ast::SetOperator::Union,
289            Except { .. } => sql_ast::SetOperator::Except,
290            Intersect { .. } => sql_ast::SetOperator::Intersect,
291            Sort(_) => continue,
292            _ => unreachable!(),
293        };
294
295        let (distinct, bottom) = match transform {
296            Union { distinct, bottom }
297            | Except { distinct, bottom }
298            | Intersect { distinct, bottom } => (distinct, bottom),
299            _ => unreachable!(),
300        };
301
302        // Some engines (like SQLite) do not support subqueries between simple parentheses, so we keep
303        // the general `SELECT * FROM (query)` or `SELECT * FROM cte` by default for complex cases.
304        //
305        // Some engines (like Postgres) need the subquery directly to properly match column type on
306        // both sides. For those:
307        // 1. we need the subquery as-is or in parentheses
308        // 2. we need to avoid CTEs
309
310        // Left hand side (aka top)
311        let left = query_to_set_expr(top, context);
312
313        // Right hand side (aka bottom)
314        let right_rel = translate_relation_expr(bottom, context)?;
315        let right = if let TableFactor::Derived { subquery, .. } = right_rel {
316            query_to_set_expr(*subquery, context)
317        } else {
318            Box::new(SetExpr::Select(Box::new(sql_ast::Select {
319                projection: vec![SelectItem::Wildcard(
320                    sql_ast::WildcardAdditionalOptions::default(),
321                )],
322                from: vec![TableWithJoins {
323                    relation: right_rel,
324                    joins: vec![],
325                }],
326                ..default_select()
327            })))
328        };
329
330        top = default_query(SetExpr::SetOperation {
331            left,
332            right,
333            set_quantifier: if distinct {
334                if context.dialect.set_ops_distinct() {
335                    sql_ast::SetQuantifier::Distinct
336                } else {
337                    sql_ast::SetQuantifier::None
338                }
339            } else {
340                sql_ast::SetQuantifier::All
341            },
342            op,
343        });
344    }
345
346    Ok(top)
347}
348
349fn translate_relation_expr(relation_expr: RelationExpr, ctx: &mut Context) -> Result<TableFactor> {
350    let alias = Some(&relation_expr.riid)
351        .and_then(|riid| ctx.anchor.relation_instances.get(riid))
352        .and_then(|ri| ri.table_ref.name.clone());
353
354    Ok(match relation_expr.kind {
355        RelationExprKind::Ref(tid) => {
356            let decl = ctx.anchor.lookup_table_decl(&tid).unwrap();
357
358            // prepare names
359            let table_name = decl.name.clone().unwrap();
360
361            let name = sql_ast::ObjectName(
362                translate_ident(Some(table_name.clone()), None, ctx)
363                    .into_iter()
364                    .map(sqlparser::ast::ObjectNamePart::Identifier)
365                    .collect(),
366            );
367
368            TableFactor::Table {
369                name,
370                alias: if Some(table_name.name) == alias {
371                    None
372                } else {
373                    translate_table_alias(alias, ctx)
374                },
375                args: None,
376                with_hints: vec![],
377                with_ordinality: false,
378                version: None,
379                partitions: vec![],
380                json_path: None,
381                sample: None,
382                index_hints: vec![],
383            }
384        }
385        RelationExprKind::SubQuery(query) => {
386            let query = translate_relation(query, ctx)?;
387
388            let alias = translate_table_alias(alias, ctx);
389
390            TableFactor::Derived {
391                lateral: false,
392                subquery: Box::new(query),
393                alias,
394            }
395        }
396    })
397}
398
399fn translate_table_alias(alias: Option<String>, ctx: &mut Context) -> Option<TableAlias> {
400    alias
401        .map(|ident| translate_ident_part(ident, ctx))
402        .map(simple_table_alias)
403}
404
405fn translate_join(
406    (side, with, filter): (JoinSide, RelationExpr, Expr),
407    ctx: &mut Context,
408) -> Result<Join> {
409    let relation = translate_relation_expr(with, ctx)?;
410
411    let constraint = JoinConstraint::On(translate_expr(filter, ctx)?.into_ast());
412
413    Ok(Join {
414        relation,
415        join_operator: match side {
416            JoinSide::Inner => JoinOperator::Inner(constraint),
417            JoinSide::Left => JoinOperator::LeftOuter(constraint),
418            JoinSide::Right => JoinOperator::RightOuter(constraint),
419            JoinSide::Full => JoinOperator::FullOuter(constraint),
420        },
421        global: false,
422    })
423}
424
425fn translate_cte(cte: Cte, ctx: &mut Context) -> Result<(sql_ast::Cte, bool)> {
426    let decl = ctx.anchor.lookup_table_decl(&cte.tid).unwrap();
427    let cte_name = decl.name.clone().unwrap();
428
429    let cte_name = translate_ident(Some(cte_name), None, ctx).pop().unwrap();
430
431    let (query, recursive) = match cte.kind {
432        // base case
433        CteKind::Normal(rel) => (translate_relation(rel, ctx)?, false),
434
435        // special: WITH RECURSIVE
436        CteKind::Loop { initial, step } => {
437            // compile initial
438            let initial = query_to_set_expr(translate_relation(initial, ctx)?, ctx);
439
440            let step = query_to_set_expr(translate_relation(step, ctx)?, ctx);
441
442            // build CTE and its SELECT
443            let inner_query = default_query(SetExpr::SetOperation {
444                op: sql_ast::SetOperator::Union,
445                set_quantifier: sql_ast::SetQuantifier::All,
446                left: initial,
447                right: step,
448            });
449
450            (inner_query, true)
451
452            // RECURSIVE can only follow WITH directly.
453            // Initial implementation assumed that it applies only to the first CTE.
454            // This meant that it had to wrap any-non-first CTE into a *nested* WITH, so the inner
455            // WITH could be RECURSIVE.
456            // This is implementation of that, in case some dialect requires it.
457            // let inner_cte = sql_ast::Cte {
458            //     alias: simple_table_alias(cte_name.clone()),
459            //     query: Box::new(inner_query),
460            //     from: None,
461            // };
462            // let outer_query = sql_ast::Query {
463            //     with: Some(sql_ast::With {
464            //         recursive: true,
465            //         cte_tables: vec![inner_cte],
466            //     }),
467            //     ..default_query(sql_ast::SetExpr::Select(Box::new(sql_ast::Select {
468            //         projection: vec![SelectItem::Wildcard(
469            //             sql_ast::WildcardAdditionalOptions::default(),
470            //         )],
471            //         from: vec![TableWithJoins {
472            //             relation: TableFactor::Table {
473            //                 name: sql_ast::ObjectName(vec![cte_name.clone()]),
474            //                 alias: None,
475            //                 args: None,
476            //                 with_hints: Vec::new(),
477            //             },
478            //             joins: vec![],
479            //         }],
480            //         ..default_select()
481            //     })))
482            // };
483            // (outer_query, false)
484        }
485    };
486
487    let cte = sql_ast::Cte {
488        alias: cte_table_alias(cte_name),
489        query: Box::new(query),
490        from: None,
491        materialized: None,
492        closing_paren_token: sqlparser::ast::helpers::attached_token::AttachedToken::empty(),
493    };
494    Ok((cte, recursive))
495}
496
497fn translate_relation_literal(data: RelationLiteral, ctx: &Context) -> Result<sql_ast::Query> {
498    // TODO: this could be made to use VALUES instead of SELECT UNION ALL SELECT
499    //       I'm not sure about compatibility though.
500    // edit: probably not, because VALUES has no way of setting names of the columns
501    //       Postgres will just name them column1, column2 as so on.
502    //       Which means we can use VALUES, but only if this is not the top-level statement,
503    //       where they really matter.
504
505    if data.rows.is_empty() {
506        let mut nulls: Vec<_> = (data.columns.iter())
507            .map(|col_name| SelectItem::ExprWithAlias {
508                expr: sql_ast::Expr::Value(sql_ast::Value::Null.into()),
509                alias: translate_ident_part(col_name.clone(), ctx),
510            })
511            .collect();
512
513        // empty projection is a parse error in some dialects, let's inject a NULL
514        if nulls.is_empty() {
515            nulls.push(SelectItem::UnnamedExpr(sql_ast::Expr::Value(
516                sql_ast::Value::Null.into(),
517            )));
518        }
519
520        return Ok(default_query(sql_ast::SetExpr::Select(Box::new(Select {
521            projection: nulls,
522            selection: Some(sql_ast::Expr::Value(sql_ast::Value::Boolean(false).into())),
523            ..default_select()
524        }))));
525    }
526
527    let mut selects = Vec::with_capacity(data.rows.len());
528
529    for row in data.rows {
530        let body = sql_ast::SetExpr::Select(Box::new(Select {
531            projection: std::iter::zip(data.columns.clone(), row)
532                .map(|(col, value)| -> Result<_> {
533                    Ok(SelectItem::ExprWithAlias {
534                        expr: translate_literal(value, ctx)?,
535                        alias: translate_ident_part(col, ctx),
536                    })
537                })
538                .try_collect()?,
539            ..default_select()
540        }));
541
542        selects.push(body)
543    }
544
545    let mut body = selects.remove(0);
546    for select in selects {
547        body = SetExpr::SetOperation {
548            op: sql_ast::SetOperator::Union,
549            set_quantifier: sql_ast::SetQuantifier::All,
550            left: Box::new(body),
551            right: Box::new(select),
552        }
553    }
554
555    Ok(default_query(body))
556}
557
558pub(super) fn translate_query_sstring(
559    items: Vec<InterpolateItem<Expr>>,
560    ctx: &mut Context,
561) -> Result<sql_ast::Query> {
562    let string = translate_sstring(items, ctx)?;
563
564    let re = Regex::new(r"(?i)^SELECT\b").unwrap();
565    let prefix = string.trim().get(0..7).unwrap_or_default();
566
567    if re.is_match(prefix) {
568        if let Some(string) = string.trim().strip_prefix(prefix) {
569            return Ok(default_query(sql_ast::SetExpr::Select(Box::new(
570                sql_ast::Select {
571                    projection: vec![sql_ast::SelectItem::UnnamedExpr(sql_ast::Expr::Identifier(
572                        sql_ast::Ident::new(string),
573                    ))],
574                    ..default_select()
575                },
576            ))));
577        }
578    }
579
580    Err(
581        Error::new_simple("s-strings representing a table must start with `SELECT `".to_string())
582            .push_hint("this is a limitation by current compiler implementation"),
583    )
584}
585
586pub(super) fn translate_query_operator(
587    name: String,
588    args: Vec<Expr>,
589    ctx: &mut Context,
590) -> Result<sql_ast::Query> {
591    let from_s_string = translate_operator(name, args, ctx)?;
592
593    let s_string = format!(" * FROM {}", from_s_string.text);
594
595    Ok(default_query(sql_ast::SetExpr::Select(Box::new(
596        sql_ast::Select {
597            projection: vec![sql_ast::SelectItem::UnnamedExpr(sql_ast::Expr::Identifier(
598                sql_ast::Ident::new(s_string),
599            ))],
600            ..default_select()
601        },
602    ))))
603}
604
605fn filter_of_conditions(exprs: Vec<Expr>, context: &mut Context) -> Result<Option<sql_ast::Expr>> {
606    Ok(if let Some(cond) = all(exprs) {
607        Some(translate_expr(cond, context)?.into_ast())
608    } else {
609        None
610    })
611}
612
613fn all(mut exprs: Vec<Expr>) -> Option<Expr> {
614    let mut condition = exprs.pop()?;
615    while let Some(expr) = exprs.pop() {
616        condition = Expr {
617            kind: ExprKind::Operator {
618                name: "std.and".to_string(),
619                args: vec![expr, condition],
620            },
621            span: None,
622        };
623    }
624    Some(condition)
625}
626
627fn default_query(body: sql_ast::SetExpr) -> sql_ast::Query {
628    sql_ast::Query {
629        with: None,
630        body: Box::new(body),
631        order_by: None,
632        limit_clause: None,
633        fetch: None,
634        locks: Vec::new(),
635        for_clause: None,
636        settings: None,
637        format_clause: None,
638        pipe_operators: Vec::new(),
639    }
640}
641
642fn default_select() -> Select {
643    Select {
644        distinct: None,
645        top: None,
646        top_before_distinct: false,
647        projection: Vec::new(),
648        into: None,
649        from: Vec::new(),
650        lateral_views: Vec::new(),
651        selection: None,
652        group_by: sql_ast::GroupByExpr::Expressions(vec![], vec![]),
653        cluster_by: Vec::new(),
654        distribute_by: Vec::new(),
655        sort_by: Vec::new(),
656        having: None,
657        named_window: vec![],
658        qualify: None,
659        value_table_mode: None,
660        window_before_qualify: false,
661        connect_by: None,
662        prewhere: None,
663        exclude: None,
664        select_token: sqlparser::ast::helpers::attached_token::AttachedToken::empty(),
665        flavor: sqlparser::ast::SelectFlavor::Standard,
666    }
667}
668
669fn simple_table_alias(name: sql_ast::Ident) -> TableAlias {
670    TableAlias {
671        name,
672        columns: Vec::new(),
673        explicit: true,
674    }
675}
676
677fn cte_table_alias(name: sql_ast::Ident) -> TableAlias {
678    TableAlias {
679        name,
680        columns: Vec::new(),
681        explicit: false,
682    }
683}
684
685fn query_to_set_expr(query: sql_ast::Query, context: &mut Context) -> Box<SetExpr> {
686    let is_simple = query.with.is_none()
687        && query.order_by.is_none()
688        && query.limit_clause.is_none()
689        && query.fetch.is_none()
690        && query.locks.is_empty();
691
692    if is_simple {
693        return query.body;
694    }
695
696    // Query is not simple, so we need to wrap it.
697    //
698    // Some engines (like SQLite) do not support subqueries between simple parentheses, so we keep
699    // the general `SELECT * FROM (query)` by default.
700    //
701    // Some engines (like Postgres) may need the subquery directly as-is or between parentheses
702    // to properly match columns for syntaxes like `UNION`, `EXCEPT`, and `INTERSECT`.
703    // Incidentally, the parenthesis syntax `(query)` allows for complex queries.
704    let set_expr = if context.dialect.prefers_subquery_parentheses_shorthand() {
705        SetExpr::Query(query.into())
706    } else {
707        SetExpr::Select(Box::new(Select {
708            projection: vec![SelectItem::Wildcard(
709                sql_ast::WildcardAdditionalOptions::default(),
710            )],
711            from: vec![TableWithJoins {
712                relation: TableFactor::Derived {
713                    lateral: false,
714                    subquery: Box::new(query),
715                    alias: Some(simple_table_alias(sql_ast::Ident::new(
716                        context.anchor.table_name.gen(),
717                    ))),
718                },
719                joins: vec![],
720            }],
721            ..default_select()
722        }))
723    };
724
725    Box::new(set_expr)
726}
727
728fn count_tables(transforms: &[Transform]) -> usize {
729    let mut count = 0;
730    for transform in transforms {
731        if let Transform::Join { .. } | Transform::From(_) = transform {
732            count += 1;
733        }
734    }
735
736    count
737}
738
739/// Extract the first expression from a projection for use in ORDER BY.
740/// Returns None if the projection is empty or only contains wildcards.
741fn first_expr_from_projection(projection: &[SelectItem]) -> Option<sql_ast::Expr> {
742    for item in projection {
743        match item {
744            SelectItem::UnnamedExpr(expr) => return Some(expr.clone()),
745            SelectItem::ExprWithAlias { alias, .. } => {
746                return Some(sql_ast::Expr::Identifier(alias.clone()));
747            }
748            SelectItem::Wildcard(_) | SelectItem::QualifiedWildcard(_, _) => continue,
749        }
750    }
751    None
752}
753
754#[cfg(test)]
755mod test {
756    use insta::assert_snapshot;
757
758    #[test]
759    fn test_variable_after_aggregate() {
760        let query = &r#"
761        from employees
762        group {title, emp_no} (
763            aggregate {emp_salary = average salary}
764        )
765        group {title} (
766            aggregate {avg_salary = average emp_salary}
767        )
768        "#;
769
770        let sql_ast = crate::tests::compile(query).unwrap();
771
772        assert_snapshot!(sql_ast, @r"
773        WITH table_0 AS (
774          SELECT
775            title,
776            AVG(salary) AS _expr_0
777          FROM
778            employees
779          GROUP BY
780            title,
781            emp_no
782        )
783        SELECT
784          title,
785          AVG(_expr_0) AS avg_salary
786        FROM
787          table_0
788        GROUP BY
789          title
790        ");
791    }
792
793    #[test]
794    fn test_derive_filter() {
795        // I suspect that the anchoring algorithm has a architectural flaw:
796        // it assumes that it can materialize all columns, even if their
797        // Compute is in a prior CTE. The problem is that because anchoring is
798        // computed back-to-front, we don't know where Compute will end up when
799        // materializing following transforms.
800        //
801        // If algorithm is changed to be front-to-back, preprocess_reorder can
802        // be (must be) removed.
803
804        let query = &r#"
805        from employees
806        derive {global_rank = rank country}
807        filter country == "USA"
808        derive {rank = rank country}
809        "#;
810
811        let sql_ast = crate::tests::compile(query).unwrap();
812
813        assert_snapshot!(sql_ast, @r"
814        WITH table_0 AS (
815          SELECT
816            *,
817            RANK() OVER () AS global_rank
818          FROM
819            employees
820        )
821        SELECT
822          *,
823          RANK() OVER () AS rank
824        FROM
825          table_0
826        WHERE
827          country = 'USA'
828        ");
829    }
830
831    #[test]
832    fn test_filter_windowed() {
833        // #806
834        let query = &r#"
835        from tbl1
836        filter (average bar) > 3
837        "#;
838
839        assert_snapshot!(crate::tests::compile(query).unwrap(), @r"
840        WITH table_0 AS (
841          SELECT
842            *,
843            AVG(bar) OVER () AS _expr_0
844          FROM
845            tbl1
846        )
847        SELECT
848          *
849        FROM
850          table_0
851        WHERE
852          _expr_0 > 3
853        ");
854    }
855
856    #[test]
857    fn test_distinct_on_with_aggregate() {
858        // #5556: DISTINCT ON with aggregate should include wildcard
859        let query = &r#"
860        prql target:sql.postgres
861
862        from t1
863        group {id, name} (take 1)
864        aggregate {c=count this}
865        "#;
866
867        assert_snapshot!(crate::tests::compile(query).unwrap(), @r"
868        WITH table_0 AS (
869          SELECT
870            DISTINCT ON (id, name) *
871          FROM
872            t1
873        )
874        SELECT
875          COUNT(*) AS c
876        FROM
877          table_0
878        ");
879    }
880
881    #[test]
882    fn test_join_with_inaccessible_table() {
883        // issue #5280: join referencing table not accessible in current scope
884        let query = r#"
885        from c = companies
886        join ca = companies_addresses (c.tax_code == ca.company)
887        group c.tax_code (
888          join a = addresses (a.id == ca.address)
889          sort {-ca.created_at}
890          take 2..
891        )
892        sort tax_code
893        "#;
894
895        let err = crate::tests::compile(query).unwrap_err();
896        assert!(err.to_string().contains("not accessible in this context"));
897    }
898}