Skip to main content

polyglot_sql/builder/
plan.rs

1//! Binding-neutral, SQLGlot-compatible builder operations.
2//!
3//! The ordinary [`crate::builder`] module is an ergonomic Rust API. This module
4//! provides a serializable, immutable expression plan so language bindings can
5//! share coercion, parsing, and AST-editing semantics without duplicating them.
6
7use crate::builder::{self, engine, Expr};
8use crate::dialects::Dialect;
9use crate::error::{Error, Result};
10use crate::expressions::*;
11use crate::generator::NotInStyle;
12use crate::Expression;
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "bindings")]
15use ts_rs::TS;
16
17pub const BUILDER_PROTOCOL_VERSION: u8 = 1;
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20#[cfg_attr(feature = "bindings", derive(TS))]
21#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
22pub enum BuilderValue {
23    Null,
24    Bool(bool),
25    Integer(#[cfg_attr(feature = "bindings", ts(type = "number"))] i64),
26    Float(f64),
27    String(String),
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31#[cfg_attr(feature = "bindings", derive(TS))]
32#[serde(rename_all = "snake_case")]
33pub enum BinaryOperator {
34    Eq,
35    Neq,
36    Lt,
37    Lte,
38    Gt,
39    Gte,
40    And,
41    Or,
42    Xor,
43    Add,
44    Sub,
45    Mul,
46    Div,
47    Mod,
48    Like,
49    Ilike,
50    Rlike,
51    Is,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
55#[cfg_attr(feature = "bindings", derive(TS))]
56#[serde(rename_all = "snake_case")]
57pub enum UnaryOperator {
58    Not,
59    Neg,
60    IsNull,
61    IsNotNull,
62}
63
64impl std::convert::From<BinaryOperator> for engine::BinaryKind {
65    fn from(value: BinaryOperator) -> Self {
66        match value {
67            BinaryOperator::Eq => Self::Eq,
68            BinaryOperator::Neq => Self::Neq,
69            BinaryOperator::Lt => Self::Lt,
70            BinaryOperator::Lte => Self::Lte,
71            BinaryOperator::Gt => Self::Gt,
72            BinaryOperator::Gte => Self::Gte,
73            BinaryOperator::And => Self::And,
74            BinaryOperator::Or => Self::Or,
75            BinaryOperator::Xor => Self::Xor,
76            BinaryOperator::Add => Self::Add,
77            BinaryOperator::Sub => Self::Sub,
78            BinaryOperator::Mul => Self::Mul,
79            BinaryOperator::Div => Self::Div,
80            BinaryOperator::Mod => Self::Mod,
81            BinaryOperator::Like => Self::Like,
82            BinaryOperator::Ilike => Self::ILike,
83            BinaryOperator::Rlike => Self::RLike,
84            BinaryOperator::Is => Self::Is,
85        }
86    }
87}
88
89impl std::convert::From<UnaryOperator> for engine::UnaryKind {
90    fn from(value: UnaryOperator) -> Self {
91        match value {
92            UnaryOperator::Not => Self::Not,
93            UnaryOperator::Neg => Self::Neg,
94            UnaryOperator::IsNull => Self::IsNull,
95            UnaryOperator::IsNotNull => Self::IsNotNull,
96        }
97    }
98}
99
100#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
101#[cfg_attr(feature = "bindings", derive(TS))]
102#[serde(rename_all = "snake_case")]
103pub enum JoinType {
104    #[default]
105    Inner,
106    Left,
107    Right,
108    Full,
109    Cross,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113#[cfg_attr(feature = "bindings", derive(TS))]
114#[serde(rename_all = "snake_case")]
115pub enum LockType {
116    Update,
117    Share,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
121#[cfg_attr(feature = "bindings", derive(TS))]
122#[serde(rename_all = "snake_case")]
123pub enum BuiltinFunction {
124    Count,
125    CountStar,
126    CountDistinct,
127    Sum,
128    Avg,
129    Min,
130    Max,
131    ApproxDistinct,
132    Upper,
133    Lower,
134    Length,
135    Trim,
136    Ltrim,
137    Rtrim,
138    Reverse,
139    Initcap,
140    Substring,
141    Replace,
142    ConcatWs,
143    Coalesce,
144    NullIf,
145    IfNull,
146    Abs,
147    Round,
148    Floor,
149    Ceil,
150    Power,
151    Sqrt,
152    Ln,
153    Exp,
154    Sign,
155    Greatest,
156    Least,
157    CurrentDate,
158    CurrentTime,
159    CurrentTimestamp,
160    RowNumber,
161    Rank,
162    DenseRank,
163}
164
165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
166#[cfg_attr(feature = "bindings", derive(TS))]
167#[serde(tag = "kind", rename_all = "snake_case")]
168pub enum BuildNode {
169    Plan {
170        plan: Box<BuilderPlan>,
171    },
172    Ast {
173        expression: Box<Expression>,
174    },
175    Sql {
176        sql: String,
177    },
178    Literal {
179        value: BuilderValue,
180    },
181    Column {
182        name: String,
183    },
184    Table {
185        name: String,
186    },
187    Star,
188    Function {
189        name: String,
190        args: Vec<BuildNode>,
191    },
192    Builtin {
193        function: BuiltinFunction,
194        #[serde(default)]
195        args: Vec<BuildNode>,
196    },
197    Extract {
198        field: String,
199        expression: Box<BuildNode>,
200    },
201    Binary {
202        op: BinaryOperator,
203        left: Box<BuildNode>,
204        right: Box<BuildNode>,
205    },
206    Unary {
207        op: UnaryOperator,
208        expression: Box<BuildNode>,
209    },
210    Alias {
211        expression: Box<BuildNode>,
212        alias: String,
213    },
214    Cast {
215        expression: Box<BuildNode>,
216        to: String,
217    },
218    Between {
219        expression: Box<BuildNode>,
220        low: Box<BuildNode>,
221        high: Box<BuildNode>,
222    },
223    InList {
224        expression: Box<BuildNode>,
225        values: Vec<BuildNode>,
226        #[serde(default)]
227        negated: bool,
228    },
229    Ordered {
230        expression: Box<BuildNode>,
231        #[serde(default)]
232        desc: bool,
233    },
234    Select {
235        expressions: Vec<BuildNode>,
236    },
237    Case {
238        operand: Option<Box<BuildNode>>,
239    },
240    Update {
241        table: String,
242        #[serde(default)]
243        assignments: Vec<BuilderAssignment>,
244        where_clause: Option<Box<BuildNode>>,
245        from: Option<String>,
246    },
247    Delete {
248        table: String,
249        where_clause: Option<Box<BuildNode>>,
250    },
251    Insert {
252        into: String,
253        expression: Option<Box<BuildNode>>,
254        #[serde(default)]
255        columns: Vec<String>,
256    },
257    Merge {
258        target: String,
259    },
260}
261
262/// An immutable builder program. The base expression is evaluated once and the
263/// operations are applied from left to right.
264#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265#[cfg_attr(feature = "bindings", derive(TS))]
266pub struct BuilderPlan {
267    pub base: BuildNode,
268    #[serde(default)]
269    pub operations: Vec<BuildOperation>,
270}
271
272impl BuilderPlan {
273    pub fn new(base: BuildNode) -> Self {
274        Self {
275            base,
276            operations: Vec::new(),
277        }
278    }
279
280    pub fn apply(mut self, operation: BuildOperation) -> Self {
281        self.operations.push(operation);
282        self
283    }
284
285    pub fn into_node(self) -> BuildNode {
286        BuildNode::Plan {
287            plan: Box::new(self),
288        }
289    }
290}
291
292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
293#[cfg_attr(feature = "bindings", derive(TS))]
294pub struct BuilderAssignment {
295    pub column: String,
296    pub value: BuildNode,
297}
298
299#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
300#[cfg_attr(feature = "bindings", derive(TS))]
301#[serde(tag = "kind", rename_all = "snake_case")]
302pub enum BuildOperation {
303    Select {
304        expressions: Vec<BuildNode>,
305        #[serde(default = "default_true")]
306        append: bool,
307    },
308    From {
309        source: BuildNode,
310    },
311    Join {
312        source: BuildNode,
313        on: Option<BuildNode>,
314        #[serde(default)]
315        join_type: JoinType,
316    },
317    Where {
318        expressions: Vec<BuildNode>,
319        #[serde(default = "default_true")]
320        append: bool,
321    },
322    GroupBy {
323        expressions: Vec<BuildNode>,
324        #[serde(default = "default_true")]
325        append: bool,
326    },
327    Having {
328        expressions: Vec<BuildNode>,
329        #[serde(default = "default_true")]
330        append: bool,
331    },
332    OrderBy {
333        expressions: Vec<BuildNode>,
334        #[serde(default = "default_true")]
335        append: bool,
336    },
337    SortBy {
338        expressions: Vec<BuildNode>,
339        #[serde(default = "default_true")]
340        append: bool,
341    },
342    Limit {
343        expression: BuildNode,
344    },
345    Offset {
346        expression: BuildNode,
347    },
348    Distinct {
349        #[serde(default = "default_true")]
350        enabled: bool,
351    },
352    Qualify {
353        expressions: Vec<BuildNode>,
354        #[serde(default = "default_true")]
355        append: bool,
356    },
357    LateralView {
358        expression: BuildNode,
359        table_alias: Option<String>,
360        #[serde(default)]
361        column_aliases: Vec<String>,
362        #[serde(default)]
363        outer: bool,
364    },
365    Window {
366        name: String,
367        #[serde(default)]
368        partition_by: Vec<BuildNode>,
369        #[serde(default)]
370        order_by: Vec<BuildNode>,
371    },
372    Lock {
373        lock_type: LockType,
374    },
375    Hint {
376        text: String,
377    },
378    Ctas {
379        table: String,
380        #[serde(default)]
381        replace: bool,
382        #[serde(default)]
383        temporary: bool,
384    },
385    Subquery {
386        alias: Option<String>,
387    },
388    Union {
389        other: BuildNode,
390        #[serde(default = "default_true")]
391        distinct: bool,
392    },
393    Intersect {
394        other: BuildNode,
395        #[serde(default = "default_true")]
396        distinct: bool,
397    },
398    Except {
399        other: BuildNode,
400        #[serde(default = "default_true")]
401        distinct: bool,
402    },
403    When {
404        condition: BuildNode,
405        result: BuildNode,
406    },
407    Else {
408        result: BuildNode,
409    },
410    Set {
411        assignments: Vec<BuilderAssignment>,
412    },
413    InsertColumns {
414        columns: Vec<String>,
415    },
416    Values {
417        rows: Vec<Vec<BuildNode>>,
418        #[serde(default = "default_true")]
419        append: bool,
420    },
421    Query {
422        query: BuildNode,
423    },
424    MergeUsing {
425        source: BuildNode,
426        on: BuildNode,
427    },
428    WhenMatchedUpdate {
429        assignments: Vec<BuilderAssignment>,
430        condition: Option<BuildNode>,
431    },
432    WhenMatchedDelete {
433        condition: Option<BuildNode>,
434    },
435    WhenNotMatchedInsert {
436        columns: Vec<String>,
437        values: Vec<BuildNode>,
438        condition: Option<BuildNode>,
439    },
440}
441
442fn default_true() -> bool {
443    true
444}
445
446#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
447#[cfg_attr(feature = "bindings", derive(TS))]
448#[serde(tag = "kind", rename_all = "snake_case")]
449pub enum BuilderOutput {
450    Ast,
451    Sql {
452        #[serde(default = "generic_dialect")]
453        dialect: String,
454    },
455}
456
457fn generic_dialect() -> String {
458    "generic".to_string()
459}
460
461#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
462#[cfg_attr(feature = "bindings", derive(TS))]
463pub struct BuildRequest {
464    #[serde(default = "protocol_version")]
465    pub version: u8,
466    #[serde(default = "generic_dialect")]
467    pub read_dialect: String,
468    pub plan: BuilderPlan,
469    #[serde(default = "ast_output")]
470    pub output: BuilderOutput,
471}
472
473fn protocol_version() -> u8 {
474    BUILDER_PROTOCOL_VERSION
475}
476
477fn ast_output() -> BuilderOutput {
478    BuilderOutput::Ast
479}
480
481#[derive(Debug, Clone, PartialEq)]
482pub enum BuildResult {
483    Ast(Expression),
484    Sql(String),
485}
486
487pub fn execute(request: &BuildRequest) -> Result<BuildResult> {
488    if request.version != BUILDER_PROTOCOL_VERSION {
489        return Err(Error::invalid_input(format!(
490            "unsupported builder protocol version {}; expected {}",
491            request.version, BUILDER_PROTOCOL_VERSION
492        )));
493    }
494    let expression = evaluate(&request.plan, &request.read_dialect)?;
495    match &request.output {
496        BuilderOutput::Ast => Ok(BuildResult::Ast(expression)),
497        BuilderOutput::Sql { dialect } => {
498            let dialect = resolve_dialect(dialect)?;
499            dialect
500                .generate_with_overrides(&expression, |config| {
501                    config.not_in_style = NotInStyle::Infix;
502                })
503                .map(BuildResult::Sql)
504        }
505    }
506}
507
508pub fn evaluate(plan: &BuilderPlan, read_dialect: &str) -> Result<Expression> {
509    Evaluator::new(read_dialect)?.evaluate_plan(plan)
510}
511
512struct Evaluator {
513    dialect: Dialect,
514}
515
516impl Evaluator {
517    fn new(name: &str) -> Result<Self> {
518        Ok(Self {
519            dialect: resolve_dialect(name)?,
520        })
521    }
522
523    fn evaluate_plan(&self, plan: &BuilderPlan) -> Result<Expression> {
524        let mut expression = if matches!(
525            plan.operations.first(),
526            Some(
527                BuildOperation::Union { .. }
528                    | BuildOperation::Intersect { .. }
529                    | BuildOperation::Except { .. }
530            )
531        ) {
532            self.evaluate_query(&plan.base)?
533        } else {
534            self.evaluate_node(&plan.base)?
535        };
536        for operation in &plan.operations {
537            if matches!(
538                operation,
539                BuildOperation::Union { .. }
540                    | BuildOperation::Intersect { .. }
541                    | BuildOperation::Except { .. }
542            ) && !engine::is_query(&expression)
543            {
544                return Err(Error::invalid_input(
545                    "set operations require query expressions",
546                ));
547            }
548            expression = self.apply(expression, operation)?;
549        }
550        Ok(expression)
551    }
552
553    fn evaluate_node(&self, node: &BuildNode) -> Result<Expression> {
554        match node {
555            BuildNode::Plan { plan } => self.evaluate_plan(plan),
556            BuildNode::Ast { expression } => Ok((**expression).clone()),
557            BuildNode::Sql { sql } => self.parse_expression(sql),
558            BuildNode::Literal { value } => Ok(match value {
559                BuilderValue::Null => builder::null().into_inner(),
560                BuilderValue::Bool(value) => builder::boolean(*value).into_inner(),
561                BuilderValue::Integer(value) => builder::lit(*value).into_inner(),
562                BuilderValue::Float(value) => builder::lit(*value).into_inner(),
563                BuilderValue::String(value) => builder::lit(value.as_str()).into_inner(),
564            }),
565            BuildNode::Column { name } => Ok(builder::col(name).into_inner()),
566            BuildNode::Table { name } => Ok(builder::table(name).into_inner()),
567            BuildNode::Star => Ok(builder::star().into_inner()),
568            BuildNode::Function { name, args } => {
569                let args = args
570                    .iter()
571                    .map(|arg| self.evaluate_node(arg).map(Expr))
572                    .collect::<Result<Vec<_>>>()?;
573                Ok(builder::func(name, args).into_inner())
574            }
575            BuildNode::Builtin { function, args } => {
576                let args = args
577                    .iter()
578                    .map(|arg| self.evaluate_node(arg).map(Expr))
579                    .collect::<Result<Vec<_>>>()?;
580                self.builtin(*function, args)
581            }
582            BuildNode::Extract { field, expression } => {
583                Ok(builder::extract_(field, Expr(self.evaluate_node(expression)?)).into_inner())
584            }
585            BuildNode::Binary { op, left, right } => Ok(engine::binary(
586                (*op).into(),
587                self.evaluate_node(left)?,
588                self.evaluate_node(right)?,
589            )),
590            BuildNode::Unary { op, expression } => {
591                Ok(engine::unary((*op).into(), self.evaluate_node(expression)?))
592            }
593            BuildNode::Alias { expression, alias } => {
594                Ok(builder::alias(Expr(self.evaluate_node(expression)?), alias).into_inner())
595            }
596            BuildNode::Cast { expression, to } => {
597                Ok(builder::cast(Expr(self.evaluate_node(expression)?), to).into_inner())
598            }
599            BuildNode::Between {
600                expression,
601                low,
602                high,
603            } => Ok(Expr(self.evaluate_node(expression)?)
604                .between(
605                    Expr(self.evaluate_node(low)?),
606                    Expr(self.evaluate_node(high)?),
607                )
608                .into_inner()),
609            BuildNode::InList {
610                expression,
611                values,
612                negated,
613            } => {
614                let values = values
615                    .iter()
616                    .map(|v| self.evaluate_node(v).map(Expr))
617                    .collect::<Result<Vec<_>>>()?;
618                let expr = Expr(self.evaluate_node(expression)?);
619                Ok(if *negated {
620                    expr.not_in(values)
621                } else {
622                    expr.in_list(values)
623                }
624                .into_inner())
625            }
626            BuildNode::Ordered { expression, desc } => {
627                let expr = Expr(self.evaluate_node(expression)?);
628                Ok(if *desc { expr.desc() } else { expr.asc() }.into_inner())
629            }
630            BuildNode::Select { expressions } => {
631                let expressions = self.evaluate_expression_list(expressions)?;
632                Ok(Expression::Select(Box::new(Select {
633                    expressions,
634                    ..Select::new()
635                })))
636            }
637            BuildNode::Case { operand } => Ok(engine::case(
638                operand
639                    .as_deref()
640                    .map(|value| self.evaluate_node(value))
641                    .transpose()?,
642            )),
643            BuildNode::Update {
644                table,
645                assignments,
646                where_clause,
647                from,
648            } => {
649                let mut expression = builder::update(table).build();
650                engine::append_update_assignments(
651                    &mut expression,
652                    self.evaluate_assignments(assignments)?,
653                )?;
654                if let Some(where_clause) = where_clause {
655                    engine::apply_where(&mut expression, self.evaluate_node(where_clause)?, false)?;
656                }
657                if let Some(from) = from {
658                    engine::set_from(&mut expression, self.parse_from(from)?)?;
659                }
660                Ok(expression)
661            }
662            BuildNode::Delete {
663                table,
664                where_clause,
665            } => {
666                let mut expression = builder::delete(table).build();
667                if let Some(where_clause) = where_clause {
668                    engine::apply_where(&mut expression, self.evaluate_node(where_clause)?, false)?;
669                }
670                Ok(expression)
671            }
672            BuildNode::Insert {
673                into,
674                expression,
675                columns,
676            } => {
677                let mut result = builder::insert_into(into).columns(columns).build();
678                if let Some(expression) = expression {
679                    let source = match expression.as_ref() {
680                        BuildNode::Sql { sql } => self.parse_statement(sql)?,
681                        node => self.evaluate_node(node)?,
682                    };
683                    match source {
684                        Expression::Values(values) => engine::apply_insert_values(
685                            &mut result,
686                            values
687                                .expressions
688                                .into_iter()
689                                .map(|tuple| tuple.expressions)
690                                .collect(),
691                            false,
692                        )?,
693                        query => engine::set_insert_query(&mut result, query)?,
694                    }
695                }
696                Ok(result)
697            }
698            BuildNode::Merge { target } => Ok(engine::merge(builder::table(target).into_inner())),
699        }
700    }
701
702    fn apply(&self, mut expression: Expression, operation: &BuildOperation) -> Result<Expression> {
703        match operation {
704            BuildOperation::Select {
705                expressions,
706                append,
707            } => {
708                let values = self.evaluate_expression_list(expressions)?;
709                engine::append_select(&mut expression, values, *append)?;
710            }
711            BuildOperation::From { source } => {
712                let values = self.evaluate_source(source)?;
713                engine::set_from(&mut expression, values)?;
714            }
715            BuildOperation::Join {
716                source,
717                on,
718                join_type,
719            } => {
720                let source = self
721                    .evaluate_source(source)?
722                    .into_iter()
723                    .next()
724                    .ok_or_else(|| Error::invalid_input("join source is empty"))?;
725                let on = on
726                    .as_ref()
727                    .map(|value| self.evaluate_node(value))
728                    .transpose()?;
729                let kind = match join_type {
730                    JoinType::Inner => JoinKind::Inner,
731                    JoinType::Left => JoinKind::Left,
732                    JoinType::Right => JoinKind::Right,
733                    JoinType::Full => JoinKind::Full,
734                    JoinType::Cross => JoinKind::Cross,
735                };
736                engine::append_join(
737                    &mut expression,
738                    Join {
739                        this: source,
740                        on,
741                        using: Vec::new(),
742                        kind,
743                        use_inner_keyword: false,
744                        use_outer_keyword: false,
745                        deferred_condition: false,
746                        join_hint: None,
747                        match_condition: None,
748                        pivots: Vec::new(),
749                        comments: Vec::new(),
750                        nesting_group: 0,
751                        directed: false,
752                    },
753                )?;
754            }
755            BuildOperation::Where {
756                expressions,
757                append,
758            } => {
759                let condition = self.combine_conditions(expressions)?;
760                engine::apply_where(&mut expression, condition, *append)?;
761            }
762            BuildOperation::GroupBy {
763                expressions,
764                append,
765            } => {
766                let values = self.evaluate_expression_list(expressions)?;
767                engine::apply_group_by(&mut expression, values, *append)?;
768            }
769            BuildOperation::Having {
770                expressions,
771                append,
772            } => {
773                let condition = self.combine_conditions(expressions)?;
774                engine::apply_having(&mut expression, condition, *append)?;
775            }
776            BuildOperation::OrderBy {
777                expressions,
778                append,
779            } => {
780                let values = self.evaluate_ordered_list(expressions)?;
781                engine::apply_order_by(&mut expression, values, *append)?;
782            }
783            BuildOperation::SortBy {
784                expressions,
785                append,
786            } => {
787                let values = self.evaluate_ordered_list(expressions)?;
788                engine::apply_sort_by(&mut expression, values, *append)?;
789            }
790            BuildOperation::Limit { expression: value } => {
791                engine::apply_limit(&mut expression, self.evaluate_node(value)?)?
792            }
793            BuildOperation::Offset { expression: value } => {
794                engine::apply_offset(&mut expression, self.evaluate_node(value)?)?
795            }
796            BuildOperation::Distinct { enabled } => {
797                engine::apply_distinct(&mut expression, *enabled)?
798            }
799            BuildOperation::Qualify {
800                expressions,
801                append,
802            } => {
803                let condition = self.combine_conditions(expressions)?;
804                engine::apply_qualify(&mut expression, condition, *append)?;
805            }
806            BuildOperation::LateralView {
807                expression: value,
808                table_alias,
809                column_aliases,
810                outer,
811            } => {
812                engine::append_lateral_view(
813                    &mut expression,
814                    self.evaluate_node(value)?,
815                    table_alias.as_deref().map(Identifier::new),
816                    column_aliases
817                        .iter()
818                        .map(|alias| Identifier::new(alias))
819                        .collect(),
820                    *outer,
821                )?;
822            }
823            BuildOperation::Window {
824                name,
825                partition_by,
826                order_by,
827            } => {
828                engine::append_window(
829                    &mut expression,
830                    Identifier::new(name),
831                    self.evaluate_expression_list(partition_by)?,
832                    self.evaluate_ordered_list(order_by)?,
833                )?;
834            }
835            BuildOperation::Lock { lock_type } => {
836                engine::append_lock(
837                    &mut expression,
838                    match lock_type {
839                        LockType::Update => engine::LockKind::Update,
840                        LockType::Share => engine::LockKind::Share,
841                    },
842                )?;
843            }
844            BuildOperation::Hint { text } => {
845                engine::append_hint(&mut expression, text.clone())?;
846            }
847            BuildOperation::Ctas {
848                table,
849                replace,
850                temporary,
851            } => {
852                let table = match builder::table(table).into_inner() {
853                    Expression::Table(table) => *table,
854                    _ => unreachable!("builder::table returns a table"),
855                };
856                expression = engine::create_table_as(expression, table, *replace, *temporary)?;
857            }
858            BuildOperation::Subquery { alias } => {
859                expression =
860                    engine::subquery(expression, alias.as_ref().map(Identifier::new), false)?;
861            }
862            BuildOperation::Union { other, distinct } => {
863                expression = engine::set_operation(
864                    engine::SetKind::Union,
865                    expression,
866                    self.evaluate_query(other)?,
867                    *distinct,
868                )?
869            }
870            BuildOperation::Intersect { other, distinct } => {
871                expression = engine::set_operation(
872                    engine::SetKind::Intersect,
873                    expression,
874                    self.evaluate_query(other)?,
875                    *distinct,
876                )?
877            }
878            BuildOperation::Except { other, distinct } => {
879                expression = engine::set_operation(
880                    engine::SetKind::Except,
881                    expression,
882                    self.evaluate_query(other)?,
883                    *distinct,
884                )?
885            }
886            BuildOperation::When { condition, result } => {
887                let condition = self.evaluate_node(condition)?;
888                let result = self.evaluate_node(result)?;
889                engine::append_case_when(&mut expression, condition, result)?;
890            }
891            BuildOperation::Else { result } => {
892                let result = self.evaluate_node(result)?;
893                engine::set_case_else(&mut expression, result)?;
894            }
895            BuildOperation::Set { assignments } => {
896                let values = self.evaluate_assignments(assignments)?;
897                engine::append_update_assignments(&mut expression, values)?;
898            }
899            BuildOperation::InsertColumns { columns } => engine::set_insert_columns(
900                &mut expression,
901                columns.iter().map(Identifier::new).collect(),
902            )?,
903            BuildOperation::Values { rows, append } => {
904                let evaluated = rows
905                    .iter()
906                    .map(|row| self.evaluate_expression_list(row))
907                    .collect::<Result<Vec<_>>>()?;
908                engine::apply_insert_values(&mut expression, evaluated, *append)?;
909            }
910            BuildOperation::Query { query } => {
911                let query = self.evaluate_query(query)?;
912                engine::set_insert_query(&mut expression, query)?;
913            }
914            BuildOperation::MergeUsing { source, on } => {
915                engine::set_merge_using(
916                    &mut expression,
917                    self.evaluate_source(source)?
918                        .into_iter()
919                        .next()
920                        .ok_or_else(|| Error::invalid_input("merge source is empty"))?,
921                    self.evaluate_node(on)?,
922                )?;
923            }
924            BuildOperation::WhenMatchedUpdate {
925                assignments,
926                condition,
927            } => {
928                engine::append_merge_update(
929                    &mut expression,
930                    self.evaluate_assignments(assignments)?,
931                    condition
932                        .as_ref()
933                        .map(|value| self.evaluate_node(value))
934                        .transpose()?,
935                )?;
936            }
937            BuildOperation::WhenMatchedDelete { condition } => {
938                engine::append_merge_delete(
939                    &mut expression,
940                    condition
941                        .as_ref()
942                        .map(|value| self.evaluate_node(value))
943                        .transpose()?,
944                )?;
945            }
946            BuildOperation::WhenNotMatchedInsert {
947                columns,
948                values,
949                condition,
950            } => {
951                engine::append_merge_insert(
952                    &mut expression,
953                    columns.iter().map(Identifier::new).collect(),
954                    self.evaluate_expression_list(values)?,
955                    condition
956                        .as_ref()
957                        .map(|value| self.evaluate_node(value))
958                        .transpose()?,
959                )?;
960            }
961        }
962        Ok(expression)
963    }
964
965    fn builtin(&self, function: BuiltinFunction, mut args: Vec<Expr>) -> Result<Expression> {
966        let name = format!("{function:?}");
967        let exact = |args: &mut Vec<Expr>, count: usize| -> Result<Vec<Expr>> {
968            if args.len() != count {
969                return Err(Error::invalid_input(format!(
970                    "{name} expects {count} argument(s), got {}",
971                    args.len()
972                )));
973            }
974            Ok(std::mem::take(args))
975        };
976        let one = |args: &mut Vec<Expr>| -> Result<Expr> {
977            Ok(exact(args, 1)?.pop().expect("one validated argument"))
978        };
979
980        let result = match function {
981            BuiltinFunction::Count => builder::count(one(&mut args)?),
982            BuiltinFunction::CountStar => {
983                exact(&mut args, 0)?;
984                builder::count_star()
985            }
986            BuiltinFunction::CountDistinct => builder::count_distinct(one(&mut args)?),
987            BuiltinFunction::Sum => builder::sum(one(&mut args)?),
988            BuiltinFunction::Avg => builder::avg(one(&mut args)?),
989            BuiltinFunction::Min => builder::min_(one(&mut args)?),
990            BuiltinFunction::Max => builder::max_(one(&mut args)?),
991            BuiltinFunction::ApproxDistinct => builder::approx_distinct(one(&mut args)?),
992            BuiltinFunction::Upper => builder::upper(one(&mut args)?),
993            BuiltinFunction::Lower => builder::lower(one(&mut args)?),
994            BuiltinFunction::Length => builder::length(one(&mut args)?),
995            BuiltinFunction::Trim => builder::trim(one(&mut args)?),
996            BuiltinFunction::Ltrim => builder::ltrim(one(&mut args)?),
997            BuiltinFunction::Rtrim => builder::rtrim(one(&mut args)?),
998            BuiltinFunction::Reverse => builder::reverse(one(&mut args)?),
999            BuiltinFunction::Initcap => builder::initcap(one(&mut args)?),
1000            BuiltinFunction::Substring => match args.len() {
1001                2 => {
1002                    let mut args = exact(&mut args, 2)?;
1003                    let start = args.pop().expect("start argument");
1004                    builder::substring(args.pop().expect("value argument"), start, None)
1005                }
1006                3 => {
1007                    let mut args = exact(&mut args, 3)?;
1008                    let length = args.pop().expect("length argument");
1009                    let start = args.pop().expect("start argument");
1010                    builder::substring(args.pop().expect("value argument"), start, Some(length))
1011                }
1012                count => {
1013                    return Err(Error::invalid_input(format!(
1014                        "Substring expects 2 or 3 arguments, got {count}"
1015                    )))
1016                }
1017            },
1018            BuiltinFunction::Replace => {
1019                let mut args = exact(&mut args, 3)?;
1020                let new = args.pop().expect("new argument");
1021                let old = args.pop().expect("old argument");
1022                builder::replace_(args.pop().expect("value argument"), old, new)
1023            }
1024            BuiltinFunction::ConcatWs => {
1025                if args.is_empty() {
1026                    return Err(Error::invalid_input(
1027                        "ConcatWs expects at least one argument",
1028                    ));
1029                }
1030                let values = args.split_off(1);
1031                builder::concat_ws(args.pop().expect("separator argument"), values)
1032            }
1033            BuiltinFunction::Coalesce => builder::coalesce(args),
1034            BuiltinFunction::NullIf => {
1035                let mut args = exact(&mut args, 2)?;
1036                let right = args.pop().expect("right argument");
1037                builder::null_if(args.pop().expect("left argument"), right)
1038            }
1039            BuiltinFunction::IfNull => {
1040                let mut args = exact(&mut args, 2)?;
1041                let fallback = args.pop().expect("fallback argument");
1042                builder::if_null(args.pop().expect("value argument"), fallback)
1043            }
1044            BuiltinFunction::Abs => builder::abs(one(&mut args)?),
1045            BuiltinFunction::Round => match args.len() {
1046                1 => builder::round(one(&mut args)?, None),
1047                2 => {
1048                    let mut args = exact(&mut args, 2)?;
1049                    let decimals = args.pop().expect("decimals argument");
1050                    builder::round(args.pop().expect("value argument"), Some(decimals))
1051                }
1052                count => {
1053                    return Err(Error::invalid_input(format!(
1054                        "Round expects 1 or 2 arguments, got {count}"
1055                    )))
1056                }
1057            },
1058            BuiltinFunction::Floor => builder::floor(one(&mut args)?),
1059            BuiltinFunction::Ceil => builder::ceil(one(&mut args)?),
1060            BuiltinFunction::Power => {
1061                let mut args = exact(&mut args, 2)?;
1062                let exponent = args.pop().expect("exponent argument");
1063                builder::power(args.pop().expect("base argument"), exponent)
1064            }
1065            BuiltinFunction::Sqrt => builder::sqrt(one(&mut args)?),
1066            BuiltinFunction::Ln => builder::ln(one(&mut args)?),
1067            BuiltinFunction::Exp => builder::exp_(one(&mut args)?),
1068            BuiltinFunction::Sign => builder::sign(one(&mut args)?),
1069            BuiltinFunction::Greatest => builder::greatest(args),
1070            BuiltinFunction::Least => builder::least(args),
1071            BuiltinFunction::CurrentDate => {
1072                exact(&mut args, 0)?;
1073                builder::current_date_()
1074            }
1075            BuiltinFunction::CurrentTime => {
1076                exact(&mut args, 0)?;
1077                builder::current_time_()
1078            }
1079            BuiltinFunction::CurrentTimestamp => {
1080                exact(&mut args, 0)?;
1081                builder::current_timestamp_()
1082            }
1083            BuiltinFunction::RowNumber => {
1084                exact(&mut args, 0)?;
1085                builder::row_number()
1086            }
1087            BuiltinFunction::Rank => {
1088                exact(&mut args, 0)?;
1089                builder::rank_()
1090            }
1091            BuiltinFunction::DenseRank => {
1092                exact(&mut args, 0)?;
1093                builder::dense_rank()
1094            }
1095        };
1096        Ok(result.into_inner())
1097    }
1098
1099    fn parse_expression(&self, sql: &str) -> Result<Expression> {
1100        self.parse_expression_list(sql)?
1101            .into_iter()
1102            .next()
1103            .ok_or_else(|| Error::invalid_input("SQL expression is empty"))
1104    }
1105
1106    fn parse_statement(&self, sql: &str) -> Result<Expression> {
1107        let mut statements = self.dialect.parse(sql)?;
1108        if statements.len() != 1 {
1109            return Err(Error::invalid_input(
1110                "builder SQL must contain exactly one statement",
1111            ));
1112        }
1113        Ok(statements.remove(0))
1114    }
1115
1116    fn evaluate_query(&self, node: &BuildNode) -> Result<Expression> {
1117        let expression = match node {
1118            BuildNode::Sql { sql } => self.parse_statement(sql)?,
1119            _ => self.evaluate_node(node)?,
1120        };
1121        if engine::is_query(&expression) {
1122            Ok(expression)
1123        } else {
1124            Err(Error::invalid_input(
1125                "set operations require query expressions",
1126            ))
1127        }
1128    }
1129
1130    fn parse_expression_list(&self, sql: &str) -> Result<Vec<Expression>> {
1131        let statements = self.dialect.parse(&format!("SELECT {sql}"))?;
1132        match statements.into_iter().next() {
1133            Some(Expression::Select(select)) => Ok(select.expressions),
1134            _ => Err(Error::invalid_input(
1135                "failed to parse SQL expression fragment",
1136            )),
1137        }
1138    }
1139
1140    fn parse_from(&self, sql: &str) -> Result<Vec<Expression>> {
1141        let statements = self.dialect.parse(&format!("SELECT * FROM {sql}"))?;
1142        match statements.into_iter().next() {
1143            Some(Expression::Select(select)) => select
1144                .from
1145                .map(|from| from.expressions)
1146                .ok_or_else(|| Error::invalid_input("failed to parse FROM fragment")),
1147            _ => Err(Error::invalid_input("failed to parse FROM fragment")),
1148        }
1149    }
1150
1151    fn parse_ordered(&self, sql: &str) -> Result<Vec<Ordered>> {
1152        let statements = self.dialect.parse(&format!(
1153            "SELECT * FROM __polyglot_builder__ ORDER BY {sql}"
1154        ))?;
1155        match statements.into_iter().next() {
1156            Some(Expression::Select(select)) => select
1157                .order_by
1158                .map(|order| order.expressions)
1159                .ok_or_else(|| Error::invalid_input("failed to parse ORDER BY fragment")),
1160            _ => Err(Error::invalid_input("failed to parse ORDER BY fragment")),
1161        }
1162    }
1163
1164    fn evaluate_expression_list(&self, values: &[BuildNode]) -> Result<Vec<Expression>> {
1165        let mut result = Vec::new();
1166        for value in values {
1167            match value {
1168                BuildNode::Sql { sql } => result.extend(self.parse_expression_list(sql)?),
1169                _ => result.push(self.evaluate_node(value)?),
1170            }
1171        }
1172        Ok(result)
1173    }
1174
1175    fn evaluate_ordered_list(&self, values: &[BuildNode]) -> Result<Vec<Ordered>> {
1176        let mut result = Vec::new();
1177        for value in values {
1178            match value {
1179                BuildNode::Sql { sql } => result.extend(self.parse_ordered(sql)?),
1180                _ => match self.evaluate_node(value)? {
1181                    Expression::Ordered(ordered) => result.push(*ordered),
1182                    expression => result.push(engine::ordered(expression)),
1183                },
1184            }
1185        }
1186        Ok(result)
1187    }
1188
1189    fn evaluate_source(&self, value: &BuildNode) -> Result<Vec<Expression>> {
1190        match value {
1191            BuildNode::Sql { sql } => self.parse_from(sql),
1192            BuildNode::Table { name } => Ok(vec![builder::table(name).into_inner()]),
1193            _ => Ok(vec![self.evaluate_node(value)?]),
1194        }
1195    }
1196
1197    fn combine_conditions(&self, values: &[BuildNode]) -> Result<Expression> {
1198        let mut values = values.iter();
1199        let first = values
1200            .next()
1201            .ok_or_else(|| Error::invalid_input("at least one condition is required"))?;
1202        let mut result = self.evaluate_node(first)?;
1203        for value in values {
1204            result = engine::binary(engine::BinaryKind::And, result, self.evaluate_node(value)?);
1205        }
1206        Ok(result)
1207    }
1208
1209    fn evaluate_assignments(
1210        &self,
1211        assignments: &[BuilderAssignment],
1212    ) -> Result<Vec<(Identifier, Expression)>> {
1213        assignments
1214            .iter()
1215            .map(|assignment| {
1216                Ok((
1217                    Identifier::new(&assignment.column),
1218                    self.evaluate_node(&assignment.value)?,
1219                ))
1220            })
1221            .collect()
1222    }
1223}
1224
1225fn resolve_dialect(name: &str) -> Result<Dialect> {
1226    Dialect::get_by_name(if name.is_empty() { "generic" } else { name })
1227        .ok_or_else(|| Error::invalid_input(format!("unknown dialect: {name}")))
1228}
1229
1230#[cfg(test)]
1231mod tests {
1232    use super::*;
1233
1234    #[test]
1235    #[cfg(feature = "bindings")]
1236    fn export_typescript_types_builder_protocol() {
1237        BuildRequest::export_all(&ts_rs::Config::default())
1238            .expect("failed to export builder protocol types");
1239    }
1240
1241    fn sql(plan: BuilderPlan) -> String {
1242        match execute(&BuildRequest {
1243            version: 1,
1244            read_dialect: "generic".into(),
1245            plan,
1246            output: BuilderOutput::Sql {
1247                dialect: "generic".into(),
1248            },
1249        })
1250        .unwrap()
1251        {
1252            BuildResult::Sql(sql) => sql,
1253            _ => unreachable!(),
1254        }
1255    }
1256
1257    #[test]
1258    fn builds_sqlglot_style_select() {
1259        let query = BuilderPlan::new(BuildNode::Select {
1260            expressions: vec![BuildNode::Sql {
1261                sql: "x, COUNT(*) AS n".into(),
1262            }],
1263        })
1264        .apply(BuildOperation::From {
1265            source: BuildNode::Sql {
1266                sql: "events".into(),
1267            },
1268        })
1269        .apply(BuildOperation::Where {
1270            expressions: vec![BuildNode::Sql {
1271                sql: "active = TRUE".into(),
1272            }],
1273            append: true,
1274        });
1275        assert_eq!(
1276            sql(query),
1277            "SELECT x, COUNT(*) AS n FROM events WHERE active = TRUE"
1278        );
1279    }
1280
1281    #[test]
1282    fn appends_conditions_without_mutating_the_original_plan() {
1283        let base = BuilderPlan::new(BuildNode::Select {
1284            expressions: vec![BuildNode::Sql { sql: "x".into() }],
1285        })
1286        .apply(BuildOperation::Where {
1287            expressions: vec![BuildNode::Sql {
1288                sql: "x > 0".into(),
1289            }],
1290            append: true,
1291        });
1292        let appended = base.clone().apply(BuildOperation::Where {
1293            expressions: vec![BuildNode::Sql {
1294                sql: "x < 9".into(),
1295            }],
1296            append: true,
1297        });
1298        assert_eq!(sql(base), "SELECT x WHERE x > 0");
1299        assert_eq!(sql(appended), "SELECT x WHERE x > 0 AND x < 9");
1300    }
1301
1302    #[test]
1303    fn distinguishes_sql_and_literal_strings() {
1304        let comparison = BuildNode::Binary {
1305            op: BinaryOperator::Eq,
1306            left: Box::new(BuildNode::Column {
1307                name: "status".into(),
1308            }),
1309            right: Box::new(BuildNode::Literal {
1310                value: BuilderValue::String("active".into()),
1311            }),
1312        };
1313        assert_eq!(sql(BuilderPlan::new(comparison)), "status = 'active'");
1314
1315        let not_in = BuildNode::InList {
1316            expression: Box::new(BuildNode::Column { name: "x".into() }),
1317            values: vec![
1318                BuildNode::Literal {
1319                    value: BuilderValue::Integer(1),
1320                },
1321                BuildNode::Literal {
1322                    value: BuilderValue::Integer(2),
1323                },
1324            ],
1325            negated: true,
1326        };
1327        assert_eq!(sql(BuilderPlan::new(not_in)), "x NOT IN (1, 2)");
1328    }
1329
1330    #[test]
1331    fn parses_full_query_strings_in_query_contexts() {
1332        let union = BuilderPlan::new(BuildNode::Sql {
1333            sql: "SELECT id".into(),
1334        })
1335        .apply(BuildOperation::Union {
1336            other: BuildNode::Sql {
1337                sql: "SELECT id FROM archive".into(),
1338            },
1339            distinct: false,
1340        });
1341        assert_eq!(sql(union), "SELECT id UNION ALL SELECT id FROM archive");
1342
1343        let insert = BuildNode::Insert {
1344            into: "users".into(),
1345            expression: Some(Box::new(BuildNode::Sql {
1346                sql: "SELECT id FROM staging".into(),
1347            })),
1348            columns: vec!["id".into()],
1349        };
1350        assert_eq!(
1351            sql(BuilderPlan::new(insert)),
1352            "INSERT INTO users (id) SELECT id FROM staging"
1353        );
1354    }
1355
1356    #[test]
1357    fn supports_typed_builtins_and_advanced_query_operations() {
1358        let plan = BuilderPlan::new(BuildNode::Select {
1359            expressions: vec![
1360                BuildNode::Column {
1361                    name: "department".into(),
1362                },
1363                BuildNode::Alias {
1364                    expression: Box::new(BuildNode::Builtin {
1365                        function: BuiltinFunction::Count,
1366                        args: vec![BuildNode::Column { name: "id".into() }],
1367                    }),
1368                    alias: "employees".into(),
1369                },
1370            ],
1371        })
1372        .apply(BuildOperation::From {
1373            source: BuildNode::Table {
1374                name: "employees".into(),
1375            },
1376        })
1377        .apply(BuildOperation::Join {
1378            source: BuildNode::Table {
1379                name: "departments".into(),
1380            },
1381            on: Some(BuildNode::Sql {
1382                sql: "employees.department_id = departments.id".into(),
1383            }),
1384            join_type: JoinType::Full,
1385        })
1386        .apply(BuildOperation::Window {
1387            name: "w".into(),
1388            partition_by: vec![BuildNode::Column {
1389                name: "department".into(),
1390            }],
1391            order_by: vec![BuildNode::Ordered {
1392                expression: Box::new(BuildNode::Column {
1393                    name: "salary".into(),
1394                }),
1395                desc: true,
1396            }],
1397        })
1398        .apply(BuildOperation::Lock {
1399            lock_type: LockType::Share,
1400        });
1401
1402        assert_eq!(
1403            sql(plan),
1404            "SELECT department, COUNT(id) AS employees FROM employees FULL JOIN departments ON employees.department_id = departments.id WINDOW w AS (PARTITION BY department ORDER BY salary DESC) FOR SHARE"
1405        );
1406    }
1407
1408    #[test]
1409    fn repeated_clauses_append_by_default_and_can_replace() {
1410        let base = BuilderPlan::new(BuildNode::Select {
1411            expressions: vec![BuildNode::Column { name: "x".into() }],
1412        })
1413        .apply(BuildOperation::Where {
1414            expressions: vec![BuildNode::Sql {
1415                sql: "x > 0".into(),
1416            }],
1417            append: true,
1418        });
1419        let appended = base.clone().apply(BuildOperation::Where {
1420            expressions: vec![BuildNode::Sql {
1421                sql: "x < 10".into(),
1422            }],
1423            append: true,
1424        });
1425        let replaced = base.apply(BuildOperation::Where {
1426            expressions: vec![BuildNode::Sql {
1427                sql: "x = 5".into(),
1428            }],
1429            append: false,
1430        });
1431
1432        assert_eq!(sql(appended), "SELECT x WHERE x > 0 AND x < 10");
1433        assert_eq!(sql(replaced), "SELECT x WHERE x = 5");
1434    }
1435
1436    #[test]
1437    fn supports_conditional_merge_actions() {
1438        let plan = BuilderPlan::new(BuildNode::Merge {
1439            target: "target".into(),
1440        })
1441        .apply(BuildOperation::MergeUsing {
1442            source: BuildNode::Table {
1443                name: "source".into(),
1444            },
1445            on: BuildNode::Sql {
1446                sql: "target.id = source.id".into(),
1447            },
1448        })
1449        .apply(BuildOperation::WhenMatchedUpdate {
1450            assignments: vec![BuilderAssignment {
1451                column: "name".into(),
1452                value: BuildNode::Column {
1453                    name: "source.name".into(),
1454                },
1455            }],
1456            condition: Some(BuildNode::Sql {
1457                sql: "source.active".into(),
1458            }),
1459        })
1460        .apply(BuildOperation::WhenMatchedDelete {
1461            condition: Some(BuildNode::Sql {
1462                sql: "source.deleted".into(),
1463            }),
1464        })
1465        .apply(BuildOperation::WhenNotMatchedInsert {
1466            columns: vec!["id".into()],
1467            values: vec![BuildNode::Column {
1468                name: "source.id".into(),
1469            }],
1470            condition: Some(BuildNode::Sql {
1471                sql: "source.active".into(),
1472            }),
1473        });
1474
1475        let sql = sql(plan);
1476        assert!(sql.contains("WHEN MATCHED AND source.active THEN UPDATE SET name = source.name"));
1477        assert!(sql.contains("WHEN MATCHED AND source.deleted THEN DELETE"));
1478        assert!(
1479            sql.contains("WHEN NOT MATCHED AND source.active THEN INSERT (id) VALUES (source.id)")
1480        );
1481    }
1482}