sqlode/query_ir

Intermediate representation shared between the query parser and the query analyzer.

This module defines two layers:

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

pub type Assignment {
  Assignment(column: String, value: Expr)
}

Constructors

  • Assignment(column: String, value: Expr)
pub type CaseBranch {
  CaseBranch(when_: Expr, then: Expr)
}

Constructors

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

  • NullLit

    NULL

  • 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. index is the 1-based index sqlode assigns to the placeholder.

  • ColumnRef(table: option.Option(String), name: String)

    Column reference (col or table.col).

  • StarRef(table: option.Option(String))

    t.* or * — only valid inside COUNT(*), really.

  • Unary(op: String, arg: Expr)

    Unary prefix operator.

  • Binary(op: String, left: Expr, right: Expr)

    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. distinct covers COUNT(DISTINCT x). filter and over carry the optional tail clauses.

  • Cast(expr: Expr, target_type: String)

    CAST(expr AS type) or the shorthand expr::type.

  • Case(
      scrutinee: option.Option(Expr),
      branches: List(CaseBranch),
      else_: option.Option(Expr),
    )

    CASE [scrutinee] WHEN … THEN … ELSE … END.

  • InExpr(expr: Expr, source: InSource, negated: Bool)

    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)right may be a subquery or array.

  • Between(expr: Expr, low: Expr, high: Expr, negated: Bool)

    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 UnsupportedExpression when 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

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 FuncArg {
  FuncArg(expr: Expr)
}

Constructors

  • FuncArg(expr: Expr)
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
pub type IsPredicate {
  IsNull
  IsTrue
  IsFalse
  IsUnknown
  IsDistinctFrom(target: Expr)
}

Constructors

  • IsNull
  • IsTrue
  • IsFalse
  • IsUnknown
  • IsDistinctFrom(target: Expr)

A JOIN clause.

pub type JoinClause {
  JoinClause(
    table_name: String,
    alias: option.Option(String),
    on_tokens: option.Option(List(lexer.Token)),
  )
}

Constructors

pub type JoinKind {
  InnerJoin
  LeftJoin
  RightJoin
  FullJoin
  CrossJoin
}

Constructors

  • InnerJoin
  • LeftJoin
  • RightJoin
  • FullJoin
  • CrossJoin
pub type JoinOn {
  JoinOnExpr(expr: Expr)
  JoinUsing(columns: List(String))
  JoinNoCondition
}

Constructors

  • JoinOnExpr(expr: Expr)
  • JoinUsing(columns: List(String))
  • JoinNoCondition
pub type LikeOp {
  Like
  Ilike
  SimilarTo
}

Constructors

  • Like
  • Ilike
  • SimilarTo
pub type NullsOrder {
  NullsFirst
  NullsLast
}

Constructors

  • NullsFirst
  • NullsLast
pub type OrderKey {
  OrderKey(
    expr: Expr,
    descending: Bool,
    nulls: option.Option(NullsOrder),
  )
}

Constructors

pub type Quantifier {
  QAny
  QAll
  QSome
}

Constructors

  • QAny
  • QAll
  • QSome
pub type RichQuery {
  RichQuery(
    base: model.ParsedQuery,
    tokens: List(lexer.Token),
    stmt: Stmt,
  )
}

Constructors

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

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

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. origin records 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

pub type SetOpKind {
  Union
  Intersect
  Except
}

Constructors

  • Union
  • Intersect
  • Except

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

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,
    on_duplicate_key_update: List(Assignment),
    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,
      on_duplicate_key_update: List(Assignment),
      returning: List(SelectItemEx),
    )

    Arguments

    on_duplicate_key_update

    MySQL ON DUPLICATE KEY UPDATE assignment list. Empty when the statement has no upsert tail or the engine is not MySQL. Preserved here so downstream passes see the clause instead of the legacy “skip to RETURNING” silent drop.

  • 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 reason string 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

pub type TokenizedQuery {
  TokenizedQuery(
    base: model.ParsedQuery,
    tokens: List(lexer.Token),
  )
}

Constructors

pub type WindowSpec {
  WindowSpec(
    partition_by: List(Expr),
    order_by: List(OrderKey),
    frame: option.Option(List(lexer.Token)),
  )
}

Constructors

Search Document