sqlode/query_ir
Intermediate representation shared between the query parser and the query analyzer.
This module defines two layers:
- The legacy structured IR (
SqlStatement,SelectItem,FromItem,JoinClause) that identifies top-level clauses but stores complex sub-expressions as rawList(lexer.Token). - The expression-aware IR (
Stmt,Expr,CteDef, …) that explicitly models the SQL subset sqlode actually needs: CTEs (including nested references), select items, table refs / aliases / derived tables / lateral subqueries, predicate expressions, arithmetic andCASE, function calls and casts,IN,EXISTS,ANY,ALL,GROUP BY,HAVING, window expressions, andRETURNING.
The expression-aware IR is intended to become the single semantic
input for type inference; RawExpr / UnstructuredStmt remain only
as explicit diagnostic hooks for concrete IR gaps.
Types
A CTE definition. columns is the explicit column list when
present (name(c1, c2) AS (…)). body is the nested statement.
pub type CteDef {
CteDef(
name: String,
columns: List(String),
body: Stmt,
recursive: Bool,
)
}
Constructors
-
CteDef( name: String, columns: List(String), body: Stmt, recursive: Bool, )
The expression AST. Designed to cover the subset of SQL that sqlode needs to infer parameter and result-column types for the complex fixtures (CTE + window + CASE, LATERAL + COALESCE, EXISTS
- nested CASE, INSERT..SELECT..RETURNING).
pub type Expr {
NullLit
BoolLit(value: Bool)
StringLit(value: String)
NumberLit(value: String)
Param(index: Int, raw: String)
ColumnRef(table: option.Option(String), name: String)
StarRef(table: option.Option(String))
Unary(op: String, arg: Expr)
Binary(op: String, left: Expr, right: Expr)
Func(
name: String,
args: List(FuncArg),
distinct: Bool,
filter: option.Option(Expr),
over: option.Option(WindowSpec),
)
Cast(expr: Expr, target_type: String)
Case(
scrutinee: option.Option(Expr),
branches: List(CaseBranch),
else_: option.Option(Expr),
)
InExpr(expr: Expr, source: InSource, negated: Bool)
Exists(core: SelectCore, negated: Bool)
ScalarSubquery(core: SelectCore)
Quantified(
op: String,
left: Expr,
quantifier: Quantifier,
right: Expr,
)
Between(expr: Expr, low: Expr, high: Expr, negated: Bool)
IsCheck(expr: Expr, predicate: IsPredicate, negated: Bool)
LikeExpr(
expr: Expr,
op: LikeOp,
pattern: Expr,
escape: option.Option(Expr),
negated: Bool,
)
ArrayLit(elements: List(Expr))
Tuple(elements: List(Expr))
Macro(name: String, body: List(lexer.Token))
RawExpr(reason: String, tokens: List(lexer.Token))
}
Constructors
-
NullLitNULL -
BoolLit(value: Bool)TRUE,FALSE -
StringLit(value: String)String literal
-
NumberLit(value: String)Numeric literal; string form preserves precision for downstream callers and lets us decide int vs float without reparsing.
-
Param(index: Int, raw: String)Parameter placeholder:
$1,?,?1,:name, etc.indexis the 1-based index sqlode assigns to the placeholder. -
ColumnRef(table: option.Option(String), name: String)Column reference (
colortable.col). -
StarRef(table: option.Option(String))t.*or*— only valid inside COUNT(*), really. -
Unary(op: String, arg: Expr)Unary prefix operator.
-
Binary operator (arithmetic, comparison, logical, string, JSON).
-
Func( name: String, args: List(FuncArg), distinct: Bool, filter: option.Option(Expr), over: option.Option(WindowSpec), )Function call.
distinctcoversCOUNT(DISTINCT x).filterandovercarry the optional tail clauses. -
Cast(expr: Expr, target_type: String)CAST(expr AS type)or the shorthandexpr::type. -
Case( scrutinee: option.Option(Expr), branches: List(CaseBranch), else_: option.Option(Expr), )CASE [scrutinee] WHEN … THEN … ELSE … END. -
expr [NOT] IN … -
Exists(core: SelectCore, negated: Bool)[NOT] EXISTS (subquery) -
ScalarSubquery(core: SelectCore)Correlated scalar subquery.
-
Quantified( op: String, left: Expr, quantifier: Quantifier, right: Expr, )left op ANY|ALL (right)—rightmay be a subquery or array. -
expr BETWEEN low AND high. -
IsCheck(expr: Expr, predicate: IsPredicate, negated: Bool)expr IS [NOT] NULL/IS [NOT] TRUE|FALSE|UNKNOWN. -
LikeExpr( expr: Expr, op: LikeOp, pattern: Expr, escape: option.Option(Expr), negated: Bool, )[NOT] LIKE/[NOT] ILIKE/[NOT] SIMILAR TO. -
ArrayLit(elements: List(Expr))ARRAY[a, b, c]. -
Tuple(elements: List(Expr))Tuple / row constructor
(a, b, c). -
Macro(name: String, body: List(lexer.Token))sqlode.arg(name)/sqlode.narg(name)/sqlode.slice(name)/sqlode.embed(table)— sqlode-specific macros. -
RawExpr(reason: String, tokens: List(lexer.Token))Explicit unsupported-expression marker. Analyzer passes surface
UnsupportedExpressionwhen inference hits this node.
A table or subquery in the FROM clause.
pub type FromItem {
TableRef(name: String, alias: option.Option(String))
SubqueryRef(
tokens: List(lexer.Token),
alias: option.Option(String),
)
}
Constructors
-
TableRef(name: String, alias: option.Option(String)) -
SubqueryRef( tokens: List(lexer.Token), alias: option.Option(String), )
Items in a FROM clause.
pub type FromItemEx {
FromTable(name: String, alias: option.Option(String))
FromSubquery(
core: SelectCore,
alias: String,
column_aliases: List(String),
)
FromValues(
rows: List(List(Expr)),
alias: String,
column_aliases: List(String),
)
FromJoin(
left: FromItemEx,
right: FromItemEx,
kind: JoinKind,
on: JoinOn,
lateral: Bool,
)
}
Constructors
-
FromTable(name: String, alias: option.Option(String)) -
FromSubquery( core: SelectCore, alias: String, column_aliases: List(String), ) -
FromValues( rows: List(List(Expr)), alias: String, column_aliases: List(String), ) -
FromJoin( left: FromItemEx, right: FromItemEx, kind: JoinKind, on: JoinOn, lateral: Bool, )
pub type InSource {
InList(values: List(Expr))
InSubquery(core: SelectCore)
InSliceMacro(name: String)
}
Constructors
-
InList(values: List(Expr)) -
InSubquery(core: SelectCore) -
InSliceMacro(name: String)IN <sqlode.slice(name)>— a sqlode-specific variadic list.
pub type InsertSource {
InsertValues(rows: List(List(Expr)))
InsertSelect(core: SelectCore)
InsertDefaultValues
}
Constructors
-
InsertValues(rows: List(List(Expr))) -
InsertSelect(core: SelectCore) -
InsertDefaultValues
A JOIN clause.
pub type JoinClause {
JoinClause(
table_name: String,
alias: option.Option(String),
on_tokens: option.Option(List(lexer.Token)),
)
}
Constructors
-
JoinClause( table_name: String, alias: option.Option(String), on_tokens: option.Option(List(lexer.Token)), )
pub type JoinKind {
InnerJoin
LeftJoin
RightJoin
FullJoin
CrossJoin
}
Constructors
-
InnerJoin -
LeftJoin -
RightJoin -
FullJoin -
CrossJoin
pub type NullsOrder {
NullsFirst
NullsLast
}
Constructors
-
NullsFirst -
NullsLast
pub type OrderKey {
OrderKey(
expr: Expr,
descending: Bool,
nulls: option.Option(NullsOrder),
)
}
Constructors
-
OrderKey( expr: Expr, descending: Bool, nulls: option.Option(NullsOrder), )
pub type Quantifier {
QAny
QAll
QSome
}
Constructors
-
QAny -
QAll -
QSome
pub type RichQuery {
RichQuery(
base: model.ParsedQuery,
tokens: List(lexer.Token),
stmt: Stmt,
)
}
Constructors
-
RichQuery( base: model.ParsedQuery, tokens: List(lexer.Token), stmt: Stmt, )
The body of a SELECT. Reused for subqueries (scalar, IN,
EXISTS) and for INSERT..SELECT.
pub type SelectCore {
SelectCore(
distinct: Bool,
select_items: List(SelectItemEx),
from: List(FromItemEx),
where_: option.Option(Expr),
group_by: List(Expr),
having: option.Option(Expr),
order_by: List(OrderKey),
limit: option.Option(Expr),
offset: option.Option(Expr),
set_op: option.Option(SetOp),
)
}
Constructors
-
SelectCore( distinct: Bool, select_items: List(SelectItemEx), from: List(FromItemEx), where_: option.Option(Expr), group_by: List(Expr), having: option.Option(Expr), order_by: List(OrderKey), limit: option.Option(Expr), offset: option.Option(Expr), set_op: option.Option(SetOp), )
A single item in a SELECT list.
pub type SelectItem {
StarItem(table_prefix: option.Option(String))
ExpressionItem(
tokens: List(lexer.Token),
alias: option.Option(String),
)
}
Constructors
-
StarItem(table_prefix: option.Option(String))*ortable.* -
ExpressionItem( tokens: List(lexer.Token), alias: option.Option(String), )An expression, possibly aliased
A select item in the expression-aware IR.
pub type SelectItemEx {
StarEx(table_prefix: option.Option(String))
ExprItem(expr: Expr, alias: option.Option(String))
}
Constructors
-
StarEx(table_prefix: option.Option(String))*,table.* -
ExprItem(expr: Expr, alias: option.Option(String))An expression, possibly aliased.
originrecords the source table when the expression is a simple qualified column, so result-column resolution can skip the token rescan.
pub type SetOp {
SetOp(kind: SetOpKind, all: Bool, right: SelectCore)
}
Constructors
-
SetOp(kind: SetOpKind, all: Bool, right: SelectCore)
Top-level statement structure extracted from the token list.
pub type SqlStatement {
SelectStatement(
select_items: List(SelectItem),
from: List(FromItem),
joins: List(JoinClause),
where_tokens: option.Option(List(lexer.Token)),
group_by_tokens: option.Option(List(lexer.Token)),
having_tokens: option.Option(List(lexer.Token)),
order_by_tokens: option.Option(List(lexer.Token)),
limit_tokens: option.Option(List(lexer.Token)),
)
InsertStatement(
table_name: String,
columns: List(String),
value_groups: List(List(lexer.Token)),
returning_tokens: option.Option(List(lexer.Token)),
)
UpdateStatement(
table_name: String,
set_tokens: List(lexer.Token),
where_tokens: option.Option(List(lexer.Token)),
returning_tokens: option.Option(List(lexer.Token)),
)
DeleteStatement(
table_name: String,
where_tokens: option.Option(List(lexer.Token)),
returning_tokens: option.Option(List(lexer.Token)),
)
UnstructuredStatement(tokens: List(lexer.Token))
}
Constructors
-
SelectStatement( select_items: List(SelectItem), from: List(FromItem), joins: List(JoinClause), where_tokens: option.Option(List(lexer.Token)), group_by_tokens: option.Option(List(lexer.Token)), having_tokens: option.Option(List(lexer.Token)), order_by_tokens: option.Option(List(lexer.Token)), limit_tokens: option.Option(List(lexer.Token)), ) -
InsertStatement( table_name: String, columns: List(String), value_groups: List(List(lexer.Token)), returning_tokens: option.Option(List(lexer.Token)), ) -
UpdateStatement( table_name: String, set_tokens: List(lexer.Token), where_tokens: option.Option(List(lexer.Token)), returning_tokens: option.Option(List(lexer.Token)), ) -
DeleteStatement( table_name: String, where_tokens: option.Option(List(lexer.Token)), returning_tokens: option.Option(List(lexer.Token)), ) -
UnstructuredStatement(tokens: List(lexer.Token))Fallback for statements that don’t match the above patterns.
Top-level statement shape in the expression-aware IR.
Every variant carries its own list of CTE definitions so an
INSERT/UPDATE/DELETE prefixed with WITH (fixture 4) is
modelled end-to-end instead of being stripped at a boundary.
pub type Stmt {
SelectStmt(ctes: List(CteDef), core: SelectCore)
InsertStmt(
ctes: List(CteDef),
table: String,
columns: List(String),
source: InsertSource,
returning: List(SelectItemEx),
)
UpdateStmt(
ctes: List(CteDef),
table: String,
alias: option.Option(String),
assignments: List(Assignment),
from: List(FromItemEx),
where_: option.Option(Expr),
returning: List(SelectItemEx),
)
DeleteStmt(
ctes: List(CteDef),
table: String,
alias: option.Option(String),
using: List(FromItemEx),
where_: option.Option(Expr),
returning: List(SelectItemEx),
)
UnstructuredStmt(reason: String, tokens: List(lexer.Token))
}
Constructors
-
SelectStmt(ctes: List(CteDef), core: SelectCore) -
InsertStmt( ctes: List(CteDef), table: String, columns: List(String), source: InsertSource, returning: List(SelectItemEx), ) -
UpdateStmt( ctes: List(CteDef), table: String, alias: option.Option(String), assignments: List(Assignment), from: List(FromItemEx), where_: option.Option(Expr), returning: List(SelectItemEx), ) -
DeleteStmt( ctes: List(CteDef), table: String, alias: option.Option(String), using: List(FromItemEx), where_: option.Option(Expr), returning: List(SelectItemEx), ) -
UnstructuredStmt(reason: String, tokens: List(lexer.Token))Explicit fallback. The
reasonstring is surfaced in analyzer diagnostics so the operator can see which IR gap was hit; the raw token list is preserved so legacy token-based passes can still work on it.
StructuredQuery wraps TokenizedQuery with the structured IR.
The raw token list is preserved for backward compatibility with
code that hasn’t migrated to the structured representation yet.
pub type StructuredQuery {
StructuredQuery(
base: model.ParsedQuery,
tokens: List(lexer.Token),
statement: SqlStatement,
)
}
Constructors
-
StructuredQuery( base: model.ParsedQuery, tokens: List(lexer.Token), statement: SqlStatement, )
pub type TokenizedQuery {
TokenizedQuery(
base: model.ParsedQuery,
tokens: List(lexer.Token),
)
}
Constructors
-
TokenizedQuery( base: model.ParsedQuery, tokens: List(lexer.Token), )
pub type WindowSpec {
WindowSpec(
partition_by: List(Expr),
order_by: List(OrderKey),
frame: option.Option(List(lexer.Token)),
)
}
Constructors
-
WindowSpec( partition_by: List(Expr), order_by: List(OrderKey), frame: option.Option(List(lexer.Token)), )