Skip to main content

datafusion_sql/unparser/
ast.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use core::fmt;
19use std::ops::ControlFlow;
20
21use sqlparser::ast::helpers::attached_token::AttachedToken;
22use sqlparser::ast::{
23    self, LimitClause, OrderByKind, SelectFlavor, visit_expressions_mut,
24};
25
26#[derive(Clone)]
27pub struct QueryBuilder {
28    with: Option<ast::With>,
29    body: Option<Box<ast::SetExpr>>,
30    order_by_kind: Option<OrderByKind>,
31    limit: Option<ast::Expr>,
32    limit_by: Vec<ast::Expr>,
33    offset: Option<ast::Offset>,
34    fetch: Option<ast::Fetch>,
35    locks: Vec<ast::LockClause>,
36    for_clause: Option<ast::ForClause>,
37    // If true, we need to unparse LogicalPlan::Union as a SQL `UNION` rather than a `UNION ALL`.
38    distinct_union: bool,
39}
40
41impl QueryBuilder {
42    pub fn with(&mut self, value: Option<ast::With>) -> &mut Self {
43        self.with = value;
44        self
45    }
46    pub fn body(&mut self, value: Box<ast::SetExpr>) -> &mut Self {
47        self.body = Some(value);
48        self
49    }
50    pub fn take_body(&mut self) -> Option<Box<ast::SetExpr>> {
51        self.body.take()
52    }
53    pub fn order_by(&mut self, value: OrderByKind) -> &mut Self {
54        self.order_by_kind = Some(value);
55        self
56    }
57    pub fn limit(&mut self, value: Option<ast::Expr>) -> &mut Self {
58        self.limit = value;
59        self
60    }
61    pub fn limit_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
62        self.limit_by = value;
63        self
64    }
65    pub fn offset(&mut self, value: Option<ast::Offset>) -> &mut Self {
66        self.offset = value;
67        self
68    }
69    pub fn fetch(&mut self, value: Option<ast::Fetch>) -> &mut Self {
70        self.fetch = value;
71        self
72    }
73    pub fn locks(&mut self, value: Vec<ast::LockClause>) -> &mut Self {
74        self.locks = value;
75        self
76    }
77    pub fn for_clause(&mut self, value: Option<ast::ForClause>) -> &mut Self {
78        self.for_clause = value;
79        self
80    }
81    pub fn distinct_union(&mut self) -> &mut Self {
82        self.distinct_union = true;
83        self
84    }
85    pub fn is_distinct_union(&self) -> bool {
86        self.distinct_union
87    }
88    pub fn build(&self) -> Result<ast::Query, BuilderError> {
89        let order_by = self
90            .order_by_kind
91            .as_ref()
92            .map(|order_by_kind| ast::OrderBy {
93                kind: order_by_kind.clone(),
94                interpolate: None,
95            });
96
97        Ok(ast::Query {
98            with: self.with.clone(),
99            body: match self.body {
100                Some(ref value) => value.clone(),
101                None => return Err(Into::into(UninitializedFieldError::from("body"))),
102            },
103            order_by,
104            limit_clause: Some(LimitClause::LimitOffset {
105                limit: self.limit.clone(),
106                offset: self.offset.clone(),
107                limit_by: self.limit_by.clone(),
108            }),
109            fetch: self.fetch.clone(),
110            locks: self.locks.clone(),
111            for_clause: self.for_clause.clone(),
112            settings: None,
113            format_clause: None,
114            pipe_operators: vec![],
115        })
116    }
117    fn create_empty() -> Self {
118        Self {
119            with: Default::default(),
120            body: Default::default(),
121            order_by_kind: Default::default(),
122            limit: Default::default(),
123            limit_by: Default::default(),
124            offset: Default::default(),
125            fetch: Default::default(),
126            locks: Default::default(),
127            for_clause: Default::default(),
128            distinct_union: false,
129        }
130    }
131}
132impl Default for QueryBuilder {
133    fn default() -> Self {
134        Self::create_empty()
135    }
136}
137
138#[derive(Clone)]
139pub struct SelectBuilder {
140    distinct: Option<ast::Distinct>,
141    top: Option<ast::Top>,
142    /// Projection items for the SELECT clause.
143    ///
144    /// This field uses `Option` to distinguish between three distinct states:
145    /// - `None`: No projection has been set (not yet initialized)
146    /// - `Some(vec![])`: Empty projection explicitly set (generates `SELECT FROM ...` or `SELECT 1 FROM ...`)
147    /// - `Some(vec![SelectItem::Wildcard(...)])`: Wildcard projection (generates `SELECT * FROM ...`)
148    /// - `Some(vec![...])`: Non-empty projection with specific columns/expressions
149    ///
150    /// Use `projection()` to set this field and `already_projected()` to check if it has been set.
151    projection: Option<Vec<ast::SelectItem>>,
152    into: Option<ast::SelectInto>,
153    from: Vec<TableWithJoinsBuilder>,
154    lateral_views: Vec<ast::LateralView>,
155    selection: Option<ast::Expr>,
156    group_by: Option<ast::GroupByExpr>,
157    cluster_by: Vec<ast::Expr>,
158    distribute_by: Vec<ast::Expr>,
159    sort_by: Vec<ast::OrderByExpr>,
160    having: Option<ast::Expr>,
161    named_window: Vec<ast::NamedWindowDefinition>,
162    qualify: Option<ast::Expr>,
163    value_table_mode: Option<ast::ValueTableMode>,
164    flavor: Option<SelectFlavor>,
165    /// Counter for generating unique LATERAL FLATTEN aliases within this SELECT.
166    flatten_alias_counter: usize,
167    /// Table aliases that correspond to LATERAL FLATTEN relations.
168    /// Column references into these aliases must use `VALUE` as the column name.
169    flatten_table_aliases: Vec<String>,
170}
171
172/// Prefix used for auto-generated LATERAL FLATTEN table aliases.
173const FLATTEN_ALIAS_PREFIX: &str = "_unnest";
174
175impl SelectBuilder {
176    /// Generate a unique alias for a LATERAL FLATTEN relation
177    /// (`_unnest_1`, `_unnest_2`, …). Each call returns a fresh name.
178    pub fn next_flatten_alias(&mut self) -> String {
179        self.flatten_alias_counter += 1;
180        format!("{FLATTEN_ALIAS_PREFIX}_{}", self.flatten_alias_counter)
181    }
182
183    /// Register a table alias as pointing to a LATERAL FLATTEN relation.
184    pub fn add_flatten_table_alias(&mut self, alias: String) {
185        self.flatten_table_aliases.push(alias);
186    }
187
188    /// Returns true if no FLATTEN table aliases have been registered.
189    pub fn flatten_table_aliases_empty(&self) -> bool {
190        self.flatten_table_aliases.is_empty()
191    }
192
193    /// Returns true if the given table alias refers to a FLATTEN relation.
194    pub fn is_flatten_table_alias(&self, alias: &str) -> bool {
195        self.flatten_table_aliases.iter().any(|a| a == alias)
196    }
197
198    /// Returns the most recently generated flatten alias, or `None` if
199    /// `next_flatten_alias` has not been called yet.
200    pub fn current_flatten_alias(&self) -> Option<String> {
201        if self.flatten_alias_counter > 0 {
202            Some(format!(
203                "{FLATTEN_ALIAS_PREFIX}_{}",
204                self.flatten_alias_counter
205            ))
206        } else {
207            None
208        }
209    }
210
211    pub fn distinct(&mut self, value: Option<ast::Distinct>) -> &mut Self {
212        self.distinct = value;
213        self
214    }
215    pub fn top(&mut self, value: Option<ast::Top>) -> &mut Self {
216        self.top = value;
217        self
218    }
219    pub fn projection(&mut self, value: Vec<ast::SelectItem>) -> &mut Self {
220        self.projection = Some(value);
221        self
222    }
223    pub fn pop_projections(&mut self) -> Vec<ast::SelectItem> {
224        self.projection.take().unwrap_or_default()
225    }
226    /// Returns true if a projection has been explicitly set via `projection()`.
227    ///
228    /// This method is used to determine whether the SELECT clause has already been
229    /// defined, which helps avoid creating duplicate projection nodes during query
230    /// unparsing. It returns `true` for both empty and non-empty projections.
231    ///
232    /// # Returns
233    ///
234    /// - `true` if `projection()` has been called (regardless of whether it was empty or not)
235    /// - `false` if no projection has been set yet
236    ///
237    /// # Example
238    ///
239    /// ```ignore
240    /// let mut builder = SelectBuilder::default();
241    /// assert!(!builder.already_projected());
242    ///
243    /// builder.projection(vec![]);
244    /// assert!(builder.already_projected()); // true even for empty projection
245    ///
246    /// builder.projection(vec![SelectItem::Wildcard(...)]);
247    /// assert!(builder.already_projected()); // true for non-empty projection
248    /// ```
249    pub fn already_projected(&self) -> bool {
250        self.projection.is_some()
251    }
252    pub fn into(&mut self, value: Option<ast::SelectInto>) -> &mut Self {
253        self.into = value;
254        self
255    }
256    pub fn from(&mut self, value: Vec<TableWithJoinsBuilder>) -> &mut Self {
257        self.from = value;
258        self
259    }
260    pub fn push_from(&mut self, value: TableWithJoinsBuilder) -> &mut Self {
261        self.from.push(value);
262        self
263    }
264    pub fn pop_from(&mut self) -> Option<TableWithJoinsBuilder> {
265        self.from.pop()
266    }
267    pub fn has_selection(&self) -> bool {
268        self.selection.is_some()
269    }
270    pub fn lateral_views(&mut self, value: Vec<ast::LateralView>) -> &mut Self {
271        self.lateral_views = value;
272        self
273    }
274
275    /// Replaces the selection with a new value.
276    ///
277    /// This function is used to replace a specific expression within the selection.
278    /// Unlike the `selection` method which combines existing and new selections with AND,
279    /// this method searches for and replaces occurrences of a specific expression.
280    ///
281    /// This method is primarily used to modify LEFT MARK JOIN expressions.
282    /// When processing a LEFT MARK JOIN, we need to replace the placeholder expression
283    /// with the actual join condition in the selection clause.
284    ///
285    /// # Arguments
286    ///
287    /// * `existing_expr` - The expression to replace
288    /// * `value` - The new expression to set as the selection
289    pub fn replace_mark(
290        &mut self,
291        existing_expr: &ast::Expr,
292        value: &ast::Expr,
293    ) -> &mut Self {
294        if let Some(selection) = &mut self.selection {
295            let _ = visit_expressions_mut(selection, |expr| {
296                if expr == existing_expr {
297                    *expr = value.clone();
298                }
299                ControlFlow::<()>::Continue(())
300            });
301        }
302        self
303    }
304
305    pub fn selection(&mut self, value: Option<ast::Expr>) -> &mut Self {
306        // With filter pushdown optimization, the LogicalPlan can have filters defined as part of `TableScan` and `Filter` nodes.
307        // To avoid overwriting one of the filters, we combine the existing filter with the additional filter.
308        // Example:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
309        // |  Projection: customer.c_phone AS cntrycode, customer.c_acctbal                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
310        // |   Filter: CAST(customer.c_acctbal AS Decimal128(38, 6)) > (<subquery>)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
311        // |     Subquery:
312        // |     ..                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
313        // |     TableScan: customer, full_filters=[customer.c_mktsegment = Utf8("BUILDING")]
314        match (&self.selection, value) {
315            (Some(existing_selection), Some(new_selection)) => {
316                self.selection = Some(ast::Expr::BinaryOp {
317                    left: Box::new(existing_selection.clone()),
318                    op: ast::BinaryOperator::And,
319                    right: Box::new(new_selection),
320                });
321            }
322            (None, Some(new_selection)) => {
323                self.selection = Some(new_selection);
324            }
325            (_, None) => (),
326        }
327
328        self
329    }
330    pub fn group_by(&mut self, value: ast::GroupByExpr) -> &mut Self {
331        self.group_by = Some(value);
332        self
333    }
334    pub fn cluster_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
335        self.cluster_by = value;
336        self
337    }
338    pub fn distribute_by(&mut self, value: Vec<ast::Expr>) -> &mut Self {
339        self.distribute_by = value;
340        self
341    }
342    pub fn sort_by(&mut self, value: Vec<ast::OrderByExpr>) -> &mut Self {
343        self.sort_by = value;
344        self
345    }
346    pub fn having(&mut self, value: Option<ast::Expr>) -> &mut Self {
347        self.having = value;
348        self
349    }
350    pub fn named_window(&mut self, value: Vec<ast::NamedWindowDefinition>) -> &mut Self {
351        self.named_window = value;
352        self
353    }
354    pub fn qualify(&mut self, value: Option<ast::Expr>) -> &mut Self {
355        self.qualify = value;
356        self
357    }
358    pub fn value_table_mode(&mut self, value: Option<ast::ValueTableMode>) -> &mut Self {
359        self.value_table_mode = value;
360        self
361    }
362    pub fn build(&self) -> Result<ast::Select, BuilderError> {
363        Ok(ast::Select {
364            optimizer_hints: vec![],
365            distinct: self.distinct.clone(),
366            select_modifiers: None,
367            top_before_distinct: false,
368            top: self.top.clone(),
369            projection: self.projection.clone().unwrap_or_default(),
370            into: self.into.clone(),
371            from: self
372                .from
373                .iter()
374                .filter_map(|b| b.build().transpose())
375                .collect::<Result<Vec<_>, BuilderError>>()?,
376            lateral_views: self.lateral_views.clone(),
377            selection: self.selection.clone(),
378            group_by: match self.group_by {
379                Some(ref value) => value.clone(),
380                None => {
381                    return Err(Into::into(UninitializedFieldError::from("group_by")));
382                }
383            },
384            cluster_by: self.cluster_by.clone(),
385            distribute_by: self.distribute_by.clone(),
386            sort_by: self.sort_by.clone(),
387            having: self.having.clone(),
388            named_window: self.named_window.clone(),
389            qualify: self.qualify.clone(),
390            value_table_mode: self.value_table_mode,
391            connect_by: Vec::new(),
392            window_before_qualify: false,
393            prewhere: None,
394            select_token: AttachedToken::empty(),
395            flavor: match self.flavor {
396                Some(ref value) => *value,
397                None => return Err(Into::into(UninitializedFieldError::from("flavor"))),
398            },
399            exclude: None,
400        })
401    }
402    fn create_empty() -> Self {
403        Self {
404            distinct: Default::default(),
405            top: Default::default(),
406            projection: None,
407            into: Default::default(),
408            from: Default::default(),
409            lateral_views: Default::default(),
410            selection: Default::default(),
411            group_by: Some(ast::GroupByExpr::Expressions(Vec::new(), Vec::new())),
412            cluster_by: Default::default(),
413            distribute_by: Default::default(),
414            sort_by: Default::default(),
415            having: Default::default(),
416            named_window: Default::default(),
417            qualify: Default::default(),
418            value_table_mode: Default::default(),
419            flavor: Some(SelectFlavor::Standard),
420            flatten_alias_counter: 0,
421            flatten_table_aliases: Vec::new(),
422        }
423    }
424}
425impl Default for SelectBuilder {
426    fn default() -> Self {
427        Self::create_empty()
428    }
429}
430
431#[derive(Clone)]
432pub struct TableWithJoinsBuilder {
433    relation: Option<RelationBuilder>,
434    joins: Vec<ast::Join>,
435}
436
437impl TableWithJoinsBuilder {
438    pub fn relation(&mut self, value: RelationBuilder) -> &mut Self {
439        self.relation = Some(value);
440        self
441    }
442
443    pub fn joins(&mut self, value: Vec<ast::Join>) -> &mut Self {
444        self.joins = value;
445        self
446    }
447    pub fn push_join(&mut self, value: ast::Join) -> &mut Self {
448        self.joins.push(value);
449        self
450    }
451
452    pub fn build(&self) -> Result<Option<ast::TableWithJoins>, BuilderError> {
453        match self.relation {
454            Some(ref value) => match value.build()? {
455                Some(relation) => Ok(Some(ast::TableWithJoins {
456                    relation,
457                    joins: self.joins.clone(),
458                })),
459                None => Ok(None),
460            },
461            None => Err(Into::into(UninitializedFieldError::from("relation"))),
462        }
463    }
464    fn create_empty() -> Self {
465        Self {
466            relation: Default::default(),
467            joins: Default::default(),
468        }
469    }
470}
471impl Default for TableWithJoinsBuilder {
472    fn default() -> Self {
473        Self::create_empty()
474    }
475}
476
477#[derive(Clone)]
478pub struct RelationBuilder {
479    relation: Option<TableFactorBuilder>,
480}
481
482#[derive(Clone)]
483// Boxing variants would penalize the common builder path; this enum is
484// constructed-then-consumed locally rather than stored at scale.
485#[expect(clippy::large_enum_variant)]
486enum TableFactorBuilder {
487    Table(TableRelationBuilder),
488    Derived(DerivedRelationBuilder),
489    NestedJoin(ast::TableWithJoins, Option<ast::TableAlias>),
490    Unnest(UnnestRelationBuilder),
491    Flatten(FlattenRelationBuilder),
492    Empty,
493}
494
495impl RelationBuilder {
496    pub fn has_relation(&self) -> bool {
497        self.relation.is_some()
498    }
499    pub fn table(&mut self, value: TableRelationBuilder) -> &mut Self {
500        self.relation = Some(TableFactorBuilder::Table(value));
501        self
502    }
503    pub fn derived(&mut self, value: DerivedRelationBuilder) -> &mut Self {
504        self.relation = Some(TableFactorBuilder::Derived(value));
505        self
506    }
507
508    pub fn nested_join(
509        &mut self,
510        value: ast::TableWithJoins,
511        alias: Option<ast::TableAlias>,
512    ) -> &mut Self {
513        self.relation = Some(TableFactorBuilder::NestedJoin(value, alias));
514        self
515    }
516
517    pub fn unnest(&mut self, value: UnnestRelationBuilder) -> &mut Self {
518        self.relation = Some(TableFactorBuilder::Unnest(value));
519        self
520    }
521
522    pub fn flatten(&mut self, value: FlattenRelationBuilder) -> &mut Self {
523        self.relation = Some(TableFactorBuilder::Flatten(value));
524        self
525    }
526
527    pub fn empty(&mut self) -> &mut Self {
528        self.relation = Some(TableFactorBuilder::Empty);
529        self
530    }
531    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
532        let new = self;
533        match new.relation {
534            Some(TableFactorBuilder::Table(ref mut rel_builder)) => {
535                rel_builder.alias = value;
536            }
537            Some(TableFactorBuilder::Derived(ref mut rel_builder)) => {
538                rel_builder.alias = value;
539            }
540            Some(TableFactorBuilder::NestedJoin(_, ref mut alias)) => {
541                *alias = value;
542            }
543            Some(TableFactorBuilder::Unnest(ref mut rel_builder)) => {
544                rel_builder.alias = value;
545            }
546            Some(TableFactorBuilder::Flatten(ref mut rel_builder)) => {
547                rel_builder.alias = value;
548            }
549            Some(TableFactorBuilder::Empty) => (),
550            None => (),
551        }
552        new
553    }
554    pub fn build(&self) -> Result<Option<ast::TableFactor>, BuilderError> {
555        Ok(match self.relation {
556            Some(TableFactorBuilder::Table(ref value)) => Some(value.build()?),
557            Some(TableFactorBuilder::Derived(ref value)) => Some(value.build()?),
558            Some(TableFactorBuilder::NestedJoin(ref table_with_joins, ref alias)) => {
559                Some(ast::TableFactor::NestedJoin {
560                    table_with_joins: Box::new(table_with_joins.clone()),
561                    alias: alias.clone(),
562                })
563            }
564            Some(TableFactorBuilder::Unnest(ref value)) => Some(value.build()?),
565            Some(TableFactorBuilder::Flatten(ref value)) => Some(value.build()?),
566            Some(TableFactorBuilder::Empty) => None,
567            None => return Err(Into::into(UninitializedFieldError::from("relation"))),
568        })
569    }
570    fn create_empty() -> Self {
571        Self {
572            relation: Default::default(),
573        }
574    }
575}
576impl Default for RelationBuilder {
577    fn default() -> Self {
578        Self::create_empty()
579    }
580}
581
582#[derive(Clone)]
583pub struct TableRelationBuilder {
584    name: Option<ast::ObjectName>,
585    alias: Option<ast::TableAlias>,
586    args: Option<Vec<ast::FunctionArg>>,
587    with_hints: Vec<ast::Expr>,
588    version: Option<ast::TableVersion>,
589    partitions: Vec<ast::Ident>,
590    index_hints: Vec<ast::TableIndexHints>,
591}
592
593impl TableRelationBuilder {
594    pub fn name(&mut self, value: ast::ObjectName) -> &mut Self {
595        self.name = Some(value);
596        self
597    }
598    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
599        self.alias = value;
600        self
601    }
602    pub fn args(&mut self, value: Option<Vec<ast::FunctionArg>>) -> &mut Self {
603        self.args = value;
604        self
605    }
606    pub fn with_hints(&mut self, value: Vec<ast::Expr>) -> &mut Self {
607        self.with_hints = value;
608        self
609    }
610    pub fn version(&mut self, value: Option<ast::TableVersion>) -> &mut Self {
611        self.version = value;
612        self
613    }
614    pub fn partitions(&mut self, value: Vec<ast::Ident>) -> &mut Self {
615        self.partitions = value;
616        self
617    }
618    pub fn index_hints(&mut self, value: Vec<ast::TableIndexHints>) -> &mut Self {
619        self.index_hints = value;
620        self
621    }
622    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
623        Ok(ast::TableFactor::Table {
624            name: match self.name {
625                Some(ref value) => value.clone(),
626                None => return Err(Into::into(UninitializedFieldError::from("name"))),
627            },
628            alias: self.alias.clone(),
629            args: self.args.clone().map(|args| ast::TableFunctionArgs {
630                args,
631                settings: None,
632            }),
633            with_hints: self.with_hints.clone(),
634            version: self.version.clone(),
635            partitions: self.partitions.clone(),
636            with_ordinality: false,
637            json_path: None,
638            sample: None,
639            index_hints: self.index_hints.clone(),
640        })
641    }
642    fn create_empty() -> Self {
643        Self {
644            name: Default::default(),
645            alias: Default::default(),
646            args: Default::default(),
647            with_hints: Default::default(),
648            version: Default::default(),
649            partitions: Default::default(),
650            index_hints: Default::default(),
651        }
652    }
653}
654impl Default for TableRelationBuilder {
655    fn default() -> Self {
656        Self::create_empty()
657    }
658}
659#[derive(Clone)]
660pub struct DerivedRelationBuilder {
661    lateral: Option<bool>,
662    subquery: Option<Box<ast::Query>>,
663    alias: Option<ast::TableAlias>,
664}
665
666impl DerivedRelationBuilder {
667    pub fn lateral(&mut self, value: bool) -> &mut Self {
668        self.lateral = Some(value);
669        self
670    }
671    pub fn subquery(&mut self, value: Box<ast::Query>) -> &mut Self {
672        self.subquery = Some(value);
673        self
674    }
675    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
676        self.alias = value;
677        self
678    }
679    fn build(&self) -> Result<ast::TableFactor, BuilderError> {
680        Ok(ast::TableFactor::Derived {
681            lateral: match self.lateral {
682                Some(ref value) => *value,
683                None => return Err(Into::into(UninitializedFieldError::from("lateral"))),
684            },
685            subquery: match self.subquery {
686                Some(ref value) => value.clone(),
687                None => {
688                    return Err(Into::into(UninitializedFieldError::from("subquery")));
689                }
690            },
691            alias: self.alias.clone(),
692            sample: None,
693        })
694    }
695    fn create_empty() -> Self {
696        Self {
697            lateral: Default::default(),
698            subquery: Default::default(),
699            alias: Default::default(),
700        }
701    }
702}
703impl Default for DerivedRelationBuilder {
704    fn default() -> Self {
705        Self::create_empty()
706    }
707}
708
709#[derive(Clone)]
710pub struct UnnestRelationBuilder {
711    pub alias: Option<ast::TableAlias>,
712    pub array_exprs: Vec<ast::Expr>,
713    with_offset: bool,
714    with_offset_alias: Option<ast::Ident>,
715    with_ordinality: bool,
716}
717
718impl UnnestRelationBuilder {
719    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
720        self.alias = value;
721        self
722    }
723    pub fn array_exprs(&mut self, value: Vec<ast::Expr>) -> &mut Self {
724        self.array_exprs = value;
725        self
726    }
727
728    pub fn with_offset(&mut self, value: bool) -> &mut Self {
729        self.with_offset = value;
730        self
731    }
732
733    pub fn with_offset_alias(&mut self, value: Option<ast::Ident>) -> &mut Self {
734        self.with_offset_alias = value;
735        self
736    }
737
738    pub fn with_ordinality(&mut self, value: bool) -> &mut Self {
739        self.with_ordinality = value;
740        self
741    }
742
743    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
744        Ok(ast::TableFactor::UNNEST {
745            alias: self.alias.clone(),
746            array_exprs: self.array_exprs.clone(),
747            with_offset: self.with_offset,
748            with_offset_alias: self.with_offset_alias.clone(),
749            with_ordinality: self.with_ordinality,
750        })
751    }
752
753    fn create_empty() -> Self {
754        Self {
755            alias: Default::default(),
756            array_exprs: Default::default(),
757            with_offset: Default::default(),
758            with_offset_alias: Default::default(),
759            with_ordinality: Default::default(),
760        }
761    }
762}
763
764impl Default for UnnestRelationBuilder {
765    fn default() -> Self {
766        Self::create_empty()
767    }
768}
769
770/// Builds a `LATERAL FLATTEN(INPUT => expr, OUTER => bool)` table factor
771/// for Snowflake-style unnesting.
772#[derive(Clone)]
773pub struct FlattenRelationBuilder {
774    pub alias: Option<ast::TableAlias>,
775    /// The input expression to flatten (e.g. a column reference).
776    pub input_expr: Option<ast::Expr>,
777    /// Whether to preserve rows for NULL/empty inputs (Snowflake `OUTER` param).
778    pub outer: bool,
779}
780
781impl FlattenRelationBuilder {
782    pub fn alias(&mut self, value: Option<ast::TableAlias>) -> &mut Self {
783        self.alias = value;
784        self
785    }
786
787    pub fn input_expr(&mut self, value: ast::Expr) -> &mut Self {
788        self.input_expr = Some(value);
789        self
790    }
791
792    pub fn outer(&mut self, value: bool) -> &mut Self {
793        self.outer = value;
794        self
795    }
796
797    pub fn build(&self) -> Result<ast::TableFactor, BuilderError> {
798        let input = self.input_expr.clone().ok_or_else(|| {
799            BuilderError::from(UninitializedFieldError::from("input_expr"))
800        })?;
801
802        let mut args = vec![ast::FunctionArg::Named {
803            name: ast::Ident::new("INPUT"),
804            arg: ast::FunctionArgExpr::Expr(input),
805            operator: ast::FunctionArgOperator::RightArrow,
806        }];
807
808        if self.outer {
809            args.push(ast::FunctionArg::Named {
810                name: ast::Ident::new("OUTER"),
811                arg: ast::FunctionArgExpr::Expr(ast::Expr::Value(
812                    ast::Value::Boolean(true).into(),
813                )),
814                operator: ast::FunctionArgOperator::RightArrow,
815            });
816        }
817
818        Ok(ast::TableFactor::Function {
819            lateral: true,
820            name: ast::ObjectName::from(vec![ast::Ident::new("FLATTEN")]),
821            args,
822            with_ordinality: false,
823            alias: self.alias.clone(),
824        })
825    }
826
827    fn create_empty() -> Self {
828        Self {
829            alias: None,
830            input_expr: None,
831            outer: false,
832        }
833    }
834}
835
836impl Default for FlattenRelationBuilder {
837    fn default() -> Self {
838        Self::create_empty()
839    }
840}
841
842/// Runtime error when a `build()` method is called and one or more required fields
843/// do not have a value.
844#[derive(Debug, Clone)]
845pub struct UninitializedFieldError(&'static str);
846
847impl UninitializedFieldError {
848    /// Create a new `UninitializedFieldError` for the specified field name.
849    pub fn new(field_name: &'static str) -> Self {
850        UninitializedFieldError(field_name)
851    }
852
853    /// Get the name of the first-declared field that wasn't initialized
854    pub fn field_name(&self) -> &'static str {
855        self.0
856    }
857}
858
859impl fmt::Display for UninitializedFieldError {
860    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
861        write!(f, "Field not initialized: {}", self.0)
862    }
863}
864
865impl From<&'static str> for UninitializedFieldError {
866    fn from(field_name: &'static str) -> Self {
867        Self::new(field_name)
868    }
869}
870impl std::error::Error for UninitializedFieldError {}
871
872#[derive(Debug)]
873pub enum BuilderError {
874    UninitializedField(&'static str),
875    ValidationError(String),
876}
877impl From<UninitializedFieldError> for BuilderError {
878    fn from(s: UninitializedFieldError) -> Self {
879        Self::UninitializedField(s.field_name())
880    }
881}
882impl From<String> for BuilderError {
883    fn from(s: String) -> Self {
884        Self::ValidationError(s)
885    }
886}
887impl fmt::Display for BuilderError {
888    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
889        match self {
890            Self::UninitializedField(field) => {
891                write!(f, "`{field}` must be initialized")
892            }
893            Self::ValidationError(error) => write!(f, "{error}"),
894        }
895    }
896}
897impl std::error::Error for BuilderError {}