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