snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-16 02:17:34 +05:00
commit 0f4df23290
7340 changed files with 238743 additions and 0 deletions

169
internal/sql/ast/CLAUDE.md Normal file
View File

@@ -0,0 +1,169 @@
# AST Package - Claude Code Guide
This package defines the Abstract Syntax Tree (AST) nodes used by sqlc to represent SQL statements across all supported databases (PostgreSQL, MySQL, SQLite).
## Key Concepts
### Node Interface
All AST nodes implement the `Node` interface with:
- `Pos() int` - returns the source position
- `Format(buf *TrackedBuffer, d format.Dialect)` - formats the node back to SQL
### TrackedBuffer
The `TrackedBuffer` type (`print.go`) handles SQL formatting with dialect-specific behavior:
- `astFormat(node Node)` - formats any AST node
- `join(list *List, sep string)` - joins list items with separator
- `WriteString(s string)` - writes raw SQL
- `QuoteIdent(name string)` - quotes identifiers (dialect-specific)
- `TypeName(ns, name string)` - formats type names (dialect-specific)
### Pretty printing
The doc renderer lives in `ast/printer` (`printer.Buffer`), which knows
nothing about AST nodes; `TrackedBuffer` embeds it and adds the
AST-facing layer (`astFormat`, comment emission, anchors).
`TrackedBuffer` records a Wadler-style document (the model behind Prettier
and ruff): Format methods emit text plus layout tokens, and the renderer
decides which break opportunities become newlines.
- `Line()` - a space when the group fits on one line, a newline otherwise
- `Softline()` - nothing when the group fits, a newline otherwise
- `Group()` / `EndGroup()` - a region laid out flat when its width fits
- `Indent()` / `EndIndent()` - one level deeper (2 spaces) after breaks
- `Breaker()` - forces every enclosing group to break (measures as
infinitely wide)
- `joinComma(list)` - joins items with `,` + `Line()`
- `condition(node)` - clause-level AND/OR chain without outer parentheses,
one branch per line when broken
`ast.Format(n, d)` renders on a single line (all breaks collapse);
`ast.Pretty(n, d, width)` breaks lines to fit `width` columns. Statement
Format methods open a group and put `Line()` before each clause keyword
(`FROM`, `WHERE`, ...), so a statement that fits stays on one line and a
long one breaks at clause boundaries. When adding a Format method, write
tokens so the flat rendering is correct SQL; layout tokens are optional.
The fmt command does not pass a real width: like gofmt it never rewraps
on its own, so it prints with an effectively infinite width and defers
to the author's line breaks instead. `AttachComments` records, for each
emission-point marker, whether its neighbouring printed nodes sat on
different source lines; when they did, `boundary()` emits a `Breaker()`
there on the real print, so the group the author broke stays broken and
everything else stays flat.
### Comments
`ast.File{Stmts, Comments}` is what a comment-surfacing parser returns
(SQLite via meyer's ParseFile). `AttachComments(raw, d, comments, src)`
classifies each comment once, against a dry-run of the printer: source
positions and line numbers decide trailing (same line as the code
before) vs leading, and each comment is attached to the emission point
— node or clause/list boundary — where the printer will reach it. From
then on positions are never consulted: `PrettyWithComments(n, d, width,
table)` emits by node identity, which is what lets edited or synthetic
trees print their comments correctly (the dave/dst model; each record
also keeps the node the comment followed, for future rewriting tools).
A line comment forces every enclosing group to break — `hardline` and
`breaker` tokens measure as infinitely wide — so commented statements
format instead of collapsing. Emission points (`beforeClause`,
`boundary` in joinComma/condition) double as classification markers on
the dry run, guaranteeing attach-time decisions and print-time emission
agree.
### Dialect Interface
Dialect-specific formatting is handled via the `Dialect` interface:
```go
type Dialect interface {
QuoteIdent(string) string
TypeName(ns, name string) string
Param(int) string // $1 for PostgreSQL, ? for MySQL
NamedParam(string) string // @name for PostgreSQL, :name for SQLite
Cast(string) string
}
```
## Adding New AST Nodes
When adding a new AST node type:
1. **Create the node file** (e.g., `variable_expr.go`):
```go
package ast
type VariableExpr struct {
Name string
Location int
}
func (n *VariableExpr) Pos() int {
return n.Location
}
func (n *VariableExpr) Format(buf *TrackedBuffer) {
if n == nil {
return
}
buf.WriteString("@")
buf.WriteString(n.Name)
}
```
2. **Add to `astutils/walk.go`** - Add a case in the Walk function:
```go
case *ast.VariableExpr:
// Leaf node - no children to traverse
```
3. **Add to `astutils/rewrite.go`** - Add a case in the Apply function:
```go
case *ast.VariableExpr:
// Leaf node - no children to traverse
```
4. **Update the parser/converter** - In the relevant engine (e.g., `dolphin/convert.go` for MySQL)
## Helper Functions for Format Methods
- `set(node Node) bool` - returns true if node is non-nil and not an empty List
- `items(list *List) bool` - returns true if list has items
- `todo(node) Node` - placeholder for unimplemented conversions (returns nil)
## Common Node Types
### Statements
- `SelectStmt` - SELECT queries with FromClause, WhereClause, etc.
- `InsertStmt` - INSERT with Relation, Cols, SelectStmt, OnConflictClause
- `UpdateStmt` - UPDATE with Relations, TargetList, WhereClause
- `DeleteStmt` - DELETE with Relations, FromClause (for JOINs), Targets
### Expressions
- `A_Expr` - General expression with operator (e.g., `a + b`, `@param`)
- `ColumnRef` - Column reference with Fields list
- `FuncCall` - Function call with Func, Args, aggregation options
- `TypeCast` - Type cast with Arg and TypeName
- `ParenExpr` - Parenthesized expression
- `VariableExpr` - MySQL user variable (e.g., `@user_id`)
### Table References
- `RangeVar` - Table reference with schema, name, alias
- `JoinExpr` - JOIN with Larg, Rarg, Jointype, Quals/UsingClause
## MySQL-Specific Nodes
- `VariableExpr` - User variables (`@var`), distinct from sqlc's `@param` syntax
- `IntervalExpr` - INTERVAL expressions
- `OnDuplicateKeyUpdate` - MySQL's ON DUPLICATE KEY UPDATE clause
- `ParenExpr` - Explicit parentheses (TiDB parser wraps expressions)
## Important Distinctions
### MySQL @variable vs sqlc @param
- sqlc named parameters (`@param`) never reach the AST: `internal/sql/preprocess`
replaces them with the dialect's native placeholder before parsing, so engines
only ever see a `ParamRef`
- `@name` is only sqlc syntax where the preprocessor's dialect says so. MySQL is
excluded, so `@user_id` there stays a user variable (`VariableExpr`)
### Type Modifiers
- `TypeName.Typmods` holds type modifiers like `varchar(255)`
- For MySQL, only populate Typmods for types where length is user-specified:
- VARCHAR, CHAR, VARBINARY, BINARY - need length
- DATETIME, TIMESTAMP, DATE - internal flen should NOT be output

View File

@@ -0,0 +1,23 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type A_ArrayExpr struct {
Tag NodeTag[A_ArrayExpr] `json:"tag"`
Elements *List `json:"elements,omitempty"`
Location int `json:"location"`
}
func (n *A_ArrayExpr) Pos() int {
return n.Location
}
func (n *A_ArrayExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteString("ARRAY[")
buf.join(n.Elements, d, ", ")
buf.WriteString("]")
}

View File

@@ -0,0 +1,27 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type A_Const struct {
Tag NodeTag[A_Const] `json:"tag"`
Val Node `json:"val,omitempty"`
Location int `json:"location"`
}
func (n *A_Const) Pos() int {
return n.Location
}
func (n *A_Const) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
if _, ok := n.Val.(*String); ok {
buf.WriteString("'")
buf.astFormat(n.Val, d)
buf.WriteString("'")
} else {
buf.astFormat(n.Val, d)
}
}

178
internal/sql/ast/a_expr.go Normal file
View File

@@ -0,0 +1,178 @@
package ast
import (
"strings"
"github.com/sqlc-dev/sqlc/internal/sql/format"
)
type A_Expr struct {
Tag NodeTag[A_Expr] `json:"tag"`
Kind A_Expr_Kind `json:"kind"`
// Name is the operator, as a list of String nodes: PostgreSQL's
// operator space is open (user-defined and schema-qualified operators),
// so the shared tree keeps pg_query's shape rather than an enum. Each
// engine's converter writes the operator as its parser saw it — for an
// engine that accepts more than one spelling of the same operator
// (SQLite's != for <>, == for =), that is the author's spelling, which
// is how the printer preserves it.
Name *List `json:"name,omitempty"`
Lexpr Node `json:"lexpr,omitempty"`
Rexpr Node `json:"rexpr,omitempty"`
Location int `json:"location"`
}
func (n *A_Expr) Pos() int {
return n.Location
}
// isNamedParam reports whether this A_Expr represents a named parameter,
// returning its sigil and name. Engines encode a named parameter as a
// prefix pseudo-operator carrying the sigil the author wrote (@name for
// most engines; SQLite also spells :name and $name). The spellings mean
// different things to sqlc, so the printer emits the one it was given.
func (n *A_Expr) isNamedParam() (sigil, name string, ok bool) {
if n.Name == nil || len(n.Name.Items) != 1 {
return "", "", false
}
s, sok := n.Name.Items[0].(*String)
if !sok || (s.Str != "@" && s.Str != ":" && s.Str != "$") {
return "", "", false
}
if set(n.Lexpr) || !set(n.Rexpr) {
return "", "", false
}
if nameStr, sok := n.Rexpr.(*String); sok {
return s.Str, nameStr.Str, true
}
// Before the compiler rewrites named parameters, @name parses as the @
// operator applied to a bare column reference.
if col, sok := n.Rexpr.(*ColumnRef); sok && col.Fields != nil && len(col.Fields.Items) == 1 {
if f, sok := col.Fields.Items[0].(*String); sok {
return s.Str, f.Str, true
}
}
return "", "", false
}
// gluePrefix reports that this prefix expression must print with no space
// between the operator and its operand. `@name::type` is a sqlc named
// parameter with a cast: the cast binds tighter than the operator, so the
// parse is @ applied to a TypeCast and isNamedParam does not see it — but
// `@ name::type` would break the parameter apart for sqlc's scanner. Glue
// the sigils onto any operand that starts with an identifier, which also
// keeps a genuine prefix operator like pg's absolute-value @ meaning the
// same thing.
func (n *A_Expr) gluePrefix() bool {
if set(n.Lexpr) || n.Name == nil || len(n.Name.Items) != 1 {
return false
}
s, ok := n.Name.Items[0].(*String)
if !ok || (s.Str != "@" && s.Str != ":" && s.Str != "$") {
return false
}
operand := n.Rexpr
if tc, ok := operand.(*TypeCast); ok {
operand = tc.Arg
}
switch operand.(type) {
case *ColumnRef, *FuncCall:
return true
}
return false
}
// negated returns true when the expression's operator carries the negated
// spelling of a pattern-match operator (e.g. "!~~" for NOT LIKE).
func (n *A_Expr) negated() bool {
if n.Name == nil || len(n.Name.Items) != 1 {
return false
}
s, ok := n.Name.Items[0].(*String)
return ok && strings.HasPrefix(s.Str, "!")
}
func (n *A_Expr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
// Check for named parameter first (works regardless of Kind)
if sigil, name, ok := n.isNamedParam(); ok {
buf.WriteString(sigil)
buf.WriteString(name)
return
}
switch n.Kind {
case A_Expr_Kind_IN:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" IN (")
buf.astFormat(n.Rexpr, d)
buf.WriteString(")")
case A_Expr_Kind_LIKE:
buf.astFormat(n.Lexpr, d)
if n.negated() {
buf.WriteString(" NOT LIKE ")
} else {
buf.WriteString(" LIKE ")
}
buf.astFormat(n.Rexpr, d)
case A_Expr_Kind_ILIKE:
buf.astFormat(n.Lexpr, d)
if n.negated() {
buf.WriteString(" NOT ILIKE ")
} else {
buf.WriteString(" ILIKE ")
}
buf.astFormat(n.Rexpr, d)
case A_Expr_Kind_SIMILAR:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" SIMILAR TO ")
buf.astFormat(n.Rexpr, d)
case A_Expr_Kind_BETWEEN:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" BETWEEN ")
if l, ok := n.Rexpr.(*List); ok && len(l.Items) == 2 {
buf.astFormat(l.Items[0], d)
buf.WriteString(" AND ")
buf.astFormat(l.Items[1], d)
}
case A_Expr_Kind_NOT_BETWEEN:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" NOT BETWEEN ")
if l, ok := n.Rexpr.(*List); ok && len(l.Items) == 2 {
buf.astFormat(l.Items[0], d)
buf.WriteString(" AND ")
buf.astFormat(l.Items[1], d)
}
case A_Expr_Kind_DISTINCT:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" IS DISTINCT FROM ")
buf.astFormat(n.Rexpr, d)
case A_Expr_Kind_NOT_DISTINCT:
buf.astFormat(n.Lexpr, d)
buf.WriteString(" IS NOT DISTINCT FROM ")
buf.astFormat(n.Rexpr, d)
case A_Expr_Kind_NULLIF:
buf.WriteString("NULLIF(")
buf.astFormat(n.Lexpr, d)
buf.WriteString(", ")
buf.astFormat(n.Rexpr, d)
buf.WriteString(")")
default:
// Standard operator (including A_Expr_Kind_OP)
if set(n.Lexpr) {
buf.astFormat(n.Lexpr, d)
buf.WriteString(" ")
}
buf.astFormat(n.Name, d)
if set(n.Rexpr) {
if !n.gluePrefix() {
buf.WriteString(" ")
}
buf.astFormat(n.Rexpr, d)
}
}
}

View File

@@ -0,0 +1,24 @@
package ast
type A_Expr_Kind uint
const (
A_Expr_Kind_OP A_Expr_Kind = 1
A_Expr_Kind_OP_ANY A_Expr_Kind = 2
A_Expr_Kind_OP_ALL A_Expr_Kind = 3
A_Expr_Kind_DISTINCT A_Expr_Kind = 4
A_Expr_Kind_NOT_DISTINCT A_Expr_Kind = 5
A_Expr_Kind_NULLIF A_Expr_Kind = 6
A_Expr_Kind_IN A_Expr_Kind = 7
A_Expr_Kind_LIKE A_Expr_Kind = 8
A_Expr_Kind_ILIKE A_Expr_Kind = 9
A_Expr_Kind_SIMILAR A_Expr_Kind = 10
A_Expr_Kind_BETWEEN A_Expr_Kind = 11
A_Expr_Kind_NOT_BETWEEN A_Expr_Kind = 12
A_Expr_Kind_BETWEEN_SYM A_Expr_Kind = 13
A_Expr_Kind_NOT_BETWEEN_SYM A_Expr_Kind = 14
)
func (n *A_Expr_Kind) Pos() int {
return 0
}

View File

@@ -0,0 +1,34 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type A_Indices struct {
Tag NodeTag[A_Indices] `json:"tag"`
IsSlice bool `json:"is_slice"`
Lidx Node `json:"lidx,omitempty"`
Uidx Node `json:"uidx,omitempty"`
}
func (n *A_Indices) Pos() int {
return 0
}
func (n *A_Indices) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteString("[")
if n.IsSlice {
if set(n.Lidx) {
buf.astFormat(n.Lidx, d)
}
buf.WriteString(":")
if set(n.Uidx) {
buf.astFormat(n.Uidx, d)
}
} else {
buf.astFormat(n.Uidx, d)
}
buf.WriteString("]")
}

View File

@@ -0,0 +1,12 @@
package ast
type A_Indirection struct {
Tag NodeTag[A_Indirection] `json:"tag"`
Arg Node `json:"arg,omitempty"`
Indirection *List `json:"indirection,omitempty"`
}
func (n *A_Indirection) Pos() int {
return 0
}

View File

@@ -0,0 +1,18 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type A_Star struct {
Tag NodeTag[A_Star] `json:"tag"`
}
func (n *A_Star) Pos() int {
return 0
}
func (n *A_Star) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteRune('*')
}

View File

@@ -0,0 +1,12 @@
package ast
type AccessPriv struct {
Tag NodeTag[AccessPriv] `json:"tag"`
PrivName *string `json:"priv_name,omitempty"`
Cols *List `json:"cols,omitempty"`
}
func (n *AccessPriv) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type AggSplit uint
func (n *AggSplit) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type AggStrategy uint
func (n *AggStrategy) Pos() int {
return 0
}

View File

@@ -0,0 +1,27 @@
package ast
type Aggref struct {
Tag NodeTag[Aggref] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Aggfnoid Oid `json:"aggfnoid"`
Aggtype Oid `json:"aggtype"`
Aggcollid Oid `json:"aggcollid"`
Inputcollid Oid `json:"inputcollid"`
Aggargtypes *List `json:"aggargtypes,omitempty"`
Aggdirectargs *List `json:"aggdirectargs,omitempty"`
Args *List `json:"args,omitempty"`
Aggorder *List `json:"aggorder,omitempty"`
Aggdistinct *List `json:"aggdistinct,omitempty"`
Aggfilter Node `json:"aggfilter,omitempty"`
Aggstar bool `json:"aggstar"`
Aggvariadic bool `json:"aggvariadic"`
Aggkind byte `json:"aggkind"`
Agglevelsup Index `json:"agglevelsup"`
Aggsplit AggSplit `json:"aggsplit"`
Location int `json:"location"`
}
func (n *Aggref) Pos() int {
return n.Location
}

28
internal/sql/ast/alias.go Normal file
View File

@@ -0,0 +1,28 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type Alias struct {
Tag NodeTag[Alias] `json:"tag"`
Aliasname *string `json:"aliasname,omitempty"`
Colnames *List `json:"colnames,omitempty"`
}
func (n *Alias) Pos() int {
return 0
}
func (n *Alias) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
if n.Aliasname != nil {
buf.WriteString(*n.Aliasname)
}
if items(n.Colnames) {
buf.WriteString("(")
buf.astFormat(n.Colnames, d)
buf.WriteString(")")
}
}

View File

@@ -0,0 +1,11 @@
package ast
type AlterCollationStmt struct {
Tag NodeTag[AlterCollationStmt] `json:"tag"`
Collname *List `json:"collname,omitempty"`
}
func (n *AlterCollationStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterDatabaseSetStmt struct {
Tag NodeTag[AlterDatabaseSetStmt] `json:"tag"`
Dbname *string `json:"dbname,omitempty"`
Setstmt *VariableSetStmt `json:"setstmt,omitempty"`
}
func (n *AlterDatabaseSetStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterDatabaseStmt struct {
Tag NodeTag[AlterDatabaseStmt] `json:"tag"`
Dbname *string `json:"dbname,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterDatabaseStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterDefaultPrivilegesStmt struct {
Tag NodeTag[AlterDefaultPrivilegesStmt] `json:"tag"`
Options *List `json:"options,omitempty"`
Action *GrantStmt `json:"action,omitempty"`
}
func (n *AlterDefaultPrivilegesStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,16 @@
package ast
type AlterDomainStmt struct {
Tag NodeTag[AlterDomainStmt] `json:"tag"`
Subtype byte `json:"subtype"`
TypeName *List `json:"type_name,omitempty"`
Name *string `json:"name,omitempty"`
Def Node `json:"def,omitempty"`
Behavior DropBehavior `json:"behavior"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterDomainStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,16 @@
package ast
type AlterEnumStmt struct {
Tag NodeTag[AlterEnumStmt] `json:"tag"`
TypeName *List `json:"type_name,omitempty"`
OldVal *string `json:"old_val,omitempty"`
NewVal *string `json:"new_val,omitempty"`
NewValNeighbor *string `json:"new_val_neighbor,omitempty"`
NewValIsAfter bool `json:"new_val_is_after"`
SkipIfNewValExists bool `json:"skip_if_new_val_exists"`
}
func (n *AlterEnumStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterEventTrigStmt struct {
Tag NodeTag[AlterEventTrigStmt] `json:"tag"`
Trigname *string `json:"trigname,omitempty"`
Tgenabled byte `json:"tgenabled"`
}
func (n *AlterEventTrigStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterExtensionContentsStmt struct {
Tag NodeTag[AlterExtensionContentsStmt] `json:"tag"`
Extname *string `json:"extname,omitempty"`
Action int `json:"action"`
Objtype ObjectType `json:"objtype"`
Object Node `json:"object,omitempty"`
}
func (n *AlterExtensionContentsStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterExtensionStmt struct {
Tag NodeTag[AlterExtensionStmt] `json:"tag"`
Extname *string `json:"extname,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterExtensionStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterFdwStmt struct {
Tag NodeTag[AlterFdwStmt] `json:"tag"`
Fdwname *string `json:"fdwname,omitempty"`
FuncOptions *List `json:"func_options,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterFdwStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterForeignServerStmt struct {
Tag NodeTag[AlterForeignServerStmt] `json:"tag"`
Servername *string `json:"servername,omitempty"`
Version *string `json:"version,omitempty"`
Options *List `json:"options,omitempty"`
HasVersion bool `json:"has_version"`
}
func (n *AlterForeignServerStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterFunctionStmt struct {
Tag NodeTag[AlterFunctionStmt] `json:"tag"`
Func *ObjectWithArgs `json:"func,omitempty"`
Actions *List `json:"actions,omitempty"`
}
func (n *AlterFunctionStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterObjectDependsStmt struct {
Tag NodeTag[AlterObjectDependsStmt] `json:"tag"`
ObjectType ObjectType `json:"object_type"`
Relation *RangeVar `json:"relation,omitempty"`
Object Node `json:"object,omitempty"`
Extname Node `json:"extname,omitempty"`
}
func (n *AlterObjectDependsStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type AlterObjectSchemaStmt struct {
Tag NodeTag[AlterObjectSchemaStmt] `json:"tag"`
ObjectType ObjectType `json:"object_type"`
Relation *RangeVar `json:"relation,omitempty"`
Object Node `json:"object,omitempty"`
Newschema *string `json:"newschema,omitempty"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterObjectSchemaStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterOpFamilyStmt struct {
Tag NodeTag[AlterOpFamilyStmt] `json:"tag"`
Opfamilyname *List `json:"opfamilyname,omitempty"`
Amname *string `json:"amname,omitempty"`
IsDrop bool `json:"is_drop"`
Items *List `json:"items,omitempty"`
}
func (n *AlterOpFamilyStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterOperatorStmt struct {
Tag NodeTag[AlterOperatorStmt] `json:"tag"`
Opername *ObjectWithArgs `json:"opername,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterOperatorStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterOwnerStmt struct {
Tag NodeTag[AlterOwnerStmt] `json:"tag"`
ObjectType ObjectType `json:"object_type"`
Relation *RangeVar `json:"relation,omitempty"`
Object Node `json:"object,omitempty"`
Newowner *RoleSpec `json:"newowner,omitempty"`
}
func (n *AlterOwnerStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type AlterPolicyStmt struct {
Tag NodeTag[AlterPolicyStmt] `json:"tag"`
PolicyName *string `json:"policy_name,omitempty"`
Table *RangeVar `json:"table,omitempty"`
Roles *List `json:"roles,omitempty"`
Qual Node `json:"qual,omitempty"`
WithCheck Node `json:"with_check,omitempty"`
}
func (n *AlterPolicyStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type AlterPublicationStmt struct {
Tag NodeTag[AlterPublicationStmt] `json:"tag"`
Pubname *string `json:"pubname,omitempty"`
Options *List `json:"options,omitempty"`
Tables *List `json:"tables,omitempty"`
ForAllTables bool `json:"for_all_tables"`
TableAction DefElemAction `json:"table_action"`
}
func (n *AlterPublicationStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterRoleSetStmt struct {
Tag NodeTag[AlterRoleSetStmt] `json:"tag"`
Role *RoleSpec `json:"role,omitempty"`
Database *string `json:"database,omitempty"`
Setstmt *VariableSetStmt `json:"setstmt,omitempty"`
}
func (n *AlterRoleSetStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterRoleStmt struct {
Tag NodeTag[AlterRoleStmt] `json:"tag"`
Role *RoleSpec `json:"role,omitempty"`
Options *List `json:"options,omitempty"`
Action int `json:"action"`
}
func (n *AlterRoleStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type AlterSeqStmt struct {
Tag NodeTag[AlterSeqStmt] `json:"tag"`
Sequence *RangeVar `json:"sequence,omitempty"`
Options *List `json:"options,omitempty"`
ForIdentity bool `json:"for_identity"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterSeqStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type AlterSubscriptionStmt struct {
Tag NodeTag[AlterSubscriptionStmt] `json:"tag"`
Kind AlterSubscriptionType `json:"kind"`
Subname *string `json:"subname,omitempty"`
Conninfo *string `json:"conninfo,omitempty"`
Publication *List `json:"publication,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterSubscriptionStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type AlterSubscriptionType uint
func (n *AlterSubscriptionType) Pos() int {
return 0
}

View File

@@ -0,0 +1,11 @@
package ast
type AlterSystemStmt struct {
Tag NodeTag[AlterSystemStmt] `json:"tag"`
Setstmt *VariableSetStmt `json:"setstmt,omitempty"`
}
func (n *AlterSystemStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,59 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
const (
AT_AddColumn AlterTableType = iota
AT_AlterColumnType
AT_DropColumn
AT_DropNotNull
AT_SetNotNull
)
type AlterTableType int
func (t AlterTableType) String() string {
switch t {
case AT_AddColumn:
return "AddColumn"
case AT_AlterColumnType:
return "AlterColumnType"
case AT_DropColumn:
return "DropColumn"
case AT_DropNotNull:
return "DropNotNull"
case AT_SetNotNull:
return "SetNotNull"
default:
return "Unknown"
}
}
type AlterTableCmd struct {
Tag NodeTag[AlterTableCmd] `json:"tag"`
Subtype AlterTableType `json:"subtype"`
Name *string `json:"name,omitempty"`
Def *ColumnDef `json:"def,omitempty"`
Newowner *RoleSpec `json:"newowner,omitempty"`
Behavior DropBehavior `json:"behavior"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterTableCmd) Pos() int {
return 0
}
func (n *AlterTableCmd) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
switch n.Subtype {
case AT_AddColumn:
buf.WriteString(" ADD COLUMN ")
case AT_DropColumn:
buf.WriteString(" DROP COLUMN ")
}
buf.astFormat(n.Def, d)
}

View File

@@ -0,0 +1,15 @@
package ast
type AlterTableMoveAllStmt struct {
Tag NodeTag[AlterTableMoveAllStmt] `json:"tag"`
OrigTablespacename *string `json:"orig_tablespacename,omitempty"`
Objtype ObjectType `json:"objtype"`
Roles *List `json:"roles,omitempty"`
NewTablespacename *string `json:"new_tablespacename,omitempty"`
Nowait bool `json:"nowait"`
}
func (n *AlterTableMoveAllStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterTableSetSchemaStmt struct {
Tag NodeTag[AlterTableSetSchemaStmt] `json:"tag"`
Table *TableName `json:"table,omitempty"`
NewSchema *string `json:"new_schema,omitempty"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterTableSetSchemaStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterTableSpaceOptionsStmt struct {
Tag NodeTag[AlterTableSpaceOptionsStmt] `json:"tag"`
Tablespacename *string `json:"tablespacename,omitempty"`
Options *List `json:"options,omitempty"`
IsReset bool `json:"is_reset"`
}
func (n *AlterTableSpaceOptionsStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,38 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type AlterTableStmt struct {
Tag NodeTag[AlterTableStmt] `json:"tag"`
// TODO: Only TableName or Relation should be defined
Relation *RangeVar `json:"relation,omitempty"`
Table *TableName `json:"table,omitempty"`
Cmds *List `json:"cmds,omitempty"`
MissingOk bool `json:"missing_ok"`
Relkind ObjectType `json:"relkind"`
// Incomplete marks a statement whose source carried syntax this node
// does not model, such as column constraints beyond plain NOT NULL and
// PRIMARY KEY on an added column. No faithful rendering exists for it.
Incomplete bool `json:"incomplete"`
}
func (n *AlterTableStmt) Pos() int {
return 0
}
func (n *AlterTableStmt) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
// An incomplete statement cannot be printed back: part of its source
// was parsed away. Render nothing, which no verification accepts, so
// the formatter keeps the statement as written.
if n.Incomplete {
return
}
buf.WriteString("ALTER TABLE ")
buf.astFormat(n.Relation, d)
buf.astFormat(n.Table, d)
buf.astFormat(n.Cmds, d)
}

View File

@@ -0,0 +1,7 @@
package ast
type AlterTableType_PG uint
func (n *AlterTableType_PG) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type AlterTSConfigType uint
func (n *AlterTSConfigType) Pos() int {
return 0
}

View File

@@ -0,0 +1,17 @@
package ast
type AlterTSConfigurationStmt struct {
Tag NodeTag[AlterTSConfigurationStmt] `json:"tag"`
Kind AlterTSConfigType `json:"kind"`
Cfgname *List `json:"cfgname,omitempty"`
Tokentype *List `json:"tokentype,omitempty"`
Dicts *List `json:"dicts,omitempty"`
Override bool `json:"override"`
Replace bool `json:"replace"`
MissingOk bool `json:"missing_ok"`
}
func (n *AlterTSConfigurationStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterTSDictionaryStmt struct {
Tag NodeTag[AlterTSDictionaryStmt] `json:"tag"`
Dictname *List `json:"dictname,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterTSDictionaryStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,16 @@
package ast
type AlterTypeAddValueStmt struct {
Tag NodeTag[AlterTypeAddValueStmt] `json:"tag"`
Type *TypeName `json:"type,omitempty"`
NewValue *string `json:"new_value,omitempty"`
NewValHasNeighbor bool `json:"new_val_has_neighbor"`
NewValNeighbor *string `json:"new_val_neighbor,omitempty"`
NewValIsAfter bool `json:"new_val_is_after"`
SkipIfNewValExists bool `json:"skip_if_new_val_exists"`
}
func (n *AlterTypeAddValueStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterTypeRenameValueStmt struct {
Tag NodeTag[AlterTypeRenameValueStmt] `json:"tag"`
Type *TypeName `json:"type,omitempty"`
OldValue *string `json:"old_value,omitempty"`
NewValue *string `json:"new_value,omitempty"`
}
func (n *AlterTypeRenameValueStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlterTypeSetSchemaStmt struct {
Tag NodeTag[AlterTypeSetSchemaStmt] `json:"tag"`
Type *TypeName `json:"type,omitempty"`
NewSchema *string `json:"new_schema,omitempty"`
}
func (n *AlterTypeSetSchemaStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type AlterUserMappingStmt struct {
Tag NodeTag[AlterUserMappingStmt] `json:"tag"`
User *RoleSpec `json:"user,omitempty"`
Servername *string `json:"servername,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *AlterUserMappingStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type AlternativeSubPlan struct {
Tag NodeTag[AlternativeSubPlan] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Subplans *List `json:"subplans,omitempty"`
}
func (n *AlternativeSubPlan) Pos() int {
return 0
}

View File

@@ -0,0 +1,19 @@
package ast
type ArrayCoerceExpr struct {
Tag NodeTag[ArrayCoerceExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
Elemfuncid Oid `json:"elemfuncid"`
Resulttype Oid `json:"resulttype"`
Resulttypmod int32 `json:"resulttypmod"`
Resultcollid Oid `json:"resultcollid"`
IsExplicit bool `json:"is_explicit"`
Coerceformat CoercionForm `json:"coerceformat"`
Location int `json:"location"`
}
func (n *ArrayCoerceExpr) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,17 @@
package ast
type ArrayExpr struct {
Tag NodeTag[ArrayExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
ArrayTypeid Oid `json:"array_typeid"`
ArrayCollid Oid `json:"array_collid"`
ElementTypeid Oid `json:"element_typeid"`
Elements *List `json:"elements,omitempty"`
Multidims bool `json:"multidims"`
Location int `json:"location"`
}
func (n *ArrayExpr) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,19 @@
package ast
type ArrayRef struct {
Tag NodeTag[ArrayRef] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Refarraytype Oid `json:"refarraytype"`
Refelemtype Oid `json:"refelemtype"`
Reftypmod int32 `json:"reftypmod"`
Refcollid Oid `json:"refcollid"`
Refupperindexpr *List `json:"refupperindexpr,omitempty"`
Reflowerindexpr *List `json:"reflowerindexpr,omitempty"`
Refexpr Node `json:"refexpr,omitempty"`
Refassgnexpr Node `json:"refassgnexpr,omitempty"`
}
func (n *ArrayRef) Pos() int {
return 0
}

View File

@@ -0,0 +1,36 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type BetweenExpr struct {
Tag NodeTag[BetweenExpr] `json:"tag"`
// Expr is the value expression to be compared.
Expr Node `json:"expr,omitempty"`
// Left is the left expression in the between statement.
Left Node `json:"left,omitempty"`
// Right is the right expression in the between statement.
Right Node `json:"right,omitempty"`
// Not is true, the expression is "not between".
Not bool `json:"not"`
Location int `json:"location"`
}
func (n *BetweenExpr) Pos() int {
return n.Location
}
func (n *BetweenExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.astFormat(n.Expr, d)
if n.Not {
buf.WriteString(" NOT BETWEEN ")
} else {
buf.WriteString(" BETWEEN ")
}
buf.astFormat(n.Left, d)
buf.WriteString(" AND ")
buf.astFormat(n.Right, d)
}

View File

@@ -0,0 +1,11 @@
package ast
type BitString struct {
Tag NodeTag[BitString] `json:"tag"`
Str string `json:"str"`
}
func (n *BitString) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type BlockIdData struct {
Tag NodeTag[BlockIdData] `json:"tag"`
BiHi uint16 `json:"bi_hi"`
BiLo uint16 `json:"bi_lo"`
}
func (n *BlockIdData) Pos() int {
return 0
}

View File

@@ -0,0 +1,70 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type BoolExpr struct {
Tag NodeTag[BoolExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Boolop BoolExprType `json:"boolop"`
Args *List `json:"args,omitempty"`
Location int `json:"location"`
}
func (n *BoolExpr) Pos() int {
return n.Location
}
func (n *BoolExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
switch n.Boolop {
case BoolExprTypeIsNull:
if items(n.Args) && len(n.Args.Items) > 0 {
buf.astFormat(n.Args.Items[0], d)
}
buf.WriteString(" IS NULL")
case BoolExprTypeIsNotNull:
if items(n.Args) && len(n.Args.Items) > 0 {
buf.astFormat(n.Args.Items[0], d)
}
buf.WriteString(" IS NOT NULL")
case BoolExprTypeNot:
// NOT expression: format as NOT <arg>
buf.WriteString("NOT ")
if items(n.Args) && len(n.Args.Items) > 0 {
buf.astFormat(n.Args.Items[0], d)
}
default:
var op string
switch n.Boolop {
case BoolExprTypeAnd:
op = "AND "
case BoolExprTypeOr:
op = "OR "
}
buf.WriteString("(")
buf.Group()
buf.Indent()
buf.Softline()
if items(n.Args) && op != "" {
first := true
for _, item := range n.Args.Items {
if _, ok := item.(*TODO); ok {
continue
}
if !first {
buf.Line()
buf.WriteString(op)
}
first = false
buf.astFormat(item, d)
}
}
buf.EndIndent()
buf.Softline()
buf.EndGroup()
buf.WriteString(")")
}
}

View File

@@ -0,0 +1,19 @@
package ast
// https://github.com/pganalyze/libpg_query/blob/13-latest/protobuf/pg_query.proto#L2783-L2789
const (
_ BoolExprType = iota
BoolExprTypeAnd
BoolExprTypeOr
BoolExprTypeNot
// Added for MySQL
BoolExprTypeIsNull
BoolExprTypeIsNotNull
)
type BoolExprType uint
func (n *BoolExprType) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type BoolTestType uint
func (n *BoolTestType) Pos() int {
return 0
}

View File

@@ -0,0 +1,28 @@
package ast
import (
"fmt"
"github.com/sqlc-dev/sqlc/internal/sql/format"
)
type Boolean struct {
Tag NodeTag[Boolean] `json:"tag"`
Boolval bool `json:"boolval"`
}
func (n *Boolean) Pos() int {
return 0
}
func (n *Boolean) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
if n.Boolval {
fmt.Fprintf(buf, "true")
} else {
fmt.Fprintf(buf, "false")
}
}

View File

@@ -0,0 +1,14 @@
package ast
type BooleanTest struct {
Tag NodeTag[BooleanTest] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
Booltesttype BoolTestType `json:"booltesttype"`
Location int `json:"location"`
}
func (n *BooleanTest) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,21 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CallStmt struct {
Tag NodeTag[CallStmt] `json:"tag"`
FuncCall *FuncCall `json:"func_call,omitempty"`
}
func (n *CallStmt) Pos() int {
if n.FuncCall == nil {
return 0
}
return n.FuncCall.Pos()
}
func (n *CallStmt) Format(buf *TrackedBuffer, d format.Dialect) {
buf.WriteString("CALL ")
buf.astFormat(n.FuncCall, d)
}

View File

@@ -0,0 +1,36 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CaseExpr struct {
Tag NodeTag[CaseExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Casetype Oid `json:"casetype"`
Casecollid Oid `json:"casecollid"`
Arg Node `json:"arg,omitempty"`
Args *List `json:"args,omitempty"`
Defresult Node `json:"defresult,omitempty"`
Location int `json:"location"`
}
func (n *CaseExpr) Pos() int {
return n.Location
}
func (n *CaseExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteString("CASE ")
if set(n.Arg) {
buf.astFormat(n.Arg, d)
buf.WriteString(" ")
}
buf.join(n.Args, d, " ")
if set(n.Defresult) {
buf.WriteString(" ELSE ")
buf.astFormat(n.Defresult, d)
}
buf.WriteString(" END")
}

View File

@@ -0,0 +1,14 @@
package ast
type CaseTestExpr struct {
Tag NodeTag[CaseTestExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
TypeId Oid `json:"type_id"`
TypeMod int32 `json:"type_mod"`
Collation Oid `json:"collation"`
}
func (n *CaseTestExpr) Pos() int {
return 0
}

View File

@@ -0,0 +1,26 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CaseWhen struct {
Tag NodeTag[CaseWhen] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Expr Node `json:"expr,omitempty"`
Result Node `json:"result,omitempty"`
Location int `json:"location"`
}
func (n *CaseWhen) Pos() int {
return n.Location
}
func (n *CaseWhen) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteString("WHEN ")
buf.astFormat(n.Expr, d)
buf.WriteString(" THEN ")
buf.astFormat(n.Result, d)
}

View File

@@ -0,0 +1,9 @@
package ast
type CheckPointStmt struct {
Tag NodeTag[CheckPointStmt] `json:"tag"`
}
func (n *CheckPointStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,11 @@
package ast
type ClosePortalStmt struct {
Tag NodeTag[ClosePortalStmt] `json:"tag"`
Portalname *string `json:"portalname,omitempty"`
}
func (n *ClosePortalStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type ClusterStmt struct {
Tag NodeTag[ClusterStmt] `json:"tag"`
Relation *RangeVar `json:"relation,omitempty"`
Indexname *string `json:"indexname,omitempty"`
Verbose bool `json:"verbose"`
}
func (n *ClusterStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type CmdType uint
func (n *CmdType) Pos() int {
return 0
}

View File

@@ -0,0 +1,28 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CoalesceExpr struct {
Tag NodeTag[CoalesceExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Coalescetype Oid `json:"coalescetype"`
Coalescecollid Oid `json:"coalescecollid"`
Args *List `json:"args,omitempty"`
Location int `json:"location"`
}
func (n *CoalesceExpr) Pos() int {
return n.Location
}
func (n *CoalesceExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
// Lower case, like every other function name: the printer upper-cases
// keywords, and function names are identifiers, which fold lower.
buf.WriteString("coalesce(")
buf.astFormat(n.Args, d)
buf.WriteString(")")
}

View File

@@ -0,0 +1,17 @@
package ast
type CoerceToDomain struct {
Tag NodeTag[CoerceToDomain] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
Resulttype Oid `json:"resulttype"`
Resulttypmod int32 `json:"resulttypmod"`
Resultcollid Oid `json:"resultcollid"`
Coercionformat CoercionForm `json:"coercionformat"`
Location int `json:"location"`
}
func (n *CoerceToDomain) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,15 @@
package ast
type CoerceToDomainValue struct {
Tag NodeTag[CoerceToDomainValue] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
TypeId Oid `json:"type_id"`
TypeMod int32 `json:"type_mod"`
Collation Oid `json:"collation"`
Location int `json:"location"`
}
func (n *CoerceToDomainValue) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,16 @@
package ast
type CoerceViaIO struct {
Tag NodeTag[CoerceViaIO] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
Resulttype Oid `json:"resulttype"`
Resultcollid Oid `json:"resultcollid"`
Coerceformat CoercionForm `json:"coerceformat"`
Location int `json:"location"`
}
func (n *CoerceViaIO) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,7 @@
package ast
type CoercionContext uint
func (n *CoercionContext) Pos() int {
return 0
}

View File

@@ -0,0 +1,7 @@
package ast
type CoercionForm uint
func (n *CoercionForm) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type CollateClause struct {
Tag NodeTag[CollateClause] `json:"tag"`
Arg Node `json:"arg,omitempty"`
Collname *List `json:"collname,omitempty"`
Location int `json:"location"`
}
func (n *CollateClause) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,25 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CollateExpr struct {
Tag NodeTag[CollateExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
CollOid Oid `json:"coll_oid"`
Location int `json:"location"`
}
func (n *CollateExpr) Pos() int {
return n.Location
}
func (n *CollateExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.astFormat(n.Xpr, d)
buf.WriteString(" COLLATE ")
buf.astFormat(n.Arg, d)
}

View File

@@ -0,0 +1,72 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type ColumnDef struct {
Tag NodeTag[ColumnDef] `json:"tag"`
Colname string `json:"colname"`
// TypeName is the column's type for the catalog. When Typeless is set
// the author wrote no type at all — SQLite allows it — and the column
// prints without one, whatever TypeName carries.
TypeName *TypeName `json:"type_name,omitempty"`
Typeless bool `json:"typeless"`
IsNotNull bool `json:"is_not_null"`
IsUnsigned bool `json:"is_unsigned"`
IsArray bool `json:"is_array"`
ArrayDims int `json:"array_dims"`
Vals *List `json:"vals,omitempty"`
Length *int `json:"length,omitempty"`
PrimaryKey bool `json:"primary_key"`
// IsHidden marks a column a relation offers by name without listing it,
// like the column an sqlite fts5 table names after itself. The legacy
// catalog drops hidden columns; the core catalog keeps them out of star
// expansions and models.
IsHidden bool `json:"is_hidden"`
// From pg.ColumnDef
Inhcount int `json:"inhcount"`
IsLocal bool `json:"is_local"`
IsFromType bool `json:"is_from_type"`
IsFromParent bool `json:"is_from_parent"`
Storage byte `json:"storage"`
RawDefault Node `json:"raw_default,omitempty"`
CookedDefault Node `json:"cooked_default,omitempty"`
Identity byte `json:"identity"`
CollClause *CollateClause `json:"coll_clause,omitempty"`
CollOid Oid `json:"coll_oid"`
Constraints *List `json:"constraints,omitempty"`
Fdwoptions *List `json:"fdwoptions,omitempty"`
Location int `json:"location"`
Comment string `json:"comment"`
}
func (n *ColumnDef) Pos() int {
return n.Location
}
func (n *ColumnDef) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
buf.WriteString(n.Colname)
if !n.Typeless {
buf.WriteString(" ")
buf.astFormat(n.TypeName, d)
// MySQL integer types: signedness is part of the type.
if n.IsUnsigned {
buf.WriteString(" unsigned")
}
}
// Use IsArray from ColumnDef since TypeName.ArrayBounds may not be set
// (for type resolution compatibility)
if n.IsArray && !items(n.TypeName.ArrayBounds) {
buf.WriteString("[]")
}
if n.PrimaryKey {
buf.WriteString(" PRIMARY KEY")
} else if n.IsNotNull {
buf.WriteString(" NOT NULL")
}
buf.astFormat(n.Constraints, d)
}

View File

@@ -0,0 +1,40 @@
package ast
import (
"strings"
"github.com/sqlc-dev/sqlc/internal/sql/format"
)
type ColumnRef struct {
Tag NodeTag[ColumnRef] `json:"tag"`
Name string `json:"name"`
// From pg.ColumnRef
Fields *List `json:"fields,omitempty"`
Location int `json:"location"`
}
func (n *ColumnRef) Pos() int {
return n.Location
}
func (n *ColumnRef) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
if n.Fields != nil {
var items []string
for _, item := range n.Fields.Items {
switch nn := item.(type) {
case *String:
items = append(items, d.QuoteIdent(nn.Str))
case *A_Star:
items = append(items, "*")
}
}
buf.WriteString(strings.Join(items, "."))
}
}

235
internal/sql/ast/comment.go Normal file
View File

@@ -0,0 +1,235 @@
package ast
import (
"sort"
"github.com/sqlc-dev/sqlc/internal/sql/format"
)
// File is a parsed query file: its statements together with the comments
// the parser's lexer saw. Engines whose parsers surface their trivia — the
// lexical channel of whitespace and comments the grammar never sees, in
// Roslyn's terminology — return it from ParseFile, so the formatter gets
// statements and comments from one lexer pass.
type File struct {
Stmts []Statement
Comments []Comment
}
// Comment is a single SQL comment, positioned by byte offsets into the
// source the statement was parsed from — the same coordinates the engine
// parsers stamp on nodes.
type Comment struct {
// Text is the comment as written, marker included ("-- x", "/* x */").
Text string
Start int
End int
// OwnLine reports that only blank space preceded the comment on its
// line, so it leads the code after it rather than trailing the code
// before it.
OwnLine bool
}
// Line reports whether the comment runs to the end of its line (-- or #),
// so nothing may be printed after it on the same line.
func (c Comment) Line() bool {
return len(c.Text) < 2 || c.Text[0] != '/' || c.Text[1] != '*'
}
// commentRec is one attached comment: the anchor nodes on either side of it
// in print order, and whether it trails the code before it (same source
// line) or leads the code after it (its own line).
//
// The next anchor is where the printer emits the comment; the prev anchor
// is the node the comment belongs to when the tree is edited — a trailing
// comment travels with the node it annotates, the way dave/dst attaches
// decorations in the Go ecosystem.
type commentRec struct {
c Comment
prev, next Node
trailing bool
}
// CommentTable holds a statement's comments attached to its nodes. It is
// built once by AttachComments, before any printing (or, later, editing):
// each comment is classified against the statement's anchor nodes by source
// position and line, and from then on positions are never consulted again —
// the printer emits comments by node identity, which is what lets an edited
// or synthetic tree print its comments correctly.
type CommentTable struct {
recs []commentRec
// byNext indexes recs by their next anchor, in source order; end holds
// the comments with no anchor after them, which trail the statement.
byNext map[Node][]int
end []int
taken []bool
nTaken int
// breaks marks the emission points where the author broke the line:
// the printer keeps those breaks, gofmt-style, instead of imposing a
// line width.
breaks map[Node]bool
}
// breakAt reports whether the author broke the line at this emission point.
func (t *CommentTable) breakAt(n Node) bool {
return t != nil && t.breaks[n]
}
// Exhausted reports whether every attached comment was printed.
func (t *CommentTable) Exhausted() bool {
return t == nil || t.nTaken == len(t.recs)
}
// take returns and consumes the comments anchored to n, in order.
func (t *CommentTable) take(n Node) []commentRec {
if t == nil || n == nil {
return nil
}
idxs := t.byNext[n]
if len(idxs) == 0 {
return nil
}
out := make([]commentRec, 0, len(idxs))
for _, i := range idxs {
if t.taken[i] {
continue
}
t.taken[i] = true
t.nTaken++
out = append(out, t.recs[i])
}
return out
}
// takeRemaining returns and consumes every comment not yet printed; the
// statement is over, so everything left trails it.
func (t *CommentTable) takeRemaining() []commentRec {
if t == nil {
return nil
}
var out []commentRec
for i := range t.recs {
if t.taken[i] {
continue
}
t.taken[i] = true
t.nTaken++
out = append(out, t.recs[i])
}
return out
}
// AttachComments classifies a statement's interior comments against its
// nodes, producing the table the printer (and any future rewriting tool)
// works from. Placement follows gofmt's rules, decided here once from
// source positions and lines: a comment on the same line as the code before
// it trails that code; any other comment leads the first node printed after
// it; a comment after the last node trails the statement.
func AttachComments(raw *RawStmt, d format.Dialect, comments []Comment, src string) *CommentTable {
anchors := collectAnchors(raw, d)
lines := []int{0}
for i := 0; i < len(src); i++ {
if src[i] == '\n' {
lines = append(lines, i+1)
}
}
lineOf := func(pos int) int {
return sort.SearchInts(lines, pos+1) - 1
}
table := &CommentTable{byNext: make(map[Node][]int, len(anchors))}
for _, c := range comments {
// prev: the printed node with the greatest position before the
// comment (for the trailing/leading call). next: the emission point
// for the first printed node after the comment — the node itself,
// or, when boundary markers immediately precede it in print order,
// the earliest of those markers, which is where the printer will
// look for this comment first.
var prev, next Node
prevPos := -1
for _, a := range anchors {
if !a.marker && a.pos < c.Start && a.pos > prevPos {
prev, prevPos = a.node, a.pos
}
}
for i, a := range anchors {
if a.marker || a.pos <= c.Start {
continue
}
j := i
for j > 0 && anchors[j-1].marker {
j--
}
next = anchors[j].node
break
}
rec := commentRec{
c: c,
prev: prev,
next: next,
trailing: !c.OwnLine && prev != nil && lineOf(prevPos) == lineOf(c.Start),
}
table.recs = append(table.recs, rec)
i := len(table.recs) - 1
if next == nil {
table.end = append(table.end, i)
} else {
table.byNext[next] = append(table.byNext[next], i)
}
}
table.taken = make([]bool, len(table.recs))
// Record where the author broke the line: an emission point whose
// neighbouring printed nodes sit on different source lines. The printer
// keeps these breaks (and only imposes new ones for comments), so the
// statement's shape stays the author's.
table.breaks = make(map[Node]bool)
for i, a := range anchors {
if !a.marker {
continue
}
prevPos, nextPos := -1, -1
for j := i - 1; j >= 0; j-- {
if !anchors[j].marker {
prevPos = anchors[j].pos
break
}
}
for j := i + 1; j < len(anchors); j++ {
if !anchors[j].marker {
nextPos = anchors[j].pos
break
}
}
if prevPos >= 0 && nextPos >= 0 && lineOf(prevPos) != lineOf(nextPos) {
table.breaks[a.node] = true
}
}
return table
}
type anchor struct {
node Node
pos int
// marker anchors are emission points (beforeClause, list boundaries)
// rather than printed nodes; they carry no position of their own.
marker bool
}
// collectAnchors renders the statement once, flat, recording every
// positioned node in the order the printer visits them. Print order is what
// comment emission is defined against, so classifying against it keeps the
// attach-time decision and the print-time emission point identical.
func collectAnchors(n Node, d format.Dialect) (out []anchor) {
defer func() {
// A formatter panic here surfaces later, on the real print; anchors
// collected so far still place most comments.
recover()
}()
tb := NewTrackedBuffer()
tb.anchors = &out
if ft, ok := n.(nodeFormatter); ok {
ft.Format(tb, d)
}
return out
}

View File

@@ -0,0 +1,13 @@
package ast
type CommentOnColumnStmt struct {
Tag NodeTag[CommentOnColumnStmt] `json:"tag"`
Table *TableName `json:"table,omitempty"`
Col *ColumnRef `json:"col,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentOnColumnStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type CommentOnSchemaStmt struct {
Tag NodeTag[CommentOnSchemaStmt] `json:"tag"`
Schema *String `json:"schema,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentOnSchemaStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type CommentOnTableStmt struct {
Tag NodeTag[CommentOnTableStmt] `json:"tag"`
Table *TableName `json:"table,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentOnTableStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type CommentOnTypeStmt struct {
Tag NodeTag[CommentOnTypeStmt] `json:"tag"`
Type *TypeName `json:"type,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentOnTypeStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,12 @@
package ast
type CommentOnViewStmt struct {
Tag NodeTag[CommentOnViewStmt] `json:"tag"`
View *TableName `json:"view,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentOnViewStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type CommentStmt struct {
Tag NodeTag[CommentStmt] `json:"tag"`
Objtype ObjectType `json:"objtype"`
Object Node `json:"object,omitempty"`
Comment *string `json:"comment,omitempty"`
}
func (n *CommentStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,49 @@
package ast
import "github.com/sqlc-dev/sqlc/internal/sql/format"
type CommonTableExpr struct {
Tag NodeTag[CommonTableExpr] `json:"tag"`
Ctename *string `json:"ctename,omitempty"`
Aliascolnames *List `json:"aliascolnames,omitempty"`
Ctequery Node `json:"ctequery,omitempty"`
Location int `json:"location"`
Cterecursive bool `json:"cterecursive"`
Cterefcount int `json:"cterefcount"`
Ctecolnames *List `json:"ctecolnames,omitempty"`
Ctecoltypes *List `json:"ctecoltypes,omitempty"`
Ctecoltypmods *List `json:"ctecoltypmods,omitempty"`
Ctecolcollations *List `json:"ctecolcollations,omitempty"`
}
func (n *CommonTableExpr) Pos() int {
return n.Location
}
func (n *CommonTableExpr) Format(buf *TrackedBuffer, d format.Dialect) {
if n == nil {
return
}
if n.Ctename != nil {
buf.WriteString(*n.Ctename)
}
if items(n.Aliascolnames) {
buf.WriteString("(")
buf.join(n.Aliascolnames, d, ", ")
buf.WriteString(")")
}
buf.WriteString(" AS (")
buf.Group()
buf.Indent()
// The body's boundary: a comment at the top of the CTE prints here, and
// an author who broke the line after the parenthesis keeps the CTE — and
// therefore the statement around it — broken open.
buf.boundary(n.Ctequery)
buf.Softline()
buf.astFormat(n.Ctequery, d)
buf.EndIndent()
buf.Softline()
buf.EndGroup()
buf.WriteString(")")
}

View File

@@ -0,0 +1,13 @@
package ast
type CompositeTypeStmt struct {
Tag NodeTag[CompositeTypeStmt] `json:"tag"`
TypeName *TypeName `json:"type_name,omitempty"`
// Coldeflist is the type's fields, each a ColumnDef.
Coldeflist *List `json:"coldeflist,omitempty"`
}
func (n *CompositeTypeStmt) Pos() int {
return 0
}

19
internal/sql/ast/const.go Normal file
View File

@@ -0,0 +1,19 @@
package ast
type Const struct {
Tag NodeTag[Const] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Consttype Oid `json:"consttype"`
Consttypmod int32 `json:"consttypmod"`
Constcollid Oid `json:"constcollid"`
Constlen int `json:"constlen"`
Constvalue Datum `json:"constvalue"`
Constisnull bool `json:"constisnull"`
Constbyval bool `json:"constbyval"`
Location int `json:"location"`
}
func (n *Const) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,14 @@
package ast
type ConstrType uint
func (n *ConstrType) Pos() int {
return 0
}
// The constraint kinds the analysis reads, numbered as PostgreSQL's parser
// numbers them, which is what an engine's converter records.
const (
ConstrTypeNull ConstrType = 1
ConstrTypeNotNull ConstrType = 2
)

View File

@@ -0,0 +1,36 @@
package ast
type Constraint struct {
Tag NodeTag[Constraint] `json:"tag"`
Contype ConstrType `json:"contype"`
Conname *string `json:"conname,omitempty"`
Deferrable bool `json:"deferrable"`
Initdeferred bool `json:"initdeferred"`
Location int `json:"location"`
IsNoInherit bool `json:"is_no_inherit"`
RawExpr Node `json:"raw_expr,omitempty"`
CookedExpr *string `json:"cooked_expr,omitempty"`
GeneratedWhen byte `json:"generated_when"`
Keys *List `json:"keys,omitempty"`
Exclusions *List `json:"exclusions,omitempty"`
Options *List `json:"options,omitempty"`
Indexname *string `json:"indexname,omitempty"`
Indexspace *string `json:"indexspace,omitempty"`
AccessMethod *string `json:"access_method,omitempty"`
WhereClause Node `json:"where_clause,omitempty"`
Pktable *RangeVar `json:"pktable,omitempty"`
FkAttrs *List `json:"fk_attrs,omitempty"`
PkAttrs *List `json:"pk_attrs,omitempty"`
FkMatchtype byte `json:"fk_matchtype"`
FkUpdAction byte `json:"fk_upd_action"`
FkDelAction byte `json:"fk_del_action"`
OldConpfeqop *List `json:"old_conpfeqop,omitempty"`
OldPktableOid Oid `json:"old_pktable_oid"`
SkipValidation bool `json:"skip_validation"`
InitiallyValid bool `json:"initially_valid"`
}
func (n *Constraint) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,12 @@
package ast
type ConstraintsSetStmt struct {
Tag NodeTag[ConstraintsSetStmt] `json:"tag"`
Constraints *List `json:"constraints,omitempty"`
Deferred bool `json:"deferred"`
}
func (n *ConstraintsSetStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type ConvertRowtypeExpr struct {
Tag NodeTag[ConvertRowtypeExpr] `json:"tag"`
Xpr Node `json:"xpr,omitempty"`
Arg Node `json:"arg,omitempty"`
Resulttype Oid `json:"resulttype"`
Convertformat CoercionForm `json:"convertformat"`
Location int `json:"location"`
}
func (n *ConvertRowtypeExpr) Pos() int {
return n.Location
}

View File

@@ -0,0 +1,17 @@
package ast
type CopyStmt struct {
Tag NodeTag[CopyStmt] `json:"tag"`
Relation *RangeVar `json:"relation,omitempty"`
Query Node `json:"query,omitempty"`
Attlist *List `json:"attlist,omitempty"`
IsFrom bool `json:"is_from"`
IsProgram bool `json:"is_program"`
Filename *string `json:"filename,omitempty"`
Options *List `json:"options,omitempty"`
}
func (n *CopyStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,13 @@
package ast
type CreateAmStmt struct {
Tag NodeTag[CreateAmStmt] `json:"tag"`
Amname *string `json:"amname,omitempty"`
HandlerName *List `json:"handler_name,omitempty"`
Amtype byte `json:"amtype"`
}
func (n *CreateAmStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type CreateCastStmt struct {
Tag NodeTag[CreateCastStmt] `json:"tag"`
Sourcetype *TypeName `json:"sourcetype,omitempty"`
Targettype *TypeName `json:"targettype,omitempty"`
Func *ObjectWithArgs `json:"func,omitempty"`
Context CoercionContext `json:"context"`
Inout bool `json:"inout"`
}
func (n *CreateCastStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,15 @@
package ast
type CreateConversionStmt struct {
Tag NodeTag[CreateConversionStmt] `json:"tag"`
ConversionName *List `json:"conversion_name,omitempty"`
ForEncodingName *string `json:"for_encoding_name,omitempty"`
ToEncodingName *string `json:"to_encoding_name,omitempty"`
FuncName *List `json:"func_name,omitempty"`
Def bool `json:"def"`
}
func (n *CreateConversionStmt) Pos() int {
return 0
}

View File

@@ -0,0 +1,14 @@
package ast
type CreateDomainStmt struct {
Tag NodeTag[CreateDomainStmt] `json:"tag"`
Domainname *List `json:"domainname,omitempty"`
TypeName *TypeName `json:"type_name,omitempty"`
CollClause *CollateClause `json:"coll_clause,omitempty"`
Constraints *List `json:"constraints,omitempty"`
}
func (n *CreateDomainStmt) Pos() int {
return 0
}

Some files were not shown because too many files have changed in this diff Show More