Help us shape the future of Go! Take the 2026 Go Developer Survey

sqlglot

package
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package sqlglot is a Go port of sqlglot's expression model and parser, scoped to what a read-only SQL guard needs first and growing outward.

The architecture -- a tokenizer, a data-driven parser over dispatch tables, an expression tree in which every construct is a visible node, and dialects as override sets -- is Toby Mao's, from github.com/tobymao/sqlglot (MIT). See NOTICE for the reference commit this port is measured against.

Index

Constants

This section is empty.

Variables

View Source
var ErrGenerate = errors.New("sqlglot-go: cannot generate SQL")

ErrGenerate reports a tree the generator cannot write.

A guard that rewrote a statement and then emitted something subtly different would be worse than one that refused, so an unknown node stops the rewrite rather than producing an approximation.

View Source
var ErrMultipleStatements = errors.New("sqlglot-go: more than one statement")

ErrMultipleStatements reports more than one statement in the input. A guard that permitted the first and ignored the rest would be no guard at all.

View Source
var ErrNotAQuery = errors.New("sqlglot-go: not a query")

ErrNotAQuery reports a statement the port recognises but does not parse: DDL and DML, which a read-only guard refuses on sight.

It is deliberately distinct from ErrUnsupported. The guard above this parser has to refuse `DROP TABLE t` for being a write, not for being unreadable -- the conformance suite checks the reason -- and naming the statement is all that takes. Building the tree would be work with no consumer: nothing here will ever execute one.

View Source
var ErrUnsupported = errors.New("sqlglot-go: unsupported statement")

ErrUnsupported reports a statement the port cannot parse yet.

It is deliberately distinct from a syntax error. The guard above this parser turns it into a refusal, and the differential harness counts it as a gap in coverage -- never as a match. A construct the port does not understand must never become a tree that merely looks plausible.

Functions

func AnnotateFully

func AnnotateFully(e *Expression, dialect string) bool

AnnotateFully stamps a type on every node of a subtree and reports whether every one of them had an answer.

It MUTATES, and that is the point: the reference's annotations are part of the tree it produces -- they are dumped, and the differential compares them -- so a port that computed a type without recording it would agree about the answer and disagree about the tree.

Where it returns false the port met a node it has no rule for. A caller that stamped UNKNOWN there would be recording its own ignorance as the reference's answer, which is why the subscript rule refuses instead.

func Anonymize

func Anonymize(sql, dialect string) string

Anonymize replaces sensitive tokens in sql -- identifiers, strings, numbers -- with fixed-width, length-preserving, consistent aliases (the same alias for every occurrence of the same original text), and blanks comment bodies and hint bodies, matching the reference's own sqlglot.anonymize.anonymize + render run together. The result is always the same LENGTH as sql: every alias is fitted back into the exact span its original token held, and the gaps between tokens keep their whitespace and comment markers with the comment BODY blanked.

Never fails: a statement the tokenizer cannot finish still comes back -- with a synthetic tail standing in for whatever it could not read -- since this exists to make SQL safe to log even when it is broken.

func CanonicalSQL

func CanonicalSQL(sql, dialect string) (string, error)

CanonicalSQL is the definition written back from its tree, with the differences that do not change what a routine is taken out: comments, whitespace, keyword case and brackets round a name that reads the same without them all vanish; CREATE OR ALTER becomes CREATE, PROC becomes PROCEDURE, and a parameter's OUT becomes OUTPUT. String literals are kept exactly as written. A definition that does not parse is an error -- there is no textual fallback.

func Dialects

func Dialects() []string

Dialects lists the dialects the port configures, neutral first, the rest alphabetically -- every key dialectConfigs actually has, not a fixed list from when there were five of them, which silently stopped growing while the dialects landing here kept going.

func Format

func Format(sql, dialect string) (string, error)

Format lays a statement out one clause per line, with subqueries indented.

It works on tokens rather than on a parse, so a fragment the parser refuses still formats. It only ever moves whitespace: every token keeps the text it was written with, keyword case included, and comments stay where they stood. A statement the tokenizer cannot lex is an error.

func FormatScript added in v1.0.4

func FormatScript(sql, dialect string) (string, error)

FormatScript keeps existing line boundaries while formatting clauses.

func FunctionName

func FunctionName(e *Expression, dialect string) (string, bool)

FunctionName is the name a function node is written with in a dialect, and whether the node is a function at all.

It exists for the layer above: a guard denying OPENROWSET or xp_cmdshell has to ask what a node is called, and the answer is not on the node -- a Count does not remember that it was written COUNT, and in T-SQL with big_int set it is written COUNT_BIG.

func Generate

func Generate(e *Expression, dialect string) (string, error)

Generate renders a tree as SQL in a dialect.

func IsIntegerType

func IsIntegerType(t *Expression) bool

IsIntegerType reports whether a stamped type is one the reference counts as an integer -- which is what decides whether a subscript's index is shifted.

func IsWrite

func IsWrite(e *Expression) bool

IsWrite reports whether a parsed statement changes anything.

The guard above this port used to learn that from ErrNotAQuery, which said "this is a write" only because a write could not be READ. Now that they can be read, the fact has to be asked for, and a caller that keeps using the error will see a CREATE go past as though it were a query.

The class of the root is not enough. SELECT … INTO is still a Select, and a write can sit under a UNION, a CTE or a DESCRIBE. Walking is the same answer the first consumer already has to give; asking only the root lets those through. INTO on a Pivot or Unpivot names a column, not a table, so only a Select's into -- and a Show's into_outfile -- count as a write slot.

func SplitBatches

func SplitBatches(script, dialect string) ([]string, error)

SplitBatches splits a script into its batches at each GO that stands alone on its line, as sqlcmd and SSMS do. A GO inside a string or a comment is text, not a separator, because the script is tokenized rather than scanned by line. Batches holding nothing but whitespace and comments are dropped.

`GO <count>`, which runs the batch before it that many times, is refused: a definition run twice is not a definition. So is anything else after GO on its line.

Types

type Arg

type Arg struct {
	Key   string
	Value any
}

Arg is one named argument of a node, in the order it was given.

type BooleanSQL

type BooleanSQL struct {
	TrueValue      string
	FalseValue     string
	TrueCondition  string
	FalseCondition string
}

ClassTrigger names the classes that, at Index, make a builder produce something other than what the probe recorded. SyntaxTemplate is how one shape of a syntax function is written, with {key} where each argument goes. BooleanSQL is how a boolean is written in each position.

type ClassTrigger

type ClassTrigger struct {
	Index   int
	Classes []string
}

type CompositeTypeSQL

type CompositeTypeSQL struct {
	ArrayTemplate      string
	ArraySizedTemplate string
	StructOpen         string
	StructClose        string
	StructFieldSep     string
	// MapTemplate is set only where MAP does NOT reuse the struct
	// delimiters -- Materialize's own `MAP[key => value]` -- and is
	// empty everywhere else, where MAP falls through to StructOpen/
	// StructClose like every other nested type.
	MapTemplate string
}

CompositeTypeSQL is how a type that contains another type is written. The array form is a TEMPLATE rather than a pair of delimiters because it is not the same shape everywhere: DuckDB suffixes (`INT[]`) and Databricks wraps (`ARRAY<INT>`).

type Config

type Config struct {
	Name          string
	SingleTokens  map[string]TokenType
	Keywords      map[string]TokenType
	Quotes        map[string]string
	FormatStrings map[string]FormatString
	Identifiers   map[string]string
	// Comments maps a start delimiter to its end. An empty end means the
	// comment runs to end of line -- the reference stores None there.
	Comments      map[string]string
	StringEscapes set
	// StringEscapePreferred is STRING_ESCAPES[0] -- the reference's generator
	// always escapes a quote with the FIRST listed escape, not whichever one
	// a set's own iteration happens to visit first. Redshift reads both `\`
	// and `'` as escapes but writes only `\`; StringEscapes alone cannot
	// tell the two apart once order is gone, which is why this exists
	// alongside it rather than replacing it.
	StringEscapePreferred            string
	ByteStringEscapes                set
	IdentifierEscapes                set
	EscapeFollowChars                set
	Commands                         ttset
	CommandPrefixTokens              ttset
	NestedComments                   bool
	HintStart                        string
	TokensPrecedingHint              ttset
	HasBitStrings                    bool
	HasHexStrings                    bool
	NumericLiterals                  map[string]string
	VarSingleTokens                  set
	StringEscapesAllowedInRawStrings bool
	HeredocTagIsIdentifier           bool
	HeredocStringAlternative         TokenType
	NumbersCanBeUnderscoreSeparated  bool
	NumbersCanHaveDecimals           bool
	IdentifiersCanStartWithDigit     bool
	UnescapedSequences               map[string]string

	// Tables is the parser's vocabulary for this dialect.
	Tables *ParserTables
	// contains filtered or unexported fields
}

Config is one dialect's resolved tokenizer tables. The fields mirror the reference's TokenizerCore constructor one-for-one; dialects_gen.go fills them from the pinned reference so a dialect override cannot be missed by hand.

func ConfigFor

func ConfigFor(dialect string) (*Config, bool)

ConfigFor returns the tokenizer configuration for a dialect. The empty name is the dialect-neutral default, matching the reference's `read=None`.

type DataTypeKind

type DataTypeKind string

DataTypeKind is the reference's DataType.Type enum member, which dumps as its own record class rather than as a plain leaf.

type DataTypeSpec

type DataTypeSpec struct {
	Kind string // dialect-preserving kind: NVARCHAR, DECIMAL, UUID, REAL, NTEXT, ...
	Name string // as the dialect writes it: NUMERIC for DECIMAL, UNIQUEIDENTIFIER for UUID
	Max  bool   // (MAX)
	// Length is a string or binary type's size; Precision and Scale a
	// number's, and Scale alone a time's fractional seconds -- DATETIME2(7).
	Length, Precision, Scale *int
}

DataTypeSpec is a data type taken apart: its kind, and what its parameters say. A size the type was not written with is nil.

func DataTypeSpecOf

func DataTypeSpecOf(e *Expression, dialect string) (DataTypeSpec, error)

DataTypeSpecOf takes a DataType node apart, naming it as the dialect writes it.

func (DataTypeSpec) String

func (s DataTypeSpec) String() string

type DiffEdit

type DiffEdit struct {
	Kind       EditKind
	Expression *Expression
	Source     *Expression
	Target     *Expression
}

DiffEdit is one entry in an edit script -- the reference's Insert, Remove, Move, Update and Keep dataclasses collapsed into one shape. Insert and Remove carry only Expression; Move, Update and Keep carry Source and Target, the two ends of what got matched.

func Diff

func Diff(source, target *Expression, dialect string, deltaOnly bool) []DiffEdit

Diff returns the edit script that turns source into target: the reference's own `sqlglot.diff.diff`, the Change Distiller algorithm (Fluri & Pinzger, building on Chawathe et al.) run over two already-parsed trees. Both trees are copied first, so the ones passed in are never mutated.

deltaOnly excludes Keep edits, which is what the reference's own tests hold the algorithm to -- and what a caller comparing two versions of a query actually wants: everything that agreed contributes nothing to read.

type EditKind

type EditKind string

EditKind names which change a DiffEdit represents.

const (
	EditInsert EditKind = "Insert"
	EditRemove EditKind = "Remove"
	EditMove   EditKind = "Move"
	EditUpdate EditKind = "Update"
	EditKeep   EditKind = "Keep"
)

type Expression

type Expression struct {
	// Class is the reference's node class name, e.g. "Select", "Table",
	// "Identifier". Kept as the Python spelling so dumps compare directly.
	Class string
	// Args are the node's named children and leaf values, in the reference's
	// key names ("this", "expressions", "from_", "db", "quoted", …). Keys
	// records insertion order, because the reference's dump order depends on
	// it and so the comparison does too.
	Args map[string]any
	Keys []string
	// Type is the node's type annotation. Most nodes have none, but a cast
	// carries the type it casts to, and the reference dumps it as its own
	// nested record list under "t" -- so a port that ignored it would mismatch
	// on every cast.
	Type *Expression
	// Parent is set by the parser; never serialised.
	Parent *Expression
	// Meta is the reference's `meta`: facts about how a node was WRITTEN
	// rather than what it is -- "name", the spelling a function was read
	// with, in a dialect that writes it back that way. The dump's "m" is
	// metadata and not compared, so this never affects tree equality.
	Meta map[string]any
	// contains filtered or unexported fields
}

Expression is one node of a parsed statement.

It mirrors sqlglot's Expression closely enough that a tree dumped by the Python reference and a tree built here can be compared field by field: a class name, and named args each holding a child, a list of children, or a leaf value. That comparability is the whole verification strategy, so the shape is not negotiable -- where this port disagrees with the reference, the port is wrong until shown otherwise.

func Annotate

func Annotate(e *Expression, dialect string) *Expression

Annotate answers what TYPE an expression has.

This is the reference's `annotate_types`, and it is what several of the port's refusals are actually waiting on: whether a subscript's index is an integer, whether a connector's operand is already a condition, whether REGEXP_REPLACE's last argument is text. Each of those is a question about a type, and the port has been refusing rather than guessing.

SCOPE-FREE only. The reference resolves a column through the optimizer's scope machinery to a table to a schema; none of that is ported, so a column annotates as UNKNOWN here rather than being guessed at. That is not a crippling limit for the callers above -- a literal, a cast and an operator over them all have local answers, which is most of what the refusals need.

A nil result means "no answer", which callers must treat as UNKNOWN rather than as any particular type.

func ApplyIndexOffset

func ApplyIndexOffset(this *Expression, items []*Expression, offset int, dialect string) ([]*Expression, bool)

ApplyIndexOffset shifts a subscript between the dialect's numbering and sqlglot's 0-based Bracket. The PARSER subtracts the offset and the GENERATOR adds it back, which is why both call this.

Almost all of it is conditions rather than arithmetic: it fires only for a single index, only where the base is UNKNOWN or an ARRAY, and only where the index is an INTEGER. So `a[x]`, `a['k']` and `a[1:2]` keep the index they were written with, and gain only the type annotations the reference stamps on them while deciding not to shift.

It reports false where the port cannot type the subtree. Its annotator is narrower than the reference's, and stamping UNKNOWN where the reference infers a type would record the port's own ignorance as an answer.

func New

func New(class string, args ...Arg) *Expression

New builds a node and wires its children's Parent pointers.

Arguments are variadic pairs rather than a map because the reference's dump order follows arg declaration order, and Go map iteration is randomised -- a map here would make the dump differ run to run and the differential comparison flap.

func ParseDataType

func ParseDataType(s, dialect string) (*Expression, error)

ParseDataType reads a bare data type -- `nvarchar(max)`, `decimal(18,2)` -- as a column definition would. Anything after the type is an error.

func ParseJSONPath

func ParseJSONPath(path string) (*Expression, error)

ParseJSONPath parses a bare JSONPath selector the way the reference's `sqlglot.jsonpath.parse` does for its own base dialect: no dialect-specific tokenizer override, so `-` is never a var character.

func ParseOne

func ParseOne(sql, dialect string) (*Expression, error)

ParseOne parses a single statement in a dialect and returns its tree.

func Simplify

func Simplify(e *Expression, dialect string) *Expression

Simplify rewrites a tree the way the reference's optimizer does.

This is the first thing in the port that CHANGES a tree rather than reproducing one. That distinction is the reason the execution oracle exists: every other harness here compares the port against sqlglot, which cannot tell a wrong-but-plausible rewrite from a right one -- the SQL still parses, still round-trips, and still runs. It just returns different rows.

The rule is conservative throughout: where a rule's precondition cannot be established, the node is LEFT ALONE. A rewrite the port declines to make shows up as a statement it did not simplify far enough, which the gate counts and which costs nothing. A rewrite it makes wrongly is a different question asked of the database.

The reference runs `annotate_types` before simplifying and several of its rules decide on the types that pass leaves behind. Those rules are not here yet, and the nodes they would touch are left alone rather than guessed at.

func (*Expression) Append

func (e *Expression) Append(key string, child *Expression)

Append adds one child to a list-valued arg, creating it if absent -- the reference's `append`. The key takes its position on first use, which is what puts joins immediately after from_ in the dump.

func (*Expression) Copy

func (e *Expression) Copy() *Expression

Copy returns a deep copy of the node.

Simplify rewrites trees, and rewriting one in place would mutate the tree the differential holds -- the parser's output is compared against the reference before and after. So a rewrite builds a new tree and the original is left as it was parsed.

func (*Expression) Dump

func (e *Expression) Dump() []map[string]any

Dump renders the tree in the reference's `serde.dump()` format, so a Go tree and a Python tree compare as the SAME kind of value.

The format is a flat list of records in pre-order. Each record carries the node's class ("c"), the index of its parent ("i"), the arg key it sits under ("k"), and "a": true when it is one element of a list. A LEAF value is its own record with "v" -- not a field on its parent. Order follows the reference's iterative algorithm exactly: children are pushed onto a stack in reverse so they pop in declaration order, which means arg order is part of the contract and Args is kept as an ordered sequence rather than a map.

Two reference fields are deliberately not produced: "m" (source positions) and "o" (comments). They are metadata, not structure, and the comparison in the harness strips them from the reference before diffing.

func (*Expression) DumpJSON

func (e *Expression) DumpJSON() []byte

DumpJSON is Dump as bytes, for a test's diff output.

func (*Expression) Equal

func (e *Expression) Equal(other *Expression) bool

Equal reports whether two nodes are the same tree.

It compares what the DUMP compares -- class, arg keys in order, and values -- because that is what the reference is held to. Parent pointers are excluded; they describe where a node sits, not what it is.

func (*Expression) FindAll

func (e *Expression) FindAll(classes ...string) []*Expression

FindAll returns every descendant (including the node itself) whose Class is one of those named -- the reference's `find_all`.

func (*Expression) Name

func (e *Expression) Name() string

Name is the reference's `.name`: the string of `this` when it is an Identifier or literal, else "".

func (*Expression) Set

func (e *Expression) Set(key string, value any)

Set assigns one arg, adopting any child expressions.

A typed nil child is normalised to an absent one. The reference stores None there, and an absent arg produces no dump record at all -- so a *Expression that happens to be nil must not become a leaf record holding "null", which is what a naive store would do and what the differential would then report as a mismatch on every optional clause.

func (*Expression) This

func (e *Expression) This() *Expression

This is the conventional primary child, as in the reference.

func (*Expression) Walk

func (e *Expression) Walk(fn func(*Expression) bool)

Walk visits the node and every descendant, depth first, in arg-key order so traversal is deterministic. The type annotation is visited too, when it is not already a child: a cast stores the same DataType in Args["to"] and Type, but a builder can stamp Type alone, and FindAll("DataType") has to see that. The reference's walk does not; this one does because a guard looking for types would otherwise miss them. Returning false from fn stops descent into that node's children.

type FormatString

type FormatString struct {
	End  string
	Type TokenType
}

FormatString is a prefixed string literal -- x'…', b'…', $$…$$ -- carrying the delimiter that closes it and the token type it produces.

type FuncArg

type FuncArg struct {
	Key    string
	Index  int
	VarLen bool
	Const  any
	// Wrap names a class to build FROM the argument rather than to hold
	// it: DATEADD records unit=Var(args[Index].name upper-cased), and the
	// argument itself appears nowhere in the result. 28 names do this,
	// and every one of them used to be refused outright.
	Wrap string
	// WrapArgs are the wrapper’s other arguments -- a Literal always
	// carries is_string, and a check for exactly one argument rejected
	// every builder whose wrapper was a Literal rather than a Var.
	WrapArgs []FuncConst
	// Nested names a class built AROUND the arguments rather than
	// from one of them -- DuckDB's ANY_VALUE is an IgnoreNulls over
	// an AnyValue, and T-SQL's YEAR a Year over a TsOrDsToDate. Its
	// own arguments are described the same way, one level down.
	Nested     string
	NestedArgs []FuncArg
	// NestedExcept names the argument KINDS that ESCAPE that wrapper:
	// "string", "number", "cast", "call", "subquery". T-SQL's LEN casts
	// its argument to TEXT unless it is ALREADY a string, so the cast
	// belongs in the spec and a string literal is the exception. Every
	// wrapper used to be unconditional, and a builder with a rule like
	// this one was refused as undescribable at every arity.
	NestedExcept []string
	// NestedAnnot is the TYPE the wrapper is annotated with, where the
	// builder annotates as well as builds: `exp.cast` records its target
	// on the node too, and the reference dumps that. A port that built
	// the cast without it produced a tree the differential called
	// different over a field no SQL shows.
	NestedAnnot *FuncArg
}

type FuncConst

type FuncConst struct {
	Key   string
	Value any
}

FuncConst is an argument that must hold this value for the spelling beside it to apply.

type FuncSQL

type FuncSQL struct {
	Name     string
	Keys     []string
	Consts   []FuncConst
	NoParens bool
	// MinArgs is how many members a LIST argument must hold for this
	// spelling to apply. T-SQL writes CONCAT(a, b) for two and just `a`
	// for one, so the call form belongs to the wider counts alone.
	MinArgs int
}

FuncArg fills one key: a positional argument, a variadic tail that collects everything from Index onward, or a constant the builder always sets -- COUNT is always big_int, whatever it was called with. FuncSQL is one way a function node may be written: the keyword, the argument keys that become its arguments in order, and the constant arguments that select this spelling over another. T-SQL writes a Count as COUNT_BIG when big_int is set and COUNT when it is not.

type FuncSpec

type FuncSpec struct {
	Class string
	Args  []FuncArg
	// Annot is the TYPE the builder annotated its OWN node with:
	// PostgreSQL's DIV is a Cast over an IntDiv, and the cast carries
	// its target twice. Nil where the builder annotates nothing.
	Annot *FuncArg
}

type GenerateError

type GenerateError struct{ What string }

GenerateError names what the generator could not write.

What is vocabulary -- a node class, a clause, a dialect limitation -- never a caller's identifier. Error() is safe to log; a caller that needs the name without scraping the message reads the field.

func (*GenerateError) Error

func (e *GenerateError) Error() string

func (*GenerateError) Is

func (e *GenerateError) Is(target error) bool

Is makes errors.Is(err, ErrGenerate) hold for this error too.

type JSONPathFunc

type JSONPathFunc struct {
	Class string
	// Fold folds every argument after the first into ONE path, a segment
	// each -- JSON_EXTRACT_PATH(x, 'y', '0', 'z'). Otherwise a single
	// argument is read as a path STRING.
	Fold   bool
	Consts []FuncConst
	// KeepsTail says whether arguments past the path survive. Databricks
	// DROPS them, and dropping an argument changes what the call means, so
	// the port refuses rather than writing a call that says something else.
	KeepsTail bool
	// IntSubscripts: a folded key that reads as an integer is a SUBSCRIPT
	// rather than a key of that name, shifted by IndexShift.
	IntSubscripts bool
	IndexShift    int
	// RootDefault: with no path argument the whole document is meant, and
	// the builder supplies a bare root. T-SQL's JSON_QUERY(x) does this.
	RootDefault bool
}

JSONPathFunc describes a name that turns its arguments into a JSON path.

Nine names across these dialects do it, and the generic probe rejected every one: it feeds placeholder COLUMNS, and these builders look at what they were handed, so the shape they show it is not the shape real SQL takes.

type JSONPathSQL

type JSONPathSQL struct {
	// Form is the call around the path, for an override -- measured with
	// the same rendering as the separators so the two agree on who
	// writes the dot after the root.
	Form string
	// PlainForm is that same call with an operand that is NOT a path,
	// which the path form would have quoted into something else.
	PlainForm string
	Open      string
	Close     string
	Key       string
	// KeyAfter is the form for a key that is not the FIRST: Databricks
	// writes `c1:item.price`, a colon and then a dot.
	KeyAfter string
	// EscapesQuote says a quote inside a key is doubled, which it must
	// be where the path is written inside a string and must NOT be
	// where the form is bare SQL.
	EscapesQuote bool
	Subscript    string
	QuotedKey    string
}

JSONPathSQL is the text around each piece of a JSON path.

type JSONPerPart

type JSONPerPart struct {
	Chain string
	Call  string
}

type KindSpec

type KindSpec struct {
	Index int
	Kind  string
	Spec  FuncSpec
}

ValueDispatch is one name's choice of class by the WORD in an argument: HASHBYTES('SHA1', x) is an SHA, HASHBYTES('MD5', x) an MD5, and anything else the Default. The words are read off the reference builder's own constants, not transcribed. KindSpec is the shape one arity takes when the argument at Index is of a particular KIND. PostgreSQL's REGEXP_REPLACE reads its last argument as flags when it is a string that does not spell a number, and as a position when it does -- one arity, two shapes.

type NotAQueryError

type NotAQueryError struct{ Kind string }

NotAQueryError names the statement. The guard above reports "DROP is not allowed; this endpoint is read-only", so it needs the word, not just the fact -- and reading it back out of a message would be a parser of its own.

func (*NotAQueryError) Error

func (e *NotAQueryError) Error() string

func (*NotAQueryError) Is

func (e *NotAQueryError) Is(target error) bool

Is makes errors.Is(err, ErrNotAQuery) hold for this error too.

type Param

type Param struct {
	Name     string // with its @
	Type     *Expression
	Default  *Expression
	Output   bool // OUTPUT or OUT
	ReadOnly bool // a READONLY table-valued parameter
}

Param is one parameter of a routine.

type ParseDiagnostic added in v1.0.4

type ParseDiagnostic struct {
	Kind           string `json:"kind"`
	Label          string `json:"label"`
	StatementKind  string `json:"statement_kind"`
	Start          int    `json:"start"`
	End            int    `json:"end"`
	StatementStart int    `json:"statement_start"`
}

type ParserTables

type ParserTables struct {
	// IDVarTokens are keywords that may be used as an identifier.
	IDVarTokens map[TokenType]struct{}
	// TableAliasTokens are keywords that may be used as a table alias --
	// a strict subset: a keyword that can start a clause is excluded, or an
	// implicit alias would swallow it.
	TableAliasTokens map[TokenType]struct{}
	// NamedFunctions are function names the reference parses into a node
	// class of their own. Everything else with an argument list is an
	// Anonymous call, which is the only form this port builds so far -- a
	// name in here is refused rather than flattened into Anonymous.
	NamedFunctions map[string]struct{}
	// NoParenFunctions are names that are a function call without an
	// argument list at all, e.g. CURRENT_DATE.
	NoParenFunctions map[TokenType]struct{}
	// NoParenFunctionClasses is the node each of those builds, with no
	// arguments at all.
	NoParenFunctionClasses map[TokenType]string
	// Functions is what each named function builds, worked out by running
	// the reference's own builder. A name in NamedFunctions but absent here
	// builds something no fixed mapping describes, and is refused rather
	// than approximated.
	Functions map[string]FuncSpec
	// FunctionSQL is how the generator writes each of those nodes back
	// out. A node absent here is one the reference writes in some shape
	// other than NAME(a, b); the generator refuses it rather than
	// emitting something that would parse back differently.
	FunctionSQL map[string][]FuncSQL
	// KeepsFunctionCase: a call is written in the case it was read in,
	// not upper-cased -- ClickHouse's function names are case-sensitive.
	// KeepsOriginalName names the classes written under the name they
	// were read with (`count()` stays `count()`), rather than their own.
	KeepsFunctionCase bool
	KeepsOriginalName map[string]struct{}
	// AggFunctions and AggFunctionSuffixes are ClickHouse's aggregate
	// names and the combinator suffixes that may follow them, longest
	// first: `sumIfMerge` is sum with If and Merge, and an aggregate node.
	// SetOpDistinctByDefault is what a bare UNION, EXCEPT or INTERSECT
	// means: "DISTINCT", "ALL", or "" where the dialect requires one
	// of the two words (ClickHouse's UNION). The word that matches the
	// default is not written back.
	SetOpDistinctByDefault map[string]string
	AggFunctions           map[string]struct{}
	AggFunctionSuffixes    []string
	// StandaloneUniqueBefore holds the words before which UNIQUE is a
	// whole constraint on its own (SQLite's `UNIQUE REFERENCES t`).
	StandaloneUniqueBefore map[string]struct{}
	// BinarySQL and UnarySQL are how each operator node is written.
	// DuckDB writes Pow as ** where the default writes POWER(a, b), so
	// the spelling is read from the reference's generator, not assumed.
	BinarySQL map[string]string
	UnarySQL  map[string]string
	// LimitIsTop puts the row ceiling in front of the projections, as
	// T-SQL's TOP does, rather than after the query as LIMIT does. The
	// guard rewrites one node either way; the dialect decides where it
	// lands, which is the whole reason the rewrite works at all.
	// TypeSQL is how each data type is written in this dialect: T-SQL
	// spells DataType.Type.INT as INTEGER.
	TypeSQL map[string]string
	// SizedTypeSQL is the name a type takes when it carries PARAMETERS,
	// where that differs: Databricks writes a bare VARCHAR as STRING and
	// a VARCHAR(255) as itself.
	SizedTypeSQL map[string]string
	// NullableTemplate wraps a type whose node does not say it is
	// non-nullable -- ClickHouse writes a type read from another
	// dialect as `Nullable(T)`, {type} where T goes -- and is empty
	// wherever nullability is not part of a type's spelling.
	// NeverNullableTypes are the kinds it never wraps: composites, and
	// the geometry types ClickHouse cannot make nullable.
	NullableTemplate   string
	NeverNullableTypes map[string]struct{}
	// IdentifierStart and IdentifierEnd are the delimiters a quoted name
	// is written with: brackets in T-SQL, backticks in Databricks.
	IdentifierStart string
	IdentifierEnd   string
	// QualifiesDerivedOutputs names every unnamed column of a CTE or a
	// derived table on the way out, because T-SQL will not accept one
	// without a name. Two executors that disagreed here would send the
	// engine different SQL for the same question.
	QualifiesDerivedOutputs bool
	// CoercesBooleans marks a dialect with no boolean type, where a value
	// used as a condition is written `x <> 0`. The port does not perform
	// that rewrite; it refuses, because emitting the uncoerced form would
	// be a statement the engine rejects.
	CoercesBooleans bool
	LimitIsTop      bool
	// LimitOnlyLiterals folds a LIMIT/OFFSET that is not already a
	// plain literal down to one at generation time -- `LIMIT 1 + 1`
	// writes as `LIMIT 2` -- because this dialect's engine will not
	// accept an expression there at all.
	LimitOnlyLiterals bool
	// MultiArgDistinct says this dialect writes `DISTINCT a, b` as
	// itself; where this is false, it has no multi-column DISTINCT
	// and the call is rewritten into a CASE that returns NULL if any
	// argument is NULL and the arguments as a row otherwise.
	MultiArgDistinct bool
	// IntervalAllowsPluralForm says an interval unit is written
	// whichever way it was: PLURAL where the statement said `DAYS`.
	// Where this is false, the unit is always singularised on the
	// way out, through the reference's own fixed TIME_PART_SINGULARS
	// table -- a reference-level constant no dialect overrides, so
	// it is written out once in the port rather than harvested here.
	IntervalAllowsPluralForm bool
	// LimitFetch says which of LIMIT and FETCH this dialect writes: the
	// port parses either into the class it was written as, and this
	// dialect converts one into the other at generation time ("ALL"
	// writes each node class as itself, converting neither).
	LimitFetch string
	// ConcatAsDPipe says this dialect ALWAYS writes CONCAT(a, b, ...)
	// as a `||` chain instead -- Redshift's own choice, unconditional
	// (it does not depend on the tree's own `coalesce` flag the way
	// other CONCAT_COALESCE dialects' generic rendering does).
	ConcatAsDPipe bool
	// SupportsSingleArgConcat says a one-argument CONCAT(a) call is
	// written as itself; where this is false, the call is written
	// bare -- `a`, not `CONCAT(a)`.
	SupportsSingleArgConcat bool
	// SuppressedWriteClasses names node classes this dialect writes as
	// nothing at all -- Materialize drops AUTO_INCREMENT, GENERATED AS
	// IDENTITY, PRIMARY KEY and ON CONFLICT entirely, none of which it
	// supports -- probed by building a bare instance of each candidate
	// class and checking the reference actually renders it empty.
	SuppressedWriteClasses map[string]struct{}
	// RewritesSerialToIdentity says SERIAL/SMALLSERIAL/BIGSERIAL write
	// back as the base int type plus GENERATED AS IDENTITY and NOT
	// NULL, matching how the reference's own Postgres-family write
	// path expands it -- a dialect that also suppresses
	// GeneratedAsIdentityColumnConstraint (see SuppressedWriteClasses)
	// is left with just the NOT NULL, which is correct for it.
	RewritesSerialToIdentity bool
	// TzToWithTimeZone says this dialect writes TIMETZ/TIMESTAMPTZ as
	// their bare-name equivalent (TIME/TIMESTAMP) plus a trailing
	// ` WITH TIME ZONE`, appended after any size the type carries
	// rather than baked into a name of its own.
	TzToWithTimeZone bool
	// EliminatesDistinctOn says this dialect has no `DISTINCT ON` of its
	// own and rewrites the whole SELECT into a `ROW_NUMBER()`-windowed
	// subquery instead, the way the reference's own preprocessing does.
	EliminatesDistinctOn bool
	// StatementTokens begin a statement that is not a query -- CREATE,
	// DELETE, INSERT and the rest. Anything else at the top level is
	// parsed as a bare expression, which is what the reference does and
	// what most of its own fixture corpus consists of.
	StatementTokens map[TokenType]struct{}
	// FuncTokens are the tokens allowed to name a function call.
	FuncTokens map[TokenType]struct{}
	// NoParenFunctionNames are names that are a call with no argument
	// list -- CURDATE in Databricks, CASE and IF everywhere -- and so are
	// not available as a bare column name.
	NoParenFunctionNames map[string]struct{}
	// InvalidFuncNameTokens are the token types that never name a
	// call, however the name reads: a QUOTED identifier or a string
	// is a name and nothing else, so a quoted CASE is a column even
	// though the bare word opens one.
	InvalidFuncNameTokens map[TokenType]struct{}
	// ValuesFollowedByParen says a bare VALUES -- one with no argument
	// list after it -- is a column name in this dialect rather than
	// the start of a VALUES clause.
	ValuesFollowedByParen bool
	// BareNameIsColumn holds the no-paren function names that read as
	// a COLUMN when nothing usable follows them. Probed one name at a
	// time: NEXT retreats without its `VALUE FOR` and CURDATE does not
	// retreat at all.
	BareNameIsColumn map[string]struct{}
	// ColonLambdaRead says the dialect reads `LAMBDA a, b : body`, and
	// ColonLambdaWrite is the template it writes one back with -- empty
	// where the dialect has no spelling of its own and the ordinary
	// arrow form is used instead.
	ColonLambdaRead  bool
	ColonLambdaWrite string
	// FunctionsWithAliasedArgs are the calls whose arguments may name
	// themselves even though the call itself is one the port builds a
	// node for: `STRUCT(1 AS a)` is a Struct of one named field.
	FunctionsWithAliasedArgs map[string]struct{}
	// StructTemplate wraps a struct's fields and StructFieldTemplate
	// writes one named field. `{key}` is the field name written as an
	// identifier and `{name}` is the bare name, which the dialect that
	// quotes its keys as strings uses instead.
	StructTemplate      string
	StructFieldTemplate string
	// PropertySpecs are the words a CREATE may say about the thing it
	// makes, keyed by the word. Class is the node built and Shape is
	// what follows the word; both are probed by parsing one, because
	// the reference keeps a little grammar per name and none of it is
	// readable as data.
	PropertySpecs map[string]PropertySpec
	// PropertyLocation says WHERE each property class is written --
	// POST_SCHEMA ones stand on their own after the columns, POST_WITH
	// ones go together inside one wrapped list.
	PropertyLocation map[string]string
	// WithPropertiesPrefix opens that wrapped list: WITH everywhere but
	// Databricks, which writes TBLPROPERTIES.
	WithPropertiesPrefix string
	// PropertyNameQuoted says a plain key-and-value property writes its
	// KEY in quotes -- Databricks writes `'a.b'=15` for what the others
	// write `a.b=15`. Read off a rendered node.
	PropertyNameQuoted bool
	// StringArgWraps are the positions where a builder REWRITES a
	// string argument rather than carrying it: T-SQL's DATETRUNC casts
	// one to DATETIME2, PostgreSQL's GENERATE_SERIES turns a step of
	// `'1 day'` into an INTERVAL. The port does the rewrite itself
	// before building, so a string that only moves in this way is not
	// evidence of a builder nobody can describe.
	StringArgWraps map[string]map[int]string
	// CallNamesTheReferenceKnows are every name the reference reads as
	// something other than an anonymous call -- a builder, a parser of
	// its own, a no-paren form, or a quantifier over a subquery. A name
	// NOT here is one the reference reads anonymously too, which is what
	// lets the annotator answer UNKNOWN for it without hiding a parse
	// gap behind the answer.
	CallNamesTheReferenceKnows map[string]struct{}
	// BareProcedureWrapper is the class a CREATE PROCEDURE's name is
	// wrapped in when it was written WITHOUT a parameter list. T-SQL
	// puts a StoredProcedure round it and the rest leave the name
	// alone, so the empty string means no wrapper.
	BareProcedureWrapper string
	// ProcedureWithOptions are the words a CREATE PROCEDURE may say
	// after WITH, and the class each becomes. The reference reads two
	// of them as a view attribute and the rest as a procedure option,
	// which is a distinction nothing in the words themselves makes --
	// so each is asked by parsing one.
	ProcedureWithOptions map[string]string
	// ColumnDefaultAfterEquals says a column definition may be followed
	// by `= <value>`, which becomes its default. T-SQL alone reads it,
	// and it is how a procedure parameter is given one.
	ColumnDefaultAfterEquals bool
	// IfIsAStatement says a bare IF opens a STATEMENT in this dialect --
	// `IF <cond> BEGIN ... END` -- rather than being a call or a name.
	IfIsAStatement bool
	// AlterCollateIsVar says the name a COLLATE gives is kept as a Var
	// rather than as an Identifier, and AlterColumnTypeTakesNull that a
	// retyped column may say NOT NULL or NULL after its new type.
	AlterCollateIsVar        bool
	AlterColumnTypeTakesNull bool
	// AlterColumnNullabilityWritten says a retyped column's NOT NULL is
	// written back. Where it is not, the reference drops the words and
	// this port refuses rather than writing a column that takes nulls
	// when the statement said it must not.
	AlterColumnNullabilityWritten bool
	// OpclassFollowWords and OpclassFollowTokens are what may follow an
	// indexed column WITHOUT being an operator class for it: a word
	// that orders the column, or the punctuation that ends it.
	OpclassFollowWords  map[string]struct{}
	OpclassFollowTokens map[TokenType]struct{}
	// TrimTypes are the words that may say WHERE a TRIM trims, and
	// TrimPatternFirst that its comma form writes the characters to
	// trim before the string they are trimmed from.
	TrimTypes        map[string]struct{}
	TrimPatternFirst bool
	// ProjectionEqualsIsAlias reads `SELECT a = 1` as `1 AS a` rather
	// than as a comparison. Only T-SQL does, and it is the same text
	// meaning two different things, so nothing but the dialect settles it.
	ProjectionEqualsIsAlias bool
	// PositionalColumns reads `#2` as the SECOND output column rather
	// than as anything to do with a hash.
	PositionalColumns bool
	// TypedDivision and SafeDivision are recorded on every Div node; the
	// reference reads them off the dialect, so they are not always false.
	TypedDivision bool
	SafeDivision  bool
	// DPipeIsStringConcat decides whether || concatenates or is a logical
	// or; StrictStringConcat becomes the DPipe node's safe flag, inverted.
	DPipeIsStringConcat bool
	StrictStringConcat  bool
	// JoinsHaveEqualPrecedence makes a comma join an explicit CROSS join.
	JoinsHaveEqualPrecedence bool
	// BareJoinIsOnTrue: a JOIN written with no ON. Databricks records it
	// as `ON TRUE` explicitly; every other dialect leaves the slot empty
	// and writes the comma form. Same relation either way, different
	// tree and different SQL -- so the two executors would send the
	// engine different statements. PROBED, like the rest.
	BareJoinIsOnTrue bool
	// DefaultTypeParams are the parameters a BARE type gains in this
	// dialect. DuckDB reads `numeric` as DECIMAL(18, 3) at parse time --
	// sqlglot calls these TYPE_CONVERTERS -- so a port that left the type
	// unparameterised sent a different CAST to the engine than the Python
	// executor did, and on a division that is a different NUMBER.
	// PROBED: the converters are closures, so the constants cannot be
	// read out; they are recovered by parsing a bare type and looking.
	DefaultTypeParams map[string][]string
	// IsNotNullWrapsInNot says how `x IS NOT NULL` is SHAPED. Every
	// dialect but PostgreSQL builds Not(Is(x, NULL)) and writes it back
	// as `NOT x IS NULL`; PostgreSQL records the negation on the Is
	// node instead. PROBED, not transcribed -- sqlglot has no flag for
	// it, the rule lives in a dialect override, and a port that assumed
	// one shape diverged on one of the commonest predicates in SQL.
	// DropsTypeParams are types whose PARAMETERS this dialect discards.
	// The mirror of DefaultTypeParams, out of the same TYPE_CONVERTERS:
	// DuckDB reads every text type as TEXT and drops the length, so
	// `VARCHAR(5)` is a bare TEXT and a port that kept the 5 sent the
	// engine a different CAST. PROBED, for the same reason.
	DropsTypeParams map[string]bool
	// LimitAllMeansNoLimit: `LIMIT ALL` is PostgreSQL for "no limit",
	// and DuckDB and Databricks follow. T-SQL and the neutral dialect
	// read ALL as a column of that name instead. PROBED: the difference
	// lands on the one clause the guard rewrites.
	LimitAllMeansNoLimit bool
	// StringSensitiveArgs are argument positions where a STRING
	// LITERAL makes the reference build something structurally
	// different: PostgreSQL reads a string step to GENERATE_SERIES as
	// an Interval, and a trailing string to REGEXP_REPLACE as
	// `modifiers`, shifting the arguments after it. The function probe
	// uses placeholder COLUMNS, so neither rule fires and the recorded
	// signature is right for columns and wrong for strings. A call that
	// puts a string in one of these slots is REFUSED: the port cannot
	// tell which slot was meant, and a plausible tree is the one thing
	// it must not build. PROBED.
	// CastSensitiveArgs are argument positions where an explicitly
	// CAST argument changes how the CALL itself is written: DuckDB
	// wraps BIT_OR over a non-integer in a round-and-cast, PostgreSQL
	// casts a double before a two-argument ROUND. The tree is the same
	// either way -- only the SQL differs -- so this is probed by
	// RENDERING, and a call that would need it is refused rather than
	// written without the coercion the engine needs. PROBED.
	// ClassSensitiveArgs are argument positions where the CLASS of the
	// argument itself changes what the builder makes: LOWER(HEX(x)) is a
	// LowerHex, LOWER(x) is a Lower. A spec probed with a placeholder
	// column describes only the second, so the call is refused when the
	// argument is one of the listed classes. PROBED against every class
	// this catalogue can itself produce.
	// FunctionsByArity holds names whose shape depends on HOW MANY
	// arguments the call has -- DATEDIFF of two is not DATEDIFF of
	// three. One spec cannot describe those, so each count gets its
	// own, and a count with no entry is refused.
	// UnitAliases are the unit spellings a name normalises: T-SQL
	// records DATEADD(qq, ...) as QUARTER, not QQ. Read off the
	// builder itself, since nothing outside it shows the mapping.
	UnitAliases map[string]map[string]string
	// SyntaxFunctions parse their arguments with their own grammar
	// rather than as a comma-separated list. A name here that the
	// port has not implemented is missing GRAMMAR, which is a
	// different thing from a builder it cannot describe.
	SyntaxFunctions map[string]struct{}
	// TableFunctions are names that are NOT a Table in a FROM clause:
	// `FROM UNNEST(x)` is an Unnest. The port has no node for these,
	// and wrapping the call in a Table round-trips perfectly while
	// being a different tree, so they are refused. PROBED.
	TableFunctions map[string]struct{}
	// SyntaxSQL is how each of those is WRITTEN, one template per set
	// of present arguments, rendered from the reference.
	SyntaxSQL          map[string][]SyntaxTemplate
	FunctionsByArity   map[string]map[int]FuncSpec
	ClassSensitiveArgs map[string][]ClassTrigger
	// TimeMapping is the dialect’s time-format spelling, and
	// TimeFormatArgs the argument positions it applies to. T-SQL
	// writes yyyy-MM-dd where the reference stores %Y-%m-%d, and the
	// builder rewrites the literal on the way in.
	TimeMapping    map[string]string
	TimeFormatArgs map[string][]int
	// InverseTimeMapping spells a stored format back out again, and
	// FormatTimeMapping is the SEPARATE table a one-character format
	// is read through. Only the dialects whose FORMAT is a builder
	// that reads its argument have the second one, which is what
	// tells the port that builder is installed.
	// DatePartMapping normalises a date part's spelling: T-SQL records
	// DATEPART(mm, x) as MONTH. A part the map does not name is kept
	// exactly as it was written, case and all.
	DatePartMapping    map[string]string
	InverseTimeMapping map[string]string
	FormatTimeMapping  map[string]string
	// FullFormatTimeMapping is TimeMapping with a dialect's OWN full-word
	// spellings layered over it -- T-SQL's DATENAME(mm, x) wants the full
	// month name, not the two-digit one TimeMapping's "mm" gives FORMAT.
	FullFormatTimeMapping map[string]string
	CastSensitiveArgs     map[string]map[int][]string
	// CastCoercions is what the dialect wraps an argument in, per the
	// type it is cast to: DuckDB rounds a float into a BIT_OR and casts a
	// decimal without rounding. A wrapper of `{arg}` alone means the slot
	// takes that type as it stands.
	CastCoercions map[string]map[int]map[string]map[string]string
	// CastIdempotentTypes are the slots whose coercion the dialect
	// applies whatever it is given, so an argument already carrying that
	// cast leaves nothing to add and the plain spelling is exact.
	CastIdempotentTypes map[string]map[string][]string
	// CastElidedAt names, per class and per key, the cast TARGETS the
	// reference's generator does not write there. Its own parser puts them
	// in -- T-SQL's LEN(x) is a Length over a cast to TEXT -- and its
	// generator takes them straight back out, so a port faithful in one
	// direction and not the other wrote LEN(CAST(x AS VARCHAR(MAX))).
	CastElidedAt map[string]map[string][]string
	// CastElidedOver names, per cast TARGET, the classes a cast to that
	// type is never written around: PostgreSQL's DIV is a Cast to DECIMAL
	// over an IntDiv, written as just DIV(4, 2).
	CastElidedOver map[string][]string
	// CastSensitiveTypes says WHICH cast targets move the rendering in
	// each of those positions. A slot is not sensitive to casting as
	// such: DuckDB wraps a non-text argument to UPPER in a cast to TEXT,
	// and leaves one that is already TEXT alone.
	CastSensitiveTypes map[string]map[int]map[string][]string
	// DropsZeroArgs are the argument keys a literal ZERO simply
	// DISAPPEARS from -- DuckDB writes REGEXP_EXTRACT(x, p) for a
	// zero group. A rule to FOLLOW, unlike the two below, and kept
	// apart from them because refusing it also turned away every
	// other literal in the same slot.
	DropsZeroArgs map[string]map[int][]string
	// ZeroSensitiveArgs is the same idea for a NUMBER rather than a
	// cast: DuckDB drops a zero group from REGEXP_EXTRACT entirely.
	// Kept apart from the cast trigger, because a call that moves for
	// one says nothing about the other.
	ZeroSensitiveArgs   map[string]map[int][]string
	StringSensitiveArgs map[string][]int
	// WritesBooleanLiteral: whether TRUE and FALSE are written as
	// themselves. T-SQL has no boolean literal -- the reference
	// rewrites them to 1 and 0, and to `1 = 1` in a condition. That is
	// a transform, not a spelling, and it is not ported; writing TRUE
	// anyway sent an engine SQL it rejects. PROBED.
	// ReservedKeywords must be QUOTED when written as an identifier
	// even though the caller wrote them bare. DuckDB reserves `all`, so
	// `SELECT 1 AS all` is written `AS "all"`; bare it is a syntax
	// error on the engine.
	ReservedKeywords map[string]bool
	// BracketIsRewritten: DuckDB and PostgreSQL shift a subscript to
	// sqlglot’s 0-based Bracket, annotate it, and run the shift
	// through the SIMPLIFIER. That is the optimizer, which is not
	// ported, so where this is set the subscript is REFUSED rather
	// than built plainly. PROBED.
	// ArrayOpen and ArrayClose are the text around an array literal:
	// `[`/`]` in DuckDB, `ARRAY[`/`]` in PostgreSQL, `ARRAY(`/`)`
	// elsewhere. PROBED.
	ArrayOpen  string
	ArrayClose string
	// IntervalUnitInsideString: PostgreSQL writes INTERVAL ‘1 DAY’
	// with the unit inside the quantity; everyone else writes
	// INTERVAL ‘1’ DAY. PROBED.
	IntervalUnitInsideString bool
	// JSONArrowOnlyJSONTypes and JSONArrowScalarOnly are stamped on
	// the node `->` and `->>` build. PROBED.
	JSONArrowOnlyJSONTypes bool
	// JSONArrowSetsScalarOnly says whether the arg is PRESENT on the
	// node at all; PostgreSQL leaves it off and the others set false.
	JSONArrowSetsScalarOnly bool
	// JSONArrowTypesWithoutPath says whether only_json_types is still
	// set when the operand is NOT a path -- PostgreSQL's builder
	// returns before stamping it, DuckDB's stamps it regardless.
	JSONArrowTypesWithoutPath bool
	// JSONExtractNeedsParens lists the extraction classes this dialect
	// writes as an OPERATOR, which the reference parenthesises when one
	// is an operand: `(a -> b) & c`, but Databricks' `a:b & c` plain.
	JSONExtractNeedsParens map[string]bool
	// VariantExtractColon says `x:a` is a JSON extraction here, the
	// form Databricks writes and this port used to write without ever
	// being able to read one back.
	VariantExtractColon bool
	// JSONKeyValueSQL is one key/value pair inside JSON_OBJECT: DuckDB
	// separates them with a comma and the others with a colon.
	JSONKeyValueSQL string
	// DefaultSampleMethod is the TABLESAMPLE method this dialect records
	// when the statement names none. DuckDB says RESERVOIR either way.
	DefaultSampleMethod string
	// BareSampleCountIsPercent: `TABLESAMPLE (3)` is a PERCENTAGE in
	// PostgreSQL and a number of rows everywhere else.
	BareSampleCountIsPercent bool
	// PrefixAlias: `SELECT foo: 1` NAMES the expression, DuckDB's
	// spelling of `SELECT 1 AS foo`. The same characters are a JSON
	// extraction in Databricks, so the dialect decides, not the shape.
	PrefixAlias bool
	// StringAliases: `SELECT 1 'foo'` names the column foo, the same
	// as a bare word would -- the word just came already quoted. T-SQL
	// has it; most dialects do not, and a bare string stays a value.
	StringAliases bool
	// NormalizeNotNull: `x NOTNULL` is written NOT x IS NULL here and
	// kept as a negated IS elsewhere -- two trees for one word.
	NormalizeNotNull bool
	// StarExceptWord is what `* EXCEPT (a)` is written with: DuckDB
	// says EXCLUDE, and both words are READ everywhere.
	StarExceptWord string
	// DeclareAssignment is what stands between a declared variable and
	// its initial value.
	DeclareAssignment string
	// EndCommits: a bare END ENDS THE TRANSACTION here, and is a name
	// or a block anywhere else.
	EndCommits bool
	// FormatSpellings says how each class that carries a FORMAT writes
	// it: `inverse` through the dialect's own mapping -- the tree
	// stores %Y-%m-%d and PostgreSQL writes YYYY-MM-DD -- `verbatim`
	// as stored, and `other` through a table of its own, which the
	// writer declines rather than guess at.
	FormatSpellings map[string]string
	// DefaultTimeFormat is the format a `default-dropped` class writes
	// as nothing at all.
	DefaultTimeFormat string
	// LikeInsideSchema: a `LIKE other` that is NOT inside a column
	// list brings its own parentheses here. SupportsCreateLike says
	// the dialect writes the word at all.
	LikeInsideSchema   bool
	SupportsCreateLike bool
	// ExecuteBuildsExecute: EXEC names a procedure to run here, and is
	// kept as raw text anywhere else.
	ExecuteBuildsExecute bool
	// ShowKinds are the phrases SHOW reads as a statement rather than
	// keeping as text.
	ShowKinds map[string]struct{}
	// ReservedTokens are the ones that can never stand in for a name,
	// which is what the reference asks where ANY token may.
	ReservedTokens map[TokenType]struct{}
	// SequenceOptions are the words a CREATE SEQUENCE takes that carry
	// no value, each with the word that may follow it: NO CYCLE is one
	// option, not two.
	SequenceOptions map[string][]string
	// CreatableTokens are the things a CREATE makes and a DROP
	// removes, and CreatableKindNames what a dialect calls them where
	// it uses another word.
	CreatableTokens    map[TokenType]struct{}
	CreatableKindNames map[string]string
	// How a COPY's parameters are written: separated by commas or by
	// spaces, and with or without an = before each value. The names
	// whose value is a LIST are kept apart, since those are read a
	// different way again.
	CopyIntoWritten   bool
	CopyParamsWrapped bool
	CopyParamsAreCSV  bool
	CopyParamsNeedEQ  bool
	CopyVarlenOptions map[string]struct{}
	// SupportsColumnJoinMarks says a bare column may carry Oracle-style
	// `(+)` outer-join syntax -- Redshift's tokenizer maps that literal
	// text to its own token, the same way the reference reads it as one.
	SupportsColumnJoinMarks bool
	// HasDistinctArrayConstructors says ARRAY[1, 2] and ARRAY(1, 2) are
	// written back differently -- Redshift stamps the node with which
	// bracket kind built it (bracket_notation) so generation can tell.
	HasDistinctArrayConstructors bool
	// SupportsImplicitUnnest says a comma- or CROSS-JOINed table whose
	// name is DOTTED and whose first part names an earlier table in the
	// FROM is really a nested column being iterated, not a real table --
	// Redshift's `t, t.arr_col AS x` sugar for `t, UNNEST(t.arr_col) AS x`.
	SupportsImplicitUnnest bool
	// HasListConstructor says `LIST[1, 2]` builds a List rather than a
	// column named LIST subscripted by [1, 2] -- true nearly
	// everywhere `ARRAY_CONSTRUCTORS` maps the word, EXCEPT where the
	// dialect reads it some other way first: DuckDB's own LIST type
	// grammar intercepts it, and T-SQL's `[...]` already means a
	// quoted identifier. Probed directly rather than read off the
	// (unconditionally shared) table, since it is the OUTCOME that
	// differs, not the table.
	HasListConstructor bool
	// DefaultsUnsizedCharTypes says a CREATE TABLE column typed bare
	// VARCHAR or CHAR, with no length, is stamped with an implicit
	// length of 1 -- Fabric's own reading of T-SQL's rule for what a
	// missing length means (`learn.microsoft.com/.../char-and-varchar`).
	DefaultsUnsizedCharTypes bool
	// ZoneAwareTimestampConstructor says a TIMESTAMP/TIME typed
	// literal carrying a zone offset or name reads as TIMESTAMPTZ/
	// TIMETZ instead -- Presto's own reading of what the literal
	// actually names, not the bare word that introduced it.
	ZoneAwareTimestampConstructor bool
	// UnaryOps is which token opens a PREFIX operator, and what it
	// builds. The empty string is the no-op unary plus.
	UnaryOps map[TokenType]string
	// PrefixCalls is punctuation that NAMES a function written in
	// front of its operand, keyed by the characters: DuckDB's `@x` is
	// ABS(x), and it takes a whole arithmetic expression.
	PrefixCalls map[string]string
	// TsOrDsParents are the classes a TsOrDsToDate DISAPPEARS under.
	// Databricks reads YEAR(y) as a Year over a TsOrDsToDate and writes
	// it back as YEAR(y) -- the wrapper is written only where its
	// parent is not one of these, which is a rule about the PARENT and
	// so invisible to a probe that renders a node on its own.
	TsOrDsParents map[string]struct{}
	// ConvertBuildsConvert: CONVERT(type, value) is a Convert here and a
	// CAST written another way everywhere else.
	ConvertBuildsConvert bool
	// The four conventions a PIVOT node carries that the statement never
	// says: how output columns are named, and three flags stamped on.
	// VersionRangeSep is the word between the two bounds of a FOR
	// SYSTEM_TIME range, which are held as a Tuple: `c TO d`, `c AND d`.
	VersionRangeSep map[string]string
	// ForClauseOptions is the option vocabulary of FOR XML and FOR JSON,
	// by kind: each word, and the second word it may take after it.
	ForClauseOptions map[string]map[string][]string
	// QueryHintOptions is T-SQL's OPTION (...) vocabulary: each word,
	// and the words that may follow it. QueryHintOptionsNeedEqual are
	// the ones written with `=` before their value.
	QueryHintOptions          map[string][][]string
	QueryHintOptionsNeedEqual map[string]struct{}
	// TableSampleWord and SelectSampleWord are what a sample is called
	// after a TABLE and after the QUERY: DuckDB says TABLESAMPLE for the
	// one and USING SAMPLE for the other, for the very same node.
	// GroupConcatOrder says where a folded ORDER BY goes when a
	// GroupConcat is written back: inside the first argument, or
	// unfolded into a WITHIN GROUP. Empty means neither, and refuse.
	// WithinGroupFolds are the function NAMES whose builder swallows a
	// following WITHIN GROUP instead of being wrapped by one. It is the
	// NAME that decides, not the class: Databricks folds STRING_AGG and
	// not LISTAGG, and both build a GroupConcat.
	WithinGroupFolds map[string]struct{}
	// MapBraceLiteral: `MAP {k: v}` is a map LITERAL here. Elsewhere MAP
	// is an ordinary name and the braces are a struct.
	MapBraceLiteral bool
	// TruncatesCatalog: a three-part name is written with only two of
	// them here, which names a different object.
	TruncatesCatalog bool
	// HoistsInsertWith: a WITH inside an INSERT is written in FRONT of
	// the statement here, and left where it was written elsewhere.
	HoistsInsertWith bool
	// ReturningWord is what this dialect calls a RETURNING clause, and
	// ReturningEnd whether it is written after the WHERE rather than
	// straight after the verb.
	ReturningWord string
	ReturningEnd  bool
	// MergeWithoutTarget: a MERGE branch's assignments are written without
	// the target's own name in front of them here.
	MergeWithoutTarget bool
	// NormalizeUnquoted and NormalizeQuoted are the case a name is COMPARED
	// in -- lower, upper, or empty for as-written.
	NormalizeUnquoted string
	NormalizeQuoted   string
	// CreateExistsWritten says, per kind, whether IF NOT EXISTS survives
	// being written. TemporaryWritten says the same of TEMPORARY.
	CreateExistsWritten map[string]bool
	TemporaryWritten    map[string]bool
	// ViewColumnCommentWritten: a view column keeps its COMMENT here.
	ViewColumnCommentWritten bool
	// AlterAddColumnWord: an ALTER writes the word COLUMN after ADD here,
	// and AlterRepeatsAdd whether each added column gets its own ADD.
	AlterAddColumnWord bool
	AlterRepeatsAdd    bool
	// AlterColumnTypeWord is what comes between an altered column and its
	// new type -- SET DATA TYPE, TYPE, or nothing at all.
	AlterColumnTypeWord string
	// PrimaryKeyMembersOrdered: a table-level PRIMARY KEY names its columns
	// as ordered index members here, not as bare names.
	PrimaryKeyMembersOrdered bool
	// UniqueConstraintWritten: a UNIQUE constraint survives being written
	// here. Where it does not, the guarantee would be silently dropped.
	UniqueConstraintWritten bool
	// FunctionReturnsPlace says WHERE a RETURNS property is written, per
	// shape of what it holds: after the parameter list, in the body, or
	// nowhere at all.
	FunctionReturnsPlace map[string]string
	// FunctionPropertiesWritten: a function's other properties -- LANGUAGE,
	// IMMUTABLE, STRICT -- survive being written here.
	FunctionPropertiesWritten bool
	// FunctionReturnAs: AS is written in front of a RETURN body.
	FunctionReturnAs bool
	// FunctionWrapsTableBody: a table-valued function's body becomes a
	// RETURN here even when it was not written as one.
	FunctionWrapsTableBody bool
	// FunctionAsTableRead: `AS TABLE <query>` is READ as a return type here.
	FunctionAsTableRead bool
	// ReturnWord is the word a RETURN writes, empty where it writes none.
	ReturnWord string
	// ParameterModePrefix: a parameter's mode is written in FRONT of it
	// here rather than after its type, and ParameterModeWords how each is
	// spelled -- one word or two.
	ParameterModePrefix bool
	ParameterModeWords  map[string]string
	// SetItemSeparator sits between a configuration name and its value.
	SetItemSeparator string
	// ComputedColumnSpelling is how a computed column is written, with
	// {expr} where the expression goes, and ComputedKeepsType whether the
	// column's declared type survives beside it.
	ComputedColumnSpelling string
	ComputedKeepsType      bool
	// IdentityWritten: a GENERATED ... AS IDENTITY column keeps that
	// spelling here rather than being rewritten into something else.
	IdentityWritten bool
	// IdentityWidensType: an identity column's type is widened to BIGINT.
	IdentityWidensType bool
	// GeneratedExpressionIsComputed: `GENERATED ALWAYS AS (x)` with no
	// STORED is a COMPUTED column here, not an identity with an
	// expression on it.
	GeneratedExpressionIsComputed bool
	// GeneratedStoredSetsPersisted: `GENERATED ALWAYS AS (x) STORED`
	// records WHICH of STORED and VIRTUAL it read -- MySQL's own
	// override does; PostgreSQL's does not, having no VIRTUAL to tell
	// STORED apart from in the first place.
	GeneratedStoredSetsPersisted bool
	// IndexOnWord sits between an index and the table it is on.
	IndexOnWord string
	// StringClassSQL is how each kind of quoted string is written, keyed by
	// class. The value is the template, a tab, and whether the body takes a
	// string's own escaping. A class missing from the map is one this
	// dialect writes in a way that loses the value.
	StringClassSQL map[string]string
	// OffsetRowsWord is written after an OFFSET count, and is empty in
	// every dialect but T-SQL.
	OffsetRowsWord string
	// IntervalUnitAliases are the unit spellings an INTERVAL normalises,
	// keyed by the upper-cased spelling written.
	IntervalUnitAliases map[string]string
	// TableHintsWritten: a table's locking hints survive here. Every
	// dialect but T-SQL drops them.
	TableHintsWritten bool
	// TransactionWord is written after BEGIN, COMMIT and ROLLBACK here.
	// TransactionSavepointWritten says whether the name a ROLLBACK TO
	// carries survives, and TransactionNameWritten whether the name the
	// TRANSACTION itself carries does. The dialects disagree about the two
	// in opposite directions.
	TransactionWord             string
	TransactionSavepointWritten bool
	TransactionNameWritten      bool
	// BareBeginIsATransaction: a BEGIN with no TRANSACTION after it opens
	// one here. In T-SQL it opens a block instead.
	BareBeginIsATransaction bool
	// SetItemKindWritten says whether a SET's scope word survives, per
	// word. Dropping one changes which scope the setting belongs to.
	SetItemKindWritten map[string]bool
	// SetItemVariableSeparator sits between a VARIABLE and its value,
	// which is not always what sits between a setting and its value.
	SetItemVariableSeparator string
	// SetWithoutASign: a SET may be written with no `=` between the name
	// and the value here.
	SetWithoutASign bool
	// PartitionSQL is how a table's PARTITION clause is written, with
	// {members} where its members go.
	PartitionSQL string
	// AlterSetOptionWritten: an `ALTER TABLE ... SET` keeps the words that
	// say WHAT it sets, and AlterSetWrapsOptions whether a list of
	// settings is written in parentheses.
	AlterSetOptionWritten bool
	AlterSetWrapsOptions  bool
	// AlterSetListIsSettings: `ALTER TABLE t SET (k = v)` is a list of
	// settings here rather than of table properties.
	AlterSetListIsSettings bool
	// AlterSetIsWrapped: `ALTER TABLE t SET (...)` puts the whole SET
	// inside parentheses of the syntax's own, and what is inside them
	// is read as PROPERTIES rather than as settings.
	AlterSetIsWrapped bool
	// KeyConstraintOptions are the words that may follow a REFERENCES or a
	// key, and what may follow each of them.
	KeyConstraintOptions map[string][]string
	// WithDataWritten: `WITH NO DATA` survives here. It says whether the
	// table is FILLED from the query or only shaped by it.
	WithDataWritten bool
	// EnumTypeSQL is how an ENUM type's members are written, with
	// {members} in it. PostgreSQL puts a space in front of the list and
	// writes the parentheses even when the list is empty; everyone else
	// spells it as an ordinary parameterised type.
	EnumTypeSQL string
	// IdentityShortSQL is the short spelling of an identity column, with
	// {start} and {increment} in it, and empty where the dialect writes
	// the whole GENERATED form instead.
	IdentityShortSQL string
	// AutoIncrementSQL is how an auto-incrementing column is spelled, and
	// is empty where the dialect writes nothing and drops the numbering.
	AutoIncrementSQL string
	// ValuesTableWrapped: a VALUES clause used as a TABLE is written in
	// parentheses here.
	ValuesTableWrapped bool
	// RenameTarget says how much of a qualified name a RENAME TO writes:
	// the whole thing, the last part only, or -- empty -- neither,
	// because the dialect writes another statement entirely.
	RenameTarget string
	// RewritesCreateAsSelect: this dialect has no CREATE TABLE AS SELECT
	// and the reference turns it into something else, which is a
	// transformation rather than a spelling.
	RewritesCreateAsSelect   bool
	GroupConcatOrder         string
	TableSampleWord          string
	SelectSampleWord         string
	PivotColumnNaming        string
	PivotIdentifiesStrings   bool
	PivotPrefixesColumns     bool
	UnpivotValueColumnsFirst bool
	// JSONPathFunctions are the names that turn their arguments into a
	// JSON PATH rather than holding them. Probed with STRING literals,
	// because with the placeholder columns the generic probe uses they
	// all fall back to a plain positional shape that real SQL never takes.
	JSONPathFunctions map[string]JSONPathFunc
	// JSONPathIsParsed: PostgreSQL keeps the string after `->` whole
	// as a single key; everyone else parses it into path parts.
	JSONPathIsParsed bool
	// WritesIntoUnlogged: PostgreSQL keeps UNLOGGED before the table
	// in SELECT ... INTO; T-SQL has no such kind and drops it.
	WritesIntoUnlogged bool
	// JSONPath is how this dialect writes a path, in pieces: DuckDB
	// quotes the whole thing and keeps the $ root, Databricks does
	// neither, and the two spell a key that needs quoting differently.
	// PROBED by subtraction.
	JSONPath JSONPathSQL
	// JSONPathByClass overrides those pieces for a class that writes a
	// path differently from the standalone rendering. Only the SEPARATORS
	// are overridden; the wrapper comes from the call's own spelling.
	JSONPathByClass map[string]JSONPathSQL
	// JSONExtractSQL is how the operator wraps a path, where the
	// dialect writes it as one. A dialect that explodes the path into
	// arguments has no entry and is refused.
	JSONExtractSQL map[string]string
	// JSONPerPartSQL is for dialects with no single path literal:
	// PostgreSQL writes one operator per part, or one ARGUMENT per
	// part when the node is not restricted to JSON types. One node
	// becomes N of something, which is a transform, not a spelling.
	JSONPerPartSQL     map[string]JSONPerPart
	BracketIsRewritten bool
	// IndexOffset is how far this dialect's subscripts are from
	// sqlglot's 0-based Bracket: 1 in DuckDB and PostgreSQL, 0
	// elsewhere. The parser subtracts it from a written index.
	IndexOffset int
	// SafeToEliminateDoubleNegation gates the optimizer's NOT NOT x
	// -> x rule. True in every dialect this port configures, which is
	// exactly why it is read rather than assumed: a rule that is right
	// everywhere today is not the same as a rule with no condition.
	SafeToEliminateDoubleNegation bool
	// Placeholder is how a bound parameter is written: `$name` in
	// DuckDB, `%(name)s` in PostgreSQL, `:name` elsewhere.
	Placeholder PlaceholderSQL
	// QuantifierWrapsSubquery: whether a quantifier over a QUERY
	// keeps the Subquery wrapper. ANY does and ALL does not, in
	// every dialect -- a per-class fact, not a per-dialect one.
	QuantifierWrapsSubquery map[string]bool
	// QuantifierQuerySQL is the same over a QUERY, where the
	// spacing and the parentheses both differ from the array form.
	QuantifierQuerySQL map[string]string
	// QuantifierComparisonRewritten: `a = ANY (...)` is written as some
	// other call entirely (ClickHouse's `has`), which the port does not
	// perform, so a comparison against a quantifier is refused.
	QuantifierComparisonRewritten bool
	// NestedTypeKinds are the DataType kinds the reference marks
	// `nested`, whether or not they were written with parameters.
	// StructTypeKinds is the subset whose parameters are NAMED
	// fields (ColumnDef) rather than bare types.
	NestedTypeKinds map[string]bool
	StructTypeKinds map[string]bool
	// SupportsFixedSizeArrays: whether `INT[3]` is a type at all.
	// Where it is not, the reference RETREATS and reads the
	// brackets as a subscript of the cast instead.
	SupportsFixedSizeArrays bool
	// SupportsBetweenFlags: whether SYMMETRIC and ASYMMETRIC survive
	// on a BETWEEN as words of their own. Where they do not, a TRUE
	// SYMMETRIC needs expanding into two BETWEENs joined by OR --
	// not yet ported, so it is refused rather than half-written; an
	// ASYMMETRIC there is simply dropped, meaning the same thing.
	SupportsBetweenFlags bool
	CompositeType        CompositeTypeSQL
	// Boolean is how TRUE and FALSE are written, and the two
	// positions can differ: T-SQL writes 1 where a value is wanted
	// and (1 = 1) where a condition is.
	// QuantifierSQL is the text before a quantifier’s operand: ALL
	// takes a trailing space and ANY does not.
	QuantifierSQL map[string]string
	// WritesNullsOrdering: whether NULLS FIRST/LAST is written back.
	// T-SQL has no such clause and drops it.
	// DefaultNullsFirst* is where NULLs sort when the statement does
	// not say, per direction. Read off the reference rather than
	// derived twice -- deriving it in the generator as well as the
	// parser got Databricks wrong.
	DefaultNullsFirstAsc  bool
	DefaultNullsFirstDesc bool
	// WithinGroupAbsorbedBy are the classes that FOLD a following
	// WITHIN GROUP into themselves instead of being wrapped by it.
	WithinGroupAbsorbedBy map[string]bool
	WritesNullsOrdering   bool
	Boolean               BooleanSQL
	WritesBooleanLiteral  bool
	IsNotNullWrapsInNot   bool
	// NullOrdering decides where NULLs sort when nobody says, and so
	// what nulls_first records on an Ordered node. It differs per
	// dialect and is not derivable from ASC or DESC alone.
	NullOrdering string
	// A trailing ORDER BY, LIMIT or OFFSET after a set operation belongs to
	// the set operation, not to the query on its right. The reference
	// parses it onto the right-hand query and then moves it up; which of
	// the three move differs per dialect.
	ModifiersAttachedToSetOp bool
	SetOpModifiers           []string
	// The precedence chain, level by level, token to node class. These are
	// per dialect and not interchangeable: DuckDB reads ^ as Pow where the
	// default reads it as BitwiseXor.
	Disjunction map[TokenType]string
	Conjunction map[TokenType]string
	Equality    map[TokenType]string
	Comparison  map[TokenType]string
	Bitwise     map[TokenType]string
	Term        map[TokenType]string
	Factor      map[TokenType]string
	Exponent    map[TokenType]string
	// JoinSides, JoinKinds and JoinMethods are the words that may precede
	// JOIN. A method -- NATURAL, ASOF, POSITIONAL -- changes what the join
	// means and is refused rather than dropped.
	JoinSides   map[TokenType]struct{}
	JoinKinds   map[TokenType]struct{}
	JoinMethods map[TokenType]struct{}
	// WithinGroupInside: the ordering an ordered-set aggregate is
	// computed over is written INSIDE the call here rather than after it
	// in a WITHIN GROUP of its own. DuckDB is the only one, and it moves
	// a percentile's order key into the call's first argument as well.
	WithinGroupInside bool
	// PercentileClasses are the ordered-set aggregates whose ARGUMENTS a
	// dialect writing the order inside reshuffles: the key becomes the
	// first and the fraction slides right.
	PercentileClasses map[string]struct{}
	// IgnoreNullsInFunc: `IGNORE NULLS` is written INSIDE the call's
	// argument list here rather than after the call.
	IgnoreNullsInFunc bool
	// ConditionCoercion is how a value that is not already a condition
	// is made into one, with {value} standing for it. T-SQL has no boolean
	// type and compares against zero instead; empty where the dialect
	// takes a value as a condition unchanged.
	ConditionCoercion string
	// SplitPartCountsBackwards is how a dialect that counts the pieces
	// of a DOTTED name from the other end opens that call, with {this} in
	// it; the number and the closing parenthesis are the writer's. Empty
	// where the dialect splits on any delimiter it is given.
	SplitPartCountsBackwards string
	// FileFormatSQL is how the storage format a table is written in is
	// spelled, with {name} standing for the format. Databricks says
	// `USING PARQUET` where the rest say `FORMAT=PARQUET`.
	FileFormatSQL string
	// TemporarySuffix is what a dialect APPENDS to a temporary object
	// of each kind: Databricks gives a temporary TABLE a storage format
	// it was never given.
	TemporarySuffix map[string]string
	// DateDeltaIsAnOperator: a date shifted by an interval is written
	// with an OPERATOR here -- `d + INTERVAL 1 DAY` -- rather than as a
	// call. The unit rides on the interval either way.
	DateDeltaIsAnOperator bool
	// ColumnCommentWritten: a COMMENT on a column survives here.
	// PostgreSQL and DuckDB have nowhere to say it in a CREATE and write
	// nothing, which is what the reference does with it.
	ColumnCommentWritten bool
	// CommaUnnestJoins: a comma join over an UNNEST is written here as
	// an explicit JOIN with `ON TRUE`, because the comma form does not
	// bind the unnested rows to the row they came from.
	CommaUnnestJoins bool
	// TableSample is how a sampling clause is written: the words that
	// open it, whether the METHOD is written, whether a bare size counts
	// ROWS or a percentage, and what the repeatable seed is called.
	TableSample TableSampleSQL
	// JSONExtractTwiceSQL is how a dialect that writes the value TWICE
	// spells an extraction: T-SQL asks JSON_QUERY for an object and
	// JSON_VALUE for a scalar and takes whichever is not null. Empty where
	// the dialect writes the value once.
	JSONExtractTwiceSQL map[string]string
	// RegexpFlagArgs names which argument of each regexp call holds the
	// FLAGS: `modifiers` on a replacement, `parameters` on an extraction.
	RegexpFlagArgs map[string]string
	// RegexpFlags are the flag characters a REGEXP_REPLACE may carry
	// here, and RegexpFlagsNeedLiteral whether they have to be written as
	// a string. An empty RegexpFlags means the dialect writes whatever it
	// is given; a dialect that writes none at all has
	// RegexpFlagsWritten false and the port refuses rather than dropping
	// them, because a flag says what the replacement DOES.
	RegexpFlags            string
	RegexpFlagsWritten     bool
	RegexpFlagsNeedLiteral bool
	// IgnoreNullsWindowFuncs are the calls that KEEP their null
	// treatment where the dialect writes it inside; IgnoreNullsDropped
	// are the ones it drops silently because they ignore nulls anyway.
	// Anything else the dialect calls unsupported, and so does the port.
	IgnoreNullsWindowFuncs map[string]struct{}
	IgnoreNullsDropped     map[string]struct{}
	// WithinGroupPercentile is what a PERCENTILE under a WITHIN GROUP
	// becomes: "outside" keeps the clause, "inside" folds it into the
	// call and reshuffles the arguments, and empty means the dialect
	// writes a different function and the port refuses.
	WithinGroupPercentile string
	// JoinHints are the words a dialect allows between the KIND and the
	// JOIN, naming how the engine should do it: `INNER HASH JOIN`. They
	// are words rather than tokens, and a dialect that names none writes
	// none -- the reference drops a hint where the target has no hints.
	JoinHints map[string]struct{}
	// RangeTokens are the operators the reference handles at the range
	// level -- IS, IN, BETWEEN, the LIKE family and a dozen operators
	// specific to one dialect. The ones not handled are refused here
	// rather than left to look like the end of the expression.
	RangeTokens map[TokenType]struct{}
	// TypeDispatchFunctions are the names whose CLASS depends on the
	// TYPE of one argument. DuckDB's DATE_TRUNC builds a DateTrunc
	// over a DATE and a TimestampTrunc over anything else -- two
	// different shapes, not just two names, which is why each carries
	// a whole spec. Answerable only with a type annotator, which is
	// why these were refused until there was one.
	TypeDispatchFunctions map[string]TypeDispatch
	// ValueDispatchFunctions are names whose CLASS is chosen by the
	// WORD in one argument: T-SQL's HASHBYTES('SHA1', x) is an SHA and
	// HASHBYTES('MD5', x) an MD5. A word not listed takes Default.
	ValueDispatchFunctions map[string]ValueDispatch
	// CreateProperties are the words that may stand between CREATE and
	// what it creates, each carrying a bare property of its own:
	// MATERIALIZED, UNLOGGED, TRANSIENT. The class is read off the tree,
	// not made from the word -- STREAMING becomes a StreamingTableProperty.
	CreateProperties map[string]string
	// KeepsUnnamedWrapped are the names whose wrapped slot keeps the
	// NODE where the argument has no name to take, rather than building
	// something else: PostgreSQL's DATE_BIN takes a subquery as its unit.
	KeepsUnnamedWrapped map[string]struct{}
	// UserDefinedTypeIsIdentifier wraps such a type's NAME in an
	// Identifier. PostgreSQL does and the rest keep the word as it
	// stands, which is a difference the dump shows and nothing else does.
	UserDefinedTypeIsIdentifier bool
	// CreateKinds are the things this dialect will CREATE. T-SQL alone
	// spells a procedure PROC as well as PROCEDURE.
	CreateKinds map[string]struct{}
	// CreateOrFlags are the words after CREATE OR and the flag each
	// turns on: REPLACE and T-SQL's ALTER both mean `replace`, and
	// Databricks' REFRESH means `refresh`.
	CreateOrFlags map[string]string
	// CreateFlagWords is how each of those flags is WRITTEN, which need
	// not be the word it was read from: T-SQL reads OR REPLACE and OR
	// ALTER alike and writes OR ALTER.
	CreateFlagWords map[string]string
	// ArityKindSpecs are the shapes an arity takes when one argument is
	// of a particular kind, beside the plain shape in FunctionsByArity.
	ArityKindSpecs map[string]map[int][]KindSpec
	// ValidIntervalUnits are the words that can follow INTERVAL as a
	// TYPE. Where the next word is not one, the reference builds a
	// bare INTERVAL type instead of reading a unit that is not there.
	ValidIntervalUnits map[string]struct{}
	// BinaryRangeOps are the range operators that are just a binary
	// node -- PostgreSQL's `@>`, `&&`, `-|-` and the rest. Probed by
	// running `a <op> b` and keeping the class where the result is
	// really a plain two-argument binary; IS, IN and BETWEEN have
	// shapes of their own and are not here.
	BinaryRangeOps map[TokenType]string
	// SwappedRangeOps are the ones whose operands go the OTHER way
	// round: PostgreSQL's `x @@ y` is a MatchAgainst of y over x.
	SwappedRangeOps map[TokenType]struct{}
	// ListedRangeOps hold one operand as a LIST of one, which is the
	// reference's shape for `@@` rather than a second operand.
	ListedRangeOps map[TokenType]struct{}
	// BinaryRangeSQL is how each of those is written back.
	BinaryRangeSQL map[string]string
	// JSONOperatorsAtBitwise are the operators this dialect reads
	// level with `||` rather than as an accessor binding tighter
	// than arithmetic. Probed by the asymmetry: `1 + x <op> 'y'` is
	// `(1 + x) <op> 'y'` where the operator is here, and
	// `1 + (x <op> 'y')` where it is not. Includes `->` / `->>` in
	// PostgreSQL and DuckDB, and `#>` / `#>>` / `?` in PostgreSQL.
	JSONOperatorsAtBitwise map[TokenType]string
	// JSONOperatorSQL is how each of those is written back -- except
	// the arrows, which have a path-aware writer of their own. Only
	// ever in the dialect that reads the operator at this tier:
	// elsewhere the same node is a function call.
	JSONOperatorSQL map[string]string
	// TypeTokens maps a type keyword to the DataType.Type member the
	// reference records. A few type tokens have no member and are absent,
	// which refuses them rather than inventing one.
	TypeTokens map[TokenType]string
	// TimestampTypeTokens are the types a WITH/WITHOUT TIME ZONE may
	// follow, and TimeTypeTokens the ones among them that become a
	// TIMETZ rather than a TIMESTAMPTZ when it says WITH.
	TimestampTypeTokens map[TokenType]struct{}
	TimeTypeTokens      map[TokenType]struct{}
}

ParserTables is one dialect's resolved parser vocabulary.

type PlaceholderSQL

type PlaceholderSQL struct {
	Named     string
	Anonymous string
	Parameter string
	// The node CLASS each spelling means in this dialect, empty
	// where the dialect has no such spelling. `@nm` is a Parameter
	// everywhere but DuckDB, where `@` is absolute value.
	DollarName       string
	DollarNumber     string
	AtName           string
	PercentNamed     string
	PercentAnonymous string
	// AnonymousJDBC: PostgreSQL stamps `jdbc` on a bare `?`, and
	// writes that node back as `?` where a plain one is `%s`.
	AnonymousJDBC    bool
	AnonymousJDBCSQL string
	// WidgetPlaceholder: Databricks reads `{name}` as a notebook
	// widget reference, a Placeholder with a flag of its own -- the
	// one dialect where a brace opens a placeholder rather than a
	// struct literal.
	WidgetPlaceholder bool
}

PlaceholderSQL is the text around a bound parameter.

type PropertySpec

type PropertySpec struct {
	Class  string
	Shape  string
	Equals bool
	// Named marks a word that takes a NAME before its list: T-SQL's
	// `ON b (c)` names a partition scheme and the column it splits by.
	// A word without it is handed the whole of `b (c)` as one call.
	Named bool
}

PropertySpec is one word a CREATE may say about the thing it makes: the node it builds, the shape of what follows the word, and whether an equals sign may stand between them.

type Routine

type Routine struct {
	Kind         string // PROCEDURE or FUNCTION; PROC reads as PROCEDURE
	Schema, Name string
	OrAlter      bool // CREATE OR ALTER (or OR REPLACE)
	Params       []Param
	// Returns is what a function returns -- a DataType, the Var TABLE, or a
	// Schema of the columns a named table returns -- and nil for a procedure.
	Returns *Expression
	// Body is a Block of statements, or a function's Return.
	Body *Expression
}

Routine is what a CREATE PROCEDURE or CREATE FUNCTION says about the routine it makes.

func RoutineOf

func RoutineOf(e *Expression) (Routine, error)

RoutineOf reads the routine a CREATE PROCEDURE or CREATE FUNCTION tree makes. Any other tree is an error.

type ScriptResult added in v1.0.4

type ScriptResult struct {
	Tree        *Expression       `json:"-"`
	Diagnostics []ParseDiagnostic `json:"diagnostics"`
	Complete    bool              `json:"complete"`
}

func ParseScript added in v1.0.4

func ParseScript(sql, dialect string) (ScriptResult, error)

type SyntaxTemplate

type SyntaxTemplate struct {
	Keys     []string
	Marked   []string
	Required []FuncConst
	Template string
	// Bracketed are the keys this dialect wraps in parentheses by
	// PRECEDENCE where the operand is not an atom: DuckDB writes
	// `d + INTERVAL 1 DAY` for a literal and `d + INTERVAL (x) DAY` for
	// anything else. The template keeps them bare and the writer puts the
	// brackets back where the operand needs them.
	Bracketed []string
}

type TableSampleSQL

type TableSampleSQL struct {
	Keywords       string
	SeedKeyword    string
	WithMethod     bool
	SizeIsRows     bool
	RequiresParens bool
	SizeIsPercent  bool
}

JSONPerPart is how one path part is written, folded left over the parts: Chain for the operator form, Call for the function form. TableSampleSQL is how a sampling clause is written.

type Token

type Token struct {
	Type     TokenType
	Text     string
	Line     int
	Col      int
	Start    int
	End      int
	Comments []string
}

Token is one lexical token. The fields, and their meanings, are the reference's: Start and End are inclusive rune offsets into the statement, Line and Col are 1-based, and Comments holds the comments attached to this token -- leading comments attach to the token that follows, trailing ones to the token that precedes.

func Tokenize

func Tokenize(sql, dialect string) ([]Token, error)

Tokenize is the one-shot form: tokenize a statement in a dialect.

func (Token) String

func (t Token) String() string

type TokenError

type TokenError struct {
	Msg     string
	Start   int
	End     int
	Snippet string
}

TokenError reports a statement the tokenizer cannot lex -- an unterminated string, most often.

Snippet is a window of the statement around the failure, the same window the reference puts in its message. It is caller text: a table name, a literal, whatever stood there. Error() does not include it, so logging the error cannot leak it; a caller that needs the window reads the field.

func (*TokenError) Error

func (e *TokenError) Error() string

type TokenType

type TokenType int

TokenType identifies a lexical token. Values match the reference's IntEnum.

const (
	TokL_PAREN                 TokenType = 1
	TokR_PAREN                 TokenType = 2
	TokL_BRACKET               TokenType = 3
	TokR_BRACKET               TokenType = 4
	TokL_BRACE                 TokenType = 5
	TokR_BRACE                 TokenType = 6
	TokCOMMA                   TokenType = 7
	TokDOT                     TokenType = 8
	TokDASH                    TokenType = 9
	TokPLUS                    TokenType = 10
	TokCOLON                   TokenType = 11
	TokDOTCOLON                TokenType = 12
	TokDOTCARET                TokenType = 13
	TokDCOLON                  TokenType = 14
	TokDCOLONDOLLAR            TokenType = 15
	TokDCOLONPERCENT           TokenType = 16
	TokDCOLONQMARK             TokenType = 17
	TokDQMARK                  TokenType = 18
	TokSEMICOLON               TokenType = 19
	TokSTAR                    TokenType = 20
	TokBACKSLASH               TokenType = 21
	TokSLASH                   TokenType = 22
	TokLT                      TokenType = 23
	TokLTE                     TokenType = 24
	TokGT                      TokenType = 25
	TokGTE                     TokenType = 26
	TokNOT                     TokenType = 27
	TokEQ                      TokenType = 28
	TokNEQ                     TokenType = 29
	TokNULLSAFE_EQ             TokenType = 30
	TokCOLON_EQ                TokenType = 31
	TokCOLON_GT                TokenType = 32
	TokNCOLON_GT               TokenType = 33
	TokAND                     TokenType = 34
	TokOR                      TokenType = 35
	TokAMP                     TokenType = 36
	TokDPIPE                   TokenType = 37
	TokPIPE_GT                 TokenType = 38
	TokPIPE                    TokenType = 39
	TokPIPE_SLASH              TokenType = 40
	TokDPIPE_SLASH             TokenType = 41
	TokCARET                   TokenType = 42
	TokCARET_AT                TokenType = 43
	TokTILDE                   TokenType = 44
	TokARROW                   TokenType = 45
	TokDARROW                  TokenType = 46
	TokFARROW                  TokenType = 47
	TokHASH                    TokenType = 48
	TokHASH_ARROW              TokenType = 49
	TokDHASH_ARROW             TokenType = 50
	TokLR_ARROW                TokenType = 51
	TokLLRR_ARROW              TokenType = 52
	TokDAT                     TokenType = 53
	TokAT_QMARK                TokenType = 54
	TokLT_AT                   TokenType = 55
	TokAT_GT                   TokenType = 56
	TokDOLLAR                  TokenType = 57
	TokPARAMETER               TokenType = 58
	TokSESSION                 TokenType = 59
	TokSESSION_PARAMETER       TokenType = 60
	TokSESSION_USER            TokenType = 61
	TokDAMP                    TokenType = 62
	TokAMP_LT                  TokenType = 63
	TokAMP_GT                  TokenType = 64
	TokADJACENT                TokenType = 65
	TokXOR                     TokenType = 66
	TokDSTAR                   TokenType = 67
	TokQMARK_AMP               TokenType = 68
	TokQMARK_PIPE              TokenType = 69
	TokHASH_DASH               TokenType = 70
	TokEXCLAMATION             TokenType = 71
	TokURI_START               TokenType = 72
	TokBLOCK_START             TokenType = 73
	TokBLOCK_END               TokenType = 74
	TokSPACE                   TokenType = 75
	TokBREAK                   TokenType = 76
	TokSTRING                  TokenType = 77
	TokNUMBER                  TokenType = 78
	TokIDENTIFIER              TokenType = 79
	TokDATABASE                TokenType = 80
	TokCOLUMN                  TokenType = 81
	TokCOLUMN_DEF              TokenType = 82
	TokSCHEMA                  TokenType = 83
	TokTABLE                   TokenType = 84
	TokWAREHOUSE               TokenType = 85
	TokSTAGE                   TokenType = 86
	TokSTREAM                  TokenType = 87
	TokSTREAMLIT               TokenType = 88
	TokVAR                     TokenType = 89
	TokBIT_STRING              TokenType = 90
	TokHEX_STRING              TokenType = 91
	TokBYTE_STRING             TokenType = 92
	TokNATIONAL_STRING         TokenType = 93
	TokRAW_STRING              TokenType = 94
	TokHEREDOC_STRING          TokenType = 95
	TokUNICODE_STRING          TokenType = 96
	TokBIT                     TokenType = 97
	TokBOOLEAN                 TokenType = 98
	TokTINYINT                 TokenType = 99
	TokUTINYINT                TokenType = 100
	TokSMALLINT                TokenType = 101
	TokUSMALLINT               TokenType = 102
	TokMEDIUMINT               TokenType = 103
	TokUMEDIUMINT              TokenType = 104
	TokINT                     TokenType = 105
	TokUINT                    TokenType = 106
	TokBIGINT                  TokenType = 107
	TokUBIGINT                 TokenType = 108
	TokBIGNUM                  TokenType = 109
	TokINT128                  TokenType = 110
	TokUINT128                 TokenType = 111
	TokINT256                  TokenType = 112
	TokUINT256                 TokenType = 113
	TokFLOAT                   TokenType = 114
	TokDOUBLE                  TokenType = 115
	TokUDOUBLE                 TokenType = 116
	TokDECIMAL                 TokenType = 117
	TokDECIMAL32               TokenType = 118
	TokDECIMAL64               TokenType = 119
	TokDECIMAL128              TokenType = 120
	TokDECIMAL256              TokenType = 121
	TokDECFLOAT                TokenType = 122
	TokUDECIMAL                TokenType = 123
	TokBIGDECIMAL              TokenType = 124
	TokCHAR                    TokenType = 125
	TokNCHAR                   TokenType = 126
	TokVARCHAR                 TokenType = 127
	TokNVARCHAR                TokenType = 128
	TokBPCHAR                  TokenType = 129
	TokTEXT                    TokenType = 130
	TokMEDIUMTEXT              TokenType = 131
	TokLONGTEXT                TokenType = 132
	TokBLOB                    TokenType = 133
	TokMEDIUMBLOB              TokenType = 134
	TokLONGBLOB                TokenType = 135
	TokTINYBLOB                TokenType = 136
	TokTINYTEXT                TokenType = 137
	TokNAME                    TokenType = 138
	TokBINARY                  TokenType = 139
	TokVARBINARY               TokenType = 140
	TokJSON                    TokenType = 141
	TokJSONB                   TokenType = 142
	TokTIME                    TokenType = 143
	TokTIMETZ                  TokenType = 144
	TokTIME_NS                 TokenType = 145
	TokTIMESTAMP               TokenType = 146
	TokTIMESTAMPTZ             TokenType = 147
	TokTIMESTAMPLTZ            TokenType = 148
	TokTIMESTAMPNTZ            TokenType = 149
	TokTIMESTAMP_S             TokenType = 150
	TokTIMESTAMP_MS            TokenType = 151
	TokTIMESTAMP_NS            TokenType = 152
	TokDATETIME                TokenType = 153
	TokDATETIME2               TokenType = 154
	TokDATETIME64              TokenType = 155
	TokSMALLDATETIME           TokenType = 156
	TokDATE                    TokenType = 157
	TokDATE32                  TokenType = 158
	TokINT4RANGE               TokenType = 159
	TokINT4MULTIRANGE          TokenType = 160
	TokINT8RANGE               TokenType = 161
	TokINT8MULTIRANGE          TokenType = 162
	TokNUMRANGE                TokenType = 163
	TokNUMMULTIRANGE           TokenType = 164
	TokTSRANGE                 TokenType = 165
	TokTSMULTIRANGE            TokenType = 166
	TokTSTZRANGE               TokenType = 167
	TokTSTZMULTIRANGE          TokenType = 168
	TokDATERANGE               TokenType = 169
	TokDATEMULTIRANGE          TokenType = 170
	TokUUID                    TokenType = 171
	TokGEOGRAPHY               TokenType = 172
	TokGEOGRAPHYPOINT          TokenType = 173
	TokNULLABLE                TokenType = 174
	TokGEOMETRY                TokenType = 175
	TokPOINT                   TokenType = 176
	TokRING                    TokenType = 177
	TokLINESTRING              TokenType = 178
	TokLOCALTIME               TokenType = 179
	TokLOCALTIMESTAMP          TokenType = 180
	TokSYSTIMESTAMP            TokenType = 181
	TokMULTILINESTRING         TokenType = 182
	TokPOLYGON                 TokenType = 183
	TokMULTIPOLYGON            TokenType = 184
	TokHLLSKETCH               TokenType = 185
	TokHSTORE                  TokenType = 186
	TokSUPER                   TokenType = 187
	TokSERIAL                  TokenType = 188
	TokSMALLSERIAL             TokenType = 189
	TokBIGSERIAL               TokenType = 190
	TokXML                     TokenType = 191
	TokYEAR                    TokenType = 192
	TokUSERDEFINED             TokenType = 193
	TokMONEY                   TokenType = 194
	TokSMALLMONEY              TokenType = 195
	TokROWVERSION              TokenType = 196
	TokIMAGE                   TokenType = 197
	TokVARIANT                 TokenType = 198
	TokOBJECT                  TokenType = 199
	TokINET                    TokenType = 200
	TokIPADDRESS               TokenType = 201
	TokIPPREFIX                TokenType = 202
	TokIPV4                    TokenType = 203
	TokIPV6                    TokenType = 204
	TokENUM                    TokenType = 205
	TokENUM8                   TokenType = 206
	TokENUM16                  TokenType = 207
	TokFIXEDSTRING             TokenType = 208
	TokLOWCARDINALITY          TokenType = 209
	TokNESTED                  TokenType = 210
	TokAGGREGATEFUNCTION       TokenType = 211
	TokSIMPLEAGGREGATEFUNCTION TokenType = 212
	TokTDIGEST                 TokenType = 213
	TokUNKNOWN                 TokenType = 214
	TokVECTOR                  TokenType = 215
	TokDYNAMIC                 TokenType = 216
	TokVOID                    TokenType = 217
	TokALIAS                   TokenType = 218
	TokALTER                   TokenType = 219
	TokALL                     TokenType = 220
	TokANTI                    TokenType = 221
	TokANY                     TokenType = 222
	TokAPPLY                   TokenType = 223
	TokARRAY                   TokenType = 224
	TokASC                     TokenType = 225
	TokASOF                    TokenType = 226
	TokATTACH                  TokenType = 227
	TokAUTO_INCREMENT          TokenType = 228
	TokBEGIN                   TokenType = 229
	TokBETWEEN                 TokenType = 230
	TokBULK_COLLECT_INTO       TokenType = 231
	TokCACHE                   TokenType = 232
	TokCASE                    TokenType = 233
	TokCHARACTER_SET           TokenType = 234
	TokCLUSTER_BY              TokenType = 235
	TokCOLLATE                 TokenType = 236
	TokCOMMAND                 TokenType = 237
	TokCOMMENT                 TokenType = 238
	TokCOMMIT                  TokenType = 239
	TokCONNECT_BY              TokenType = 240
	TokCONSTRAINT              TokenType = 241
	TokCOPY                    TokenType = 242
	TokCREATE                  TokenType = 243
	TokCROSS                   TokenType = 244
	TokCUBE                    TokenType = 245
	TokCURRENT_DATE            TokenType = 246
	TokCURRENT_DATETIME        TokenType = 247
	TokCURRENT_SCHEMA          TokenType = 248
	TokCURRENT_TIME            TokenType = 249
	TokCURRENT_TIMESTAMP       TokenType = 250
	TokCURRENT_USER            TokenType = 251
	TokCURRENT_USER_ID         TokenType = 252
	TokCURRENT_ROLE            TokenType = 253
	TokCURRENT_CATALOG         TokenType = 254
	TokDECLARE                 TokenType = 255
	TokDEFAULT                 TokenType = 256
	TokDELETE                  TokenType = 257
	TokDESC                    TokenType = 258
	TokDESCRIBE                TokenType = 259
	TokDETACH                  TokenType = 260
	TokDICTIONARY              TokenType = 261
	TokDISTINCT                TokenType = 262
	TokDISTRIBUTE_BY           TokenType = 263
	TokDIV                     TokenType = 264
	TokDROP                    TokenType = 265
	TokELSE                    TokenType = 266
	TokEND                     TokenType = 267
	TokESCAPE                  TokenType = 268
	TokEXCEPT                  TokenType = 269
	TokEXECUTE                 TokenType = 270
	TokEXISTS                  TokenType = 271
	TokFALSE                   TokenType = 272
	TokFETCH                   TokenType = 273
	TokFILE                    TokenType = 274
	TokFILE_FORMAT             TokenType = 275
	TokFILTER                  TokenType = 276
	TokFINAL                   TokenType = 277
	TokFIRST                   TokenType = 278
	TokFOR                     TokenType = 279
	TokFORCE                   TokenType = 280
	TokFOREIGN_KEY             TokenType = 281
	TokFORMAT                  TokenType = 282
	TokFROM                    TokenType = 283
	TokFULL                    TokenType = 284
	TokFUNCTION                TokenType = 285
	TokGET                     TokenType = 286
	TokGLOB                    TokenType = 287
	TokGLOBAL                  TokenType = 288
	TokGRANT                   TokenType = 289
	TokGROUP_BY                TokenType = 290
	TokGROUPING_SETS           TokenType = 291
	TokHAVING                  TokenType = 292
	TokHINT                    TokenType = 293
	TokIGNORE                  TokenType = 294
	TokILIKE                   TokenType = 295
	TokIN                      TokenType = 296
	TokINDEX                   TokenType = 297
	TokINDEXED_BY              TokenType = 298
	TokINNER                   TokenType = 299
	TokINSERT                  TokenType = 300
	TokINSTALL                 TokenType = 301
	TokINTEGRATION             TokenType = 302
	TokINTERSECT               TokenType = 303
	TokINTERVAL                TokenType = 304
	TokINTO                    TokenType = 305
	TokINTRODUCER              TokenType = 306
	TokIRLIKE                  TokenType = 307
	TokIS                      TokenType = 308
	TokISNULL                  TokenType = 309
	TokJOIN                    TokenType = 310
	TokJOIN_MARKER             TokenType = 311
	TokKEEP                    TokenType = 312
	TokKEY                     TokenType = 313
	TokKILL                    TokenType = 314
	TokLANGUAGE                TokenType = 315
	TokLATERAL                 TokenType = 316
	TokLEFT                    TokenType = 317
	TokLIKE                    TokenType = 318
	TokLIMIT                   TokenType = 319
	TokLIST                    TokenType = 320
	TokLOAD                    TokenType = 321
	TokLOCK                    TokenType = 322
	TokMAP                     TokenType = 323
	TokMATCH                   TokenType = 324
	TokMATCH_CONDITION         TokenType = 325
	TokMATCH_RECOGNIZE         TokenType = 326
	TokMEMBER_OF               TokenType = 327
	TokMERGE                   TokenType = 328
	TokMOD                     TokenType = 329
	TokMODEL                   TokenType = 330
	TokNATURAL                 TokenType = 331
	TokNEXT                    TokenType = 332
	TokNOTHING                 TokenType = 333
	TokNOTNULL                 TokenType = 334
	TokNULL                    TokenType = 335
	TokOBJECT_IDENTIFIER       TokenType = 336
	TokOFFSET                  TokenType = 337
	TokON                      TokenType = 338
	TokONLY                    TokenType = 339
	TokOPERATOR                TokenType = 340
	TokORDER_BY                TokenType = 341
	TokORDER_SIBLINGS_BY       TokenType = 342
	TokORDERED                 TokenType = 343
	TokORDINALITY              TokenType = 344
	TokOUT                     TokenType = 345
	TokINOUT                   TokenType = 346
	TokOUTER                   TokenType = 347
	TokOVER                    TokenType = 348
	TokOVERLAPS                TokenType = 349
	TokOVERWRITE               TokenType = 350
	TokPACKAGE                 TokenType = 351
	TokPARTITION               TokenType = 352
	TokPARTITION_BY            TokenType = 353
	TokPERCENT                 TokenType = 354
	TokPIVOT                   TokenType = 355
	TokPLACEHOLDER             TokenType = 356
	TokPOLICY                  TokenType = 357
	TokPOOL                    TokenType = 358
	TokPOSITIONAL              TokenType = 359
	TokPRAGMA                  TokenType = 360
	TokPREWHERE                TokenType = 361
	TokPRIMARY_KEY             TokenType = 362
	TokPROCEDURE               TokenType = 363
	TokPROPERTIES              TokenType = 364
	TokPROJECTION              TokenType = 365
	TokPSEUDO_TYPE             TokenType = 366
	TokPUT                     TokenType = 367
	TokQUALIFY                 TokenType = 368
	TokQUOTE                   TokenType = 369
	TokQDCOLON                 TokenType = 370
	TokRANGE                   TokenType = 371
	TokRECURSIVE               TokenType = 372
	TokREFRESH                 TokenType = 373
	TokRENAME                  TokenType = 374
	TokREPLACE                 TokenType = 375
	TokRETURNING               TokenType = 376
	TokREVOKE                  TokenType = 377
	TokREFERENCES              TokenType = 378
	TokRIGHT                   TokenType = 379
	TokRLIKE                   TokenType = 380
	TokROLE                    TokenType = 381
	TokROLLBACK                TokenType = 382
	TokROLLUP                  TokenType = 383
	TokROW                     TokenType = 384
	TokROWS                    TokenType = 385
	TokRULE                    TokenType = 386
	TokSELECT                  TokenType = 387
	TokSEMI                    TokenType = 388
	TokSEPARATOR               TokenType = 389
	TokSEQUENCE                TokenType = 390
	TokSERDE_PROPERTIES        TokenType = 391
	TokSET                     TokenType = 392
	TokSETTINGS                TokenType = 393
	TokSHOW                    TokenType = 394
	TokSIMILAR_TO              TokenType = 395
	TokSOME                    TokenType = 396
	TokSORT_BY                 TokenType = 397
	TokSOUNDS_LIKE             TokenType = 398
	TokSQL_SECURITY            TokenType = 399
	TokSTART_WITH              TokenType = 400
	TokSTORAGE_INTEGRATION     TokenType = 401
	TokSTRAIGHT_JOIN           TokenType = 402
	TokSTRUCT                  TokenType = 403
	TokSUMMARIZE               TokenType = 404
	TokTABLE_SAMPLE            TokenType = 405
	TokTAG                     TokenType = 406
	TokTEMPORARY               TokenType = 407
	TokTOP                     TokenType = 408
	TokTHEN                    TokenType = 409
	TokTRUE                    TokenType = 410
	TokTRUNCATE                TokenType = 411
	TokTRIGGER                 TokenType = 412
	TokTYPE                    TokenType = 413
	TokUNCACHE                 TokenType = 414
	TokUNDROP                  TokenType = 415
	TokUNION                   TokenType = 416
	TokUNNEST                  TokenType = 417
	TokUNPIVOT                 TokenType = 418
	TokUPDATE                  TokenType = 419
	TokUSE                     TokenType = 420
	TokUSING                   TokenType = 421
	TokVALUES                  TokenType = 422
	TokVARIADIC                TokenType = 423
	TokVIEW                    TokenType = 424
	TokSEMANTIC_VIEW           TokenType = 425
	TokVOLATILE                TokenType = 426
	TokVOLUME                  TokenType = 427
	TokWHEN                    TokenType = 428
	TokWHERE                   TokenType = 429
	TokWINDOW                  TokenType = 430
	TokWITH                    TokenType = 431
	TokUNIQUE                  TokenType = 432
	TokUTC_DATE                TokenType = 433
	TokUTC_TIME                TokenType = 434
	TokUTC_TIMESTAMP           TokenType = 435
	TokOPTION                  TokenType = 436
	TokSINK                    TokenType = 437
	TokSOURCE                  TokenType = 438
	TokANALYZE                 TokenType = 439
	TokNAMESPACE               TokenType = 440
	TokEXPORT                  TokenType = 441
	TokHIVE_TOKEN_STREAM       TokenType = 442
	TokSENTINEL                TokenType = 443
)

func TokenTypeByName

func TokenTypeByName(name string) (TokenType, bool)

TokenTypeByName looks a token type up by its reference name, for tests and for reading the token expectations.

func (TokenType) String

func (t TokenType) String() string

String renders the token type the way the reference's __str__ does.

type Tokenizer

type Tokenizer struct {
	// contains filtered or unexported fields
}

Tokenizer turns SQL into tokens for one dialect. It is a direct port of the reference's TokenizerCore: same scan order, same lookahead, same positions. It is not safe for concurrent use; make one per goroutine, they are cheap.

func NewTokenizer

func NewTokenizer(dialect string) (*Tokenizer, error)

NewTokenizer returns a tokenizer for a dialect. The empty name is the dialect-neutral default.

func (*Tokenizer) Config

func (t *Tokenizer) Config() *Config

Config exposes the dialect tables this tokenizer reads.

func (*Tokenizer) Tokenize

func (t *Tokenizer) Tokenize(sql string) (toks []Token, err error)

Tokenize returns the tokens of one SQL string.

type TypeDispatch

type TypeDispatch struct {
	Index   int
	Default FuncSpec
	ByType  map[string]FuncSpec
}

FuncSpec is how one function name becomes a node: the class, and what fills each of its argument keys. TypeDispatch is a name that builds a different node depending on the type of one of its arguments.

type UnsupportedError

type UnsupportedError struct {
	Construct string
	Token     string
	Start     int
	End       int
	HasRange  bool
	// TokenIsKeyword reports whether Token is dialect vocabulary rather than
	// something the caller wrote. Only a keyword is safe to record.
	TokenIsKeyword bool
}

UnsupportedError names the construct the port could not read.

The construct is the field worth recording. A consumer that wants to know what to build next needs "PIVOT" or "window function", and reading that back out of a formatted message would be a parser of its own -- the same reason NotAQueryError carries Kind.

Construct is SQL VOCABULARY and safe to log: the seven call sites that interpolate do so with a function name, a type, a join method or a clause keyword, never with a caller's identifier. Token is the opposite -- it is whatever the statement said at the point of failure, which can be a table or column name -- so it is available to a caller that has a reason for it and is not part of Error().

func (*UnsupportedError) Error

func (e *UnsupportedError) Error() string

func (*UnsupportedError) Is

func (e *UnsupportedError) Is(target error) bool

Is makes errors.Is(err, ErrUnsupported) hold for this error too.

func (*UnsupportedError) Label

func (e *UnsupportedError) Label() string

Label is the construct, named as precisely as can be done SAFELY -- the string to count when deciding what to build next.

Construct alone is not always enough. A window function and a PIVOT both stop the parser at "trailing tokens", the second largest refusal bucket in the fixture corpus, and the two need entirely different work. What separates them is the token: OVER, PIVOT. Those are keywords -- dialect vocabulary, fixed and public -- so appending one narrows the label without recording anything the caller wrote. When the token is an identifier, a string or a number, it is omitted, because a refusal on `WHERE email = '...'` must not put that anywhere.

type ValueDispatch

type ValueDispatch struct {
	Index   int
	Default FuncSpec
	ByValue map[string]FuncSpec
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL