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