Skip to main content

uqa_sql/ast/
from.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use serde::{Deserialize, Serialize};
8
9use super::{Expr, FunctionBinding, InternalRelationId, SelectStmt};
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum FromClause {
13    /// `FROM <table> [AS <alias>]`.
14    Table {
15        /// Durable catalog identity, including an explicit schema when present.
16        name: String,
17        /// Relation name visible to SQL column binding before an alias is applied.
18        qualifier: String,
19        alias: Option<String>,
20        /// Positional names exposed by the range-table alias.
21        #[serde(default, skip_serializing_if = "Vec::is_empty")]
22        column_aliases: Vec<String>,
23        /// Creation-bound physical columns of a stored table source, before positional aliases. Column deletion and renaming update this list; later additions do not change its shape.
24        #[serde(default, skip_serializing_if = "Option::is_none")]
25        bound_columns: Option<Vec<String>>,
26        /// Ordinary references include inheritance children; `ONLY table`
27        /// clears this flag.
28        #[serde(default = "default_include_descendants")]
29        include_descendants: bool,
30    },
31    /// `FROM left <kind> right ON predicate`. `lateral` is true when
32    /// the right side is a LATERAL subquery / function -- the engine
33    /// re-evaluates it for every left row.
34    Join {
35        left: Box<FromClause>,
36        right: Box<FromClause>,
37        kind: JoinKind,
38        /// Boolean qualification supplied by `ON`. This is mutually
39        /// exclusive with `using` and `natural` in parser-produced trees.
40        on: Option<Expr>,
41        /// `PostgreSQL` `USING (column, ...) [AS alias]` metadata. The column
42        /// list must remain explicit until both input row types are known so
43        /// binding can validate each side and construct the merged output.
44        #[serde(default, skip_serializing_if = "Option::is_none")]
45        using: Option<JoinUsing>,
46        /// `NATURAL` derives its `USING` list from the visible columns of both
47        /// input row types at binding time.
48        #[serde(default)]
49        natural: bool,
50        /// Alias applied to the complete parenthesized JOIN result. When
51        /// present, the input relation names are hidden from the enclosing
52        /// query level.
53        #[serde(default, skip_serializing_if = "Option::is_none")]
54        alias: Option<String>,
55        /// Positional aliases for the JOIN output after USING/NATURAL shaping.
56        #[serde(default, skip_serializing_if = "Vec::is_empty")]
57        column_aliases: Vec<String>,
58        #[allow(dead_code)]
59        lateral: bool,
60    },
61    /// `FROM (VALUES (...)...) [AS <alias>(<col_aliases>)]`.
62    Values {
63        rows: Vec<Vec<Expr>>,
64        alias: Option<String>,
65        column_aliases: Vec<String>,
66        /// Opaque identity for an engine-injected, SQL-invisible VALUES row
67        /// carrier. Parser-produced VALUES sources always leave this unset.
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        #[doc(hidden)]
70        internal_relation: Option<InternalRelationId>,
71        /// Declared physical attribute types for an internal VALUES carrier;
72        /// needed even when the carrier has zero rows.
73        #[serde(default, skip_serializing_if = "Vec::is_empty")]
74        #[doc(hidden)]
75        internal_column_types: Vec<Option<super::ColumnType>>,
76        /// Implicit collation identities of internal attributes, independent of row presence. An absent legacy vector uses each declared type's default.
77        #[serde(default, skip_serializing_if = "Vec::is_empty")]
78        #[doc(hidden)]
79        internal_column_collations: Vec<Option<u32>>,
80    },
81    /// `FROM <fn>(<args>) [AS <alias>(<col_aliases>)]` -- e.g.
82    /// `generate_series(1, 5)`, `unnest(arr)`, `regexp_split_to_table`,
83    /// `json_each(...)`, `cypher(...) AS (col agtype, ...)`. The engine
84    /// dispatches by name.
85    Function {
86        name: String,
87        /// Exact catalog routine identity for a stored expression tree. Parser-produced trees leave this unset and catalog owners bind it before persistence.
88        #[serde(default, skip_serializing_if = "Option::is_none")]
89        binding: Option<FunctionBinding>,
90        /// Local function identifier used as `PostgreSQL`'s default output column label. Kept separate from the catalog-qualified lookup name so quoted identifiers containing `.` remain indivisible.
91        output_name: String,
92        /// Catalog relations bound to a tuple-producing operator join, kept separate from scalar arguments so each operand retains its own name-resolution, dependency, planning, and execution context.
93        #[serde(default, skip_serializing_if = "Option::is_none")]
94        relations: Option<OperatorJoinRelations>,
95        args: Vec<Expr>,
96        alias: Option<String>,
97        column_aliases: Vec<String>,
98        /// Append `PostgreSQL`'s one-based `bigint` ordinality column after the function's ordinary output columns.
99        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
100        ordinality: bool,
101        /// Declared column types when the alias used a column
102        /// definition list (`AS (col agtype, n int)`); empty when the
103        /// alias only renamed columns. Type names are lowercased
104        /// `PostgreSQL` internal names (`agtype`, `int4`, `text`, ...).
105        #[serde(default)]
106        column_types: Vec<String>,
107    },
108    /// One `PostgreSQL` range-function group. This represents explicit
109    /// `ROWS FROM (...)` syntax and the parser transform of an unqualified
110    /// multi-argument `unnest(a, b, ...)` into independent unary
111    /// `pg_catalog.unnest` members. Members are evaluated independently and
112    /// their result columns are concatenated in declaration order.
113    FunctionGroup {
114        functions: Vec<TableFunction>,
115        /// Alias applied to the complete group rather than to an individual
116        /// member.
117        alias: Option<String>,
118        /// Positional aliases for the concatenated group output.
119        column_aliases: Vec<String>,
120        /// Append one group-wide, one-based `bigint` ordinality column after
121        /// every member's ordinary output columns.
122        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
123        ordinality: bool,
124    },
125    /// `FROM (SELECT ...) AS <alias>` -- subquery as a relation.
126    /// The body re-runs as if a CTE; the alias renames the result
127    /// columns when supplied.
128    Subquery {
129        body: Box<SelectStmt>,
130        alias: Option<String>,
131        column_aliases: Vec<String>,
132    },
133}
134
135const fn default_include_descendants() -> bool {
136    true
137}
138
139/// One function inside a [`FromClause::FunctionGroup`].
140///
141/// A member owns its column definition list because `ROWS FROM` permits a
142/// distinct `AS (name type, ...)` clause after each call. The range item's
143/// relation alias, positional aliases, and ordinality remain on the enclosing
144/// group.
145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
146pub struct TableFunction {
147    pub name: String,
148    /// Exact catalog routine identity for a stored expression tree. Parser-produced trees leave this unset and catalog owners bind it before persistence.
149    #[serde(default, skip_serializing_if = "Option::is_none")]
150    pub binding: Option<FunctionBinding>,
151    pub output_name: String,
152    #[serde(default, skip_serializing_if = "Option::is_none")]
153    pub relations: Option<OperatorJoinRelations>,
154    pub args: Vec<Expr>,
155    #[serde(default, skip_serializing_if = "Vec::is_empty")]
156    pub column_aliases: Vec<String>,
157    #[serde(default, skip_serializing_if = "Vec::is_empty")]
158    pub column_types: Vec<String>,
159}
160
161/// The two independently bound catalog relations consumed by an operator-join table function.
162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
163pub struct OperatorJoinRelations {
164    pub left: String,
165    pub right: String,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
169pub struct JoinUsing {
170    pub columns: Vec<String>,
171    #[serde(default, skip_serializing_if = "Option::is_none")]
172    pub alias: Option<String>,
173}
174
175impl FromClause {
176    /// All table names referenced under this clause, in declaration
177    /// order. Used by the compiler to resolve unqualified column refs.
178    pub fn collect_tables(&self, out: &mut Vec<(String, Option<String>)>) {
179        match self {
180            FromClause::Table {
181                name,
182                qualifier,
183                alias,
184                ..
185            } => out.push((
186                name.clone(),
187                Some(alias.as_ref().unwrap_or(qualifier).clone()),
188            )),
189            FromClause::Join { left, right, .. } => {
190                left.collect_tables(out);
191                right.collect_tables(out);
192            }
193            FromClause::Values { alias, .. }
194            | FromClause::Function { alias, .. }
195            | FromClause::FunctionGroup { alias, .. }
196            | FromClause::Subquery { alias, .. } => {
197                if let Some(a) = alias {
198                    out.push((a.clone(), Some(a.clone())));
199                }
200            }
201        }
202    }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
206pub enum JoinKind {
207    Inner,
208    Left,
209    Right,
210    Full,
211    Cross,
212}