mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
* squashed 45 commits into one :) * tlt/sql experiment (#2035) * Move PlanOperator to sql3/planner/types package includes: type PlanOperatorColumn struct type PlanOperator interface * Remove planner dependencies from pilosa package The goal after this is to prevent the planner package (which doesn't exist yet) from being imported by the pilosa package; we just want it injected into the server in server/server.go. This is because the planner package uses pilosa types, so we need to avoid circular dependencies. Added ExecutionPlannerFn Make public: pilosa.ExecOptions Added a pilosa.Executor interface Added a planner.types.CompilePlanner interface Isolated the planner calls to: - Executor.Execute() - *API.[method]() * Move executionplanner files into the sql3/planner package. This required a bit of gymnastics, and there are some things around FieldOptions which need to be addressed soon. * Remove the hacky FieldOptions stuff I added earlier This implementation just uses the pilosa.FieldOption functional options provided by the API (as opposed to trying to build a FieldOptions object. It also changes field types to constants. These are private for now, but if we need to make them public, we should put them in the planner/types package. * Implement the "scale" value from Decimal(scale) Also, precision and scale were currently reversed in the parser. This fixes that. * Modify the parser to handle CACHETYPE <type> SIZE <size> It's a little odd to me that the cache type values are Tokens, but I guess it's ok. One thing to keep in mind is that FeatureBase expects lowercase values, so this commit changes the parser to set the value to the lowercase version of the type. * Fix the /sql2 tests This entailed a combination of commenting out or t.Skip()-ing tests which covered code in the parser that has been commented out or removed as not currently supported in sql3. It also adds some coverage for the sql.Contraint stringers. * Prevent JSON sql results from containing closing commas This commit just re-works the existing output code to avoid inserting closing commas (which results in invalid JSON). * Enhance the CREATE TABLE test coverage. In particular, ensure that the fields which get created in FeatureBase are what we expect based on the fields defined in the CREATE TABLE statement. This also ensures that the TIMEQUANTUM and CACHETYPE contraints are not provided for the same field (since those constraints are not supported together). * Adjust the EBNF file to indicate SIZE contraint is optional A CACHETYPE can be provided without a SIZE. This change indicates that SIZE is optional. * Remove `executionplanner_` from file names (#2040) * implementation of ALTER TABLE (sans column RENAME) * refactored expression analysis; added more robust type checking; all unary and bin ops function on ints * added type support for expressions; full bin/unary op support; added cast; more literal support * cast int to all other types * all literals (except idset, stringset & timestamp) make it thru; cast to all types with int as source now works * implemented LIKE/NOT LIKE * Implemented IS [NOT] NULL * Move sql2 files into sql3/parser package (#2045) * Move sql2 files into sql3/parser package This also removes the sql2 package. * Fix tests which were typing _id fields as INT intead of ID * implemented BETWEEN, NOT BETWEEN * Add featurebase/error package (#2046) * Add featurebase/error package I copied the `dax/errors` package which I am starting to use in the DAX prototype into `featurebase/errors` in order to start using it with the sql3 package. It's basically a wrapper around `github.com/pkg/errors`, but it uses a customer coded error. The sql package can define its own errors based on the `featurebase/errors` types. Then do things like `Wrap()` and `Is()`. * Address the linter complaints: shadowed variables, unreachable code * implemented IN & NOT IN with expression lists * first cut of CASE * Fixed some errors from rebase * updated bnf; removed unused code; tightened up error handling * first crack at basic CLI for SQL3 Use: `featurebase cli` Still lots to do here, but for example: > select count(*) from tremor +--------------+ | COUNT | +--------------+ | 1.158321e+06 | +--------------+ * Iterate on the CLI (#2057) Handle the errors. Add an "exit" command. Add some general formatting and white space. Add termination character: ";" (semicolon) This commit allows a user to provide multiple or partial SQL statements. Example of multiple statements: ``` show tables; select * from foo; ``` Example of partial (multi-line) statements: ``` select * from foo; ``` Don't uppercase the header values * error refactoring; first cut of TOP; remove unused code; use log.Printf instead of fmt.Printf * fixed a bug with QualifiedRef from refactoring; added bones of INSERT; removal of unused code; tightened up errors more; fixed failing tests * single value list for INSERT * Update bnf per discussion with Travis; INSERT now doing the requisite stuff * Pat's eyes went square - nothing wrong with TOP, Pat needed to learn arrays again. * improved some errors; fixed tests to suit * send warnings back in the api; update CLI to display warnings * start warning on stuff not implemented so we don't get bugged about it * Tlt/sql experiment (#2063) * Expresssion -> Expression * Add SQL planner test - adds a test to which it is easier to add tables and SQL statments - un-exports all of the expression types - removes the planner pointer from the expression types (it can be added back later if need be) * Fix where clause on a string field Prior to this commit, the binary expression for a where clause on a string field was building the call by providing a range operator which is typically used for BSI fields. This changes it to use the call.Args for string values. * Update planner tests to handle multiple sql for the same results * Reorganize SQL tests Introduce a test/helpers package and move shared MustQueryRows into that package. * Add a compatibility map for field types. (#2064) This is primarily to address the fact that ID fields were previously incompatible with INT literals. We should probably consider introducing a custom type for FieldType which can be used to define compatibilities. * significantly refactored type checking * Handle nil (NULL) values in the sql CLI. (#2067) go-pretty panics if the interface{} field value is nil. This replaces nil values with a "NULL" string. * Squash some commits fixed a still failing test added line, col to all error messages refactored source handling to enable table aliases fixed some copypasta per review warnings for order by & topn; implemented select as a source starting to handle in (select...); added stub for optimizer JSON-encode the sql error and warning strings (#2069) Error strings with unencoded characters (like double quotes) were resulting in invalid json. got insert working; added symbol table; added concrete optimizer; added nascent NestedLoopsOperator; rewrite "where foo in (select..." as inner join * all about the sets (#2085) * implemented setcontains() * implemented set literal; insert set column values; setcontains/all/any both in expr eval and pql filters * Convert test to use latest framework. (#2086) * fixed some comments * removed refactored tests Co-authored-by: Travis Turner <travis@pilosa.com> * Add support for Decimal fields to the sql test. (#2090) * dates (#2094) * return dates as strings in output; tightened up decimal type checking * return dates as strings in output; tightened up decimal type checking * fixed failing tests after decimal changes * can now insert decimal values * implemented insert for timestamp data type; implemented current_date, current_timestamp constants * fixed some failing tests * handle date literals from strings in insert statements * changes from feedback * Fix pointer method error * sql3 API interface (#2110) * Introduce API-related interfaces: SchemaAPI, ComputeAPI The sql3 code was relying on the pointer: *pilosa.API in order to call API methods directly on the local node. If we want to import and use the sql3 package in another service (the DAX queryer, for example), we need to be able to use an implementation of an interface for those API method calls. This commit introduces two interfaces, both automatically implemented by pilosa.API: - SchemaAPI - ComputeAPI * Convert sql3 code to use IndexInfo instead of Index The sql3 code was relying on a *pilosa.Index and its methods to get general information like index and field name, type, etc. This commit converts everything to use a *pilosa.IndexInfo instead. This allows us to modify the SchemaAPI interface to also return IndexInfo instead of Index, which will be a lot easier to implement in a non-pilosa package (like DAX); creating a *pilosa.Index requires providing things like data directory paths and holders, which are not necessary for these use cases. * Unary and Binary Ops R US plus CAST (#2111) * implemented string literal for timestamp epoch * fixed failing test * fixed the failing test again * refactored tests; implemented unary op tests for all datatypes; implemented binop tests for int/int, int/id, int/decimal & ID/int * implemented all binary ops for INT & all other types, ID & all other types * implemented binary ops for DECIMAL types & all other types * added STRING & BOOL to various tests; implemented all remaining binOp tests * fix up some stuff after rebasing * refactored test defs into multiple files; implemented CAST for every datatype * added tests for like/not like * addressed review feedback * addressed type review feedback * tightened up IS [NOT] NULL behavior plus tests (#2118) * tightened up IS [NOT] NULL behavior plus tests * BETWEEN/NOT BETWEEN with all data types * addressed review feedback * Handle negative integers in column min/max constraints (#2120) This commit parses the min/max contraint as an expression, as opposed to an int literal, so that negative values are treated as Unary expressions. There currently isn't support for min/max constraints on `decimal` fiels, so for now this change only expects +/- integer values. * Implement the CREATE TABLE keypartitions logic (#2123) * Execution time, IN/NOT IN & multiple aggregates (#2124) * added display of execution time * IN/NOT IN tests for all data types * fixed date parsing * removed duplicative tests * refactoring aggregates * suport multiple aggregates * Address review feedback * final round of feedback * Add method SchemaAPI.CreateIndexAndFields() (#2127) In order to support a CREATE TABLE statement as a single command, this commit alters the SchemaAPI interface to contain a single method which handles both the index and its fields. It also updates the sql3 code to use this interface instead of CreateIndex() and CreateField() indepedently. * Symbol Handling (Again) (#2129) * Refactored symbol handling in the planner; re-instated the select as source tests * removed commented out code * addressing review feedback * Move hard-coded _id field out of planner and into interface implementation (#2130) This commit moves the hard-coded addition of the `_id` field from the planner to the SchemaAPI.IndexInfo() implementation method. NOTE: If anything was expecting SchemaAPI.Schema() to also return the `_id` field as part of its field list in each table, then it would not be there because the `_id` field is only added in the IndexInfo() method for now. Currently that's not a problem because nothing is expecting the `_id` field for `Schema()`. * Multiple aggregates, all aggregates stand alone and in GROUP BY (#2132) * handle multiple aggregates in group by queries * added handling for avg() aggregate both stand alone and in group by * tightened up sum & avg outside of group by * added min, max & percentile * added warnings * Make MaterializedRowSet implement the PlanOperator interface. (#2133) This commit refactors the PQLMultiGroupByOperator to have a PlanOperator as its output. Then, when it initializes, it sets up a MaterializedRowSet and populates that with the values from the multiple group by operations. * added explicit min/max pql operators * saved a file I forgot to save * per review * Un-indent some if/else nesting (#2136) Co-authored-by: Travis Turner <travis@pilosa.com> * Add optional `name` argument to test structs. This commit adds the `name` argument to `tableTest` and `sqlTest` so that a test can be optionally named. This allows a developer to more easily run/identify a particular test by name. * Inbuilt functions (redux) (#2141) * set functions type parameter type checking * implemented datepart * include SQL3 type in SHOW COLUMNS output * fixed select as source; failing SHOW COLUMNS test * select in select list * dump output columns; handle optimization for select list subqueries * make it an error to return multiple rows for a select list subquery * added description * contants and test coverage for datepart function * SQL3 Refactor-palooza (#2182) * removed unneeded IsAggregate() * first cut of working nested loops operator aka INNER JOIN * remove selectListItemPlanExpression * added some warnings * all the tests are passing again! * addressed some linter complaints * added basic order by * bug fixes; added 'or replace'/'replace' to insert * for insert references should return appropriately * added back ability to use subquery singleton expressions * removed dead code; fixed test * json-able plan, Schema() plus refactoring * fixed dumb code * add some tests for time quantum behavior * Code cleanup during review. Also fixed INSERT to keyed table bug. This commit contains a lot of minor adjustments made during code review. It also contains a bug fix that was preventing INSERT into a keyed table (i.e. _id type STRING) from working. Co-authored-by: Travis Turner <travis@molecula.com> * Fix expected min/max on timestamp column test (decimal field) I don't know why this changed, but presumably something to do with decimal related work that happened on master. * Fix compile problem after rebase * review feedback Co-authored-by: Matthew Jaffee <jaffee@pilosa.com> Co-authored-by: Travis Turner <travis@pilosa.com> Co-authored-by: Travis Turner <travis@molecula.com> Co-authored-by: Fletcher Haynes <fletcher@capitalprawn.com>
4019 lines
99 KiB
Go
4019 lines
99 KiB
Go
// Copyright 2021 Molecula Corp. All rights reserved.
|
|
package parser
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Node interface {
|
|
node()
|
|
fmt.Stringer
|
|
}
|
|
|
|
func (*AlterTableStatement) node() {}
|
|
func (*AnalyzeStatement) node() {}
|
|
func (*Assignment) node() {}
|
|
func (*ShowTablesStatement) node() {}
|
|
func (*ShowColumnsStatement) node() {}
|
|
func (*BeginStatement) node() {}
|
|
func (*BinaryExpr) node() {}
|
|
func (*BoolLit) node() {}
|
|
func (*BulkInsertStatement) node() {}
|
|
func (*CacheTypeConstraint) node() {}
|
|
func (*Call) node() {}
|
|
func (*CaseBlock) node() {}
|
|
func (*CaseExpr) node() {}
|
|
func (*CastExpr) node() {}
|
|
func (*CheckConstraint) node() {}
|
|
func (*ColumnDefinition) node() {}
|
|
func (*CommitStatement) node() {}
|
|
func (*CreateIndexStatement) node() {}
|
|
func (*CreateTableStatement) node() {}
|
|
func (*CreateTriggerStatement) node() {}
|
|
func (*CreateViewStatement) node() {}
|
|
func (*DateLit) node() {}
|
|
func (*DefaultConstraint) node() {}
|
|
func (*DeleteStatement) node() {}
|
|
func (*DropIndexStatement) node() {}
|
|
func (*DropTableStatement) node() {}
|
|
func (*DropTriggerStatement) node() {}
|
|
func (*DropViewStatement) node() {}
|
|
func (*Exists) node() {}
|
|
func (*ExplainStatement) node() {}
|
|
func (*ExprList) node() {}
|
|
func (*FilterClause) node() {}
|
|
func (*FloatLit) node() {}
|
|
func (*ForeignKeyArg) node() {}
|
|
func (*ForeignKeyConstraint) node() {}
|
|
func (*FrameSpec) node() {}
|
|
func (*Ident) node() {}
|
|
func (*IndexedColumn) node() {}
|
|
func (*InsertStatement) node() {}
|
|
func (*JoinClause) node() {}
|
|
func (*JoinOperator) node() {}
|
|
func (*KeyPartitionsOption) node() {}
|
|
func (*MinConstraint) node() {}
|
|
func (*MaxConstraint) node() {}
|
|
func (*NotNullConstraint) node() {}
|
|
func (*NullLit) node() {}
|
|
func (*IntegerLit) node() {}
|
|
func (*OnConstraint) node() {}
|
|
func (*OrderingTerm) node() {}
|
|
func (*OverClause) node() {}
|
|
func (*ParenExpr) node() {}
|
|
func (*SetLiteralExpr) node() {}
|
|
func (*ParenSource) node() {}
|
|
func (*PrimaryKeyConstraint) node() {}
|
|
func (*QualifiedRef) node() {}
|
|
func (*QualifiedTableName) node() {}
|
|
func (*Range) node() {}
|
|
func (*ReleaseStatement) node() {}
|
|
func (*ResultColumn) node() {}
|
|
func (*RollbackStatement) node() {}
|
|
func (*SavepointStatement) node() {}
|
|
func (*SelectStatement) node() {}
|
|
func (*ShardWidthOption) node() {}
|
|
func (*StringLit) node() {}
|
|
func (*TimeUnitConstraint) node() {}
|
|
func (*TimeQuantumConstraint) node() {}
|
|
func (*Type) node() {}
|
|
func (*UnaryExpr) node() {}
|
|
func (*UniqueConstraint) node() {}
|
|
func (*UpdateStatement) node() {}
|
|
func (*UpsertClause) node() {}
|
|
func (*UsingConstraint) node() {}
|
|
func (*Window) node() {}
|
|
func (*WindowDefinition) node() {}
|
|
func (*WithClause) node() {}
|
|
|
|
type Statement interface {
|
|
Node
|
|
stmt()
|
|
}
|
|
|
|
func (*AlterTableStatement) stmt() {}
|
|
func (*AnalyzeStatement) stmt() {}
|
|
func (*BeginStatement) stmt() {}
|
|
func (*BulkInsertStatement) stmt() {}
|
|
func (*ShowTablesStatement) stmt() {}
|
|
func (*ShowColumnsStatement) stmt() {}
|
|
func (*CommitStatement) stmt() {}
|
|
func (*CreateIndexStatement) stmt() {}
|
|
func (*CreateTableStatement) stmt() {}
|
|
func (*CreateTriggerStatement) stmt() {}
|
|
func (*CreateViewStatement) stmt() {}
|
|
func (*DeleteStatement) stmt() {}
|
|
func (*DropIndexStatement) stmt() {}
|
|
func (*DropTableStatement) stmt() {}
|
|
func (*DropTriggerStatement) stmt() {}
|
|
func (*DropViewStatement) stmt() {}
|
|
func (*ExplainStatement) stmt() {}
|
|
func (*InsertStatement) stmt() {}
|
|
func (*ReleaseStatement) stmt() {}
|
|
func (*RollbackStatement) stmt() {}
|
|
func (*SavepointStatement) stmt() {}
|
|
func (*SelectStatement) stmt() {}
|
|
func (*UpdateStatement) stmt() {}
|
|
|
|
// CloneStatement returns a deep copy stmt.
|
|
func CloneStatement(stmt Statement) Statement {
|
|
if stmt == nil {
|
|
return nil
|
|
}
|
|
|
|
switch stmt := stmt.(type) {
|
|
case *AlterTableStatement:
|
|
return stmt.Clone()
|
|
case *AnalyzeStatement:
|
|
return stmt.Clone()
|
|
case *BeginStatement:
|
|
return stmt.Clone()
|
|
case *CommitStatement:
|
|
return stmt.Clone()
|
|
case *CreateIndexStatement:
|
|
return stmt.Clone()
|
|
case *CreateTableStatement:
|
|
return stmt.Clone()
|
|
case *CreateTriggerStatement:
|
|
return stmt.Clone()
|
|
case *CreateViewStatement:
|
|
return stmt.Clone()
|
|
case *DeleteStatement:
|
|
return stmt.Clone()
|
|
case *DropIndexStatement:
|
|
return stmt.Clone()
|
|
case *DropTableStatement:
|
|
return stmt.Clone()
|
|
case *DropTriggerStatement:
|
|
return stmt.Clone()
|
|
case *DropViewStatement:
|
|
return stmt.Clone()
|
|
case *ExplainStatement:
|
|
return stmt.Clone()
|
|
case *InsertStatement:
|
|
return stmt.Clone()
|
|
case *ReleaseStatement:
|
|
return stmt.Clone()
|
|
case *RollbackStatement:
|
|
return stmt.Clone()
|
|
case *SavepointStatement:
|
|
return stmt.Clone()
|
|
case *SelectStatement:
|
|
return stmt.Clone()
|
|
case *UpdateStatement:
|
|
return stmt.Clone()
|
|
default:
|
|
panic(fmt.Sprintf("invalid statement type: %T", stmt))
|
|
}
|
|
}
|
|
|
|
func cloneStatements(a []Statement) []Statement {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]Statement, len(a))
|
|
for i := range a {
|
|
other[i] = CloneStatement(a[i])
|
|
}
|
|
return other
|
|
}
|
|
|
|
// StatementSource returns the root statement for a statement.
|
|
func StatementSource(stmt Statement) Source {
|
|
switch stmt := stmt.(type) {
|
|
case *SelectStatement:
|
|
return stmt.Source
|
|
case *UpdateStatement:
|
|
return stmt.Table
|
|
case *DeleteStatement:
|
|
return stmt.Table
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type Expr interface {
|
|
Node
|
|
expr()
|
|
|
|
IsLiteral() bool
|
|
DataType() ExprDataType
|
|
Pos() Pos
|
|
}
|
|
|
|
func (*BinaryExpr) expr() {}
|
|
func (*BoolLit) expr() {}
|
|
func (*Call) expr() {}
|
|
func (*CaseExpr) expr() {}
|
|
func (*CaseBlock) expr() {}
|
|
func (*CastExpr) expr() {}
|
|
func (*DateLit) expr() {}
|
|
func (*Exists) expr() {}
|
|
func (*ExprList) expr() {}
|
|
func (*Ident) expr() {}
|
|
func (*NullLit) expr() {}
|
|
func (*IntegerLit) expr() {}
|
|
func (*FloatLit) expr() {}
|
|
func (*ParenExpr) expr() {}
|
|
func (*SetLiteralExpr) expr() {}
|
|
func (*QualifiedRef) expr() {}
|
|
func (*Range) expr() {}
|
|
func (*StringLit) expr() {}
|
|
func (*UnaryExpr) expr() {}
|
|
func (*SelectStatement) expr() {}
|
|
|
|
// CloneExpr returns a deep copy expr.
|
|
func CloneExpr(expr Expr) Expr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
switch expr := expr.(type) {
|
|
case *BinaryExpr:
|
|
return expr.Clone()
|
|
case *BoolLit:
|
|
return expr.Clone()
|
|
case *Call:
|
|
return expr.Clone()
|
|
case *CaseExpr:
|
|
return expr.Clone()
|
|
case *CastExpr:
|
|
return expr.Clone()
|
|
case *Exists:
|
|
return expr.Clone()
|
|
case *ExprList:
|
|
return expr.Clone()
|
|
case *Ident:
|
|
return expr.Clone()
|
|
case *NullLit:
|
|
return expr.Clone()
|
|
case *IntegerLit:
|
|
return expr.Clone()
|
|
case *ParenExpr:
|
|
return expr.Clone()
|
|
case *QualifiedRef:
|
|
return expr.Clone()
|
|
case *Range:
|
|
return expr.Clone()
|
|
case *StringLit:
|
|
return expr.Clone()
|
|
case *UnaryExpr:
|
|
return expr.Clone()
|
|
default:
|
|
panic(fmt.Sprintf("invalid expr type: %T", expr))
|
|
}
|
|
}
|
|
|
|
func cloneExprs(a []Expr) []Expr {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]Expr, len(a))
|
|
for i := range a {
|
|
other[i] = CloneExpr(a[i])
|
|
}
|
|
return other
|
|
}
|
|
|
|
// ExprString returns the string representation of expr.
|
|
// Returns a blank string if expr is nil.
|
|
func ExprString(expr Expr) string {
|
|
if expr == nil {
|
|
return ""
|
|
}
|
|
return expr.String()
|
|
}
|
|
|
|
// SplitExprTree splits apart expr so it is a list of all AND joined expressions.
|
|
// For example, the expression "A AND B AND (C OR (D AND E))" would be split into
|
|
// a list of "A", "B", "C OR (D AND E)".
|
|
func SplitExprTree(expr Expr) []Expr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
var a []Expr
|
|
splitExprTree(expr, &a)
|
|
return a
|
|
}
|
|
|
|
func splitExprTree(expr Expr, a *[]Expr) {
|
|
switch expr := expr.(type) {
|
|
case *BinaryExpr:
|
|
if expr.Op != AND {
|
|
*a = append(*a, expr)
|
|
return
|
|
}
|
|
splitExprTree(expr.X, a)
|
|
splitExprTree(expr.Y, a)
|
|
case *ParenExpr:
|
|
splitExprTree(expr.X, a)
|
|
default:
|
|
*a = append(*a, expr)
|
|
}
|
|
}
|
|
|
|
// SourceOutputColumn is an identifier that is either a possible output column
|
|
// for a Source or a referenced output column for a Source. These are computed during
|
|
// the analysis phase
|
|
type SourceOutputColumn struct {
|
|
TableName string
|
|
ColumnName string
|
|
ColumnIndex int
|
|
Datatype ExprDataType
|
|
}
|
|
|
|
// Source represents a data source for a select statement.
|
|
// A select statement has one source, but they can be one of a table ref, a join,
|
|
// another select statement or any of the above parenthesiszed. For join operators, the Source
|
|
// can form a graph, with the join terms being themselves a Source.
|
|
type Source interface {
|
|
Node
|
|
source()
|
|
SourceFromAlias(alias string) Source
|
|
|
|
// get the possible output columns from the source
|
|
PossibleOutputColumns() []*SourceOutputColumn
|
|
|
|
// find output columns by name
|
|
OutputColumnNamed(name string) (*SourceOutputColumn, error)
|
|
OutputColumnQualifierNamed(qualifier string, name string) (*SourceOutputColumn, error)
|
|
}
|
|
|
|
func (*JoinClause) source() {}
|
|
func (*ParenSource) source() {}
|
|
func (*QualifiedTableName) source() {}
|
|
func (*SelectStatement) source() {}
|
|
|
|
// CloneSource returns a deep copy src.
|
|
func CloneSource(src Source) Source {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
switch src := src.(type) {
|
|
case *JoinClause:
|
|
return src.Clone()
|
|
case *ParenSource:
|
|
return src.Clone()
|
|
case *QualifiedTableName:
|
|
return src.Clone()
|
|
case *SelectStatement:
|
|
return src.Clone()
|
|
default:
|
|
panic(fmt.Sprintf("invalid source type: %T", src))
|
|
}
|
|
}
|
|
|
|
// SourceList returns a list of sources starting from a source.
|
|
func SourceList(src Source) []Source {
|
|
var a []Source
|
|
ForEachSource(src, func(s Source) bool {
|
|
a = append(a, s)
|
|
return true
|
|
})
|
|
return a
|
|
}
|
|
|
|
// ForEachSource calls fn for every source within the current scope.
|
|
// Stops iteration if fn returns false.
|
|
func ForEachSource(src Source, fn func(Source) bool) {
|
|
forEachSource(src, fn)
|
|
}
|
|
|
|
func forEachSource(src Source, fn func(Source) bool) bool {
|
|
if !fn(src) {
|
|
return false
|
|
}
|
|
|
|
switch src := src.(type) {
|
|
case *JoinClause:
|
|
if !forEachSource(src.X, fn) {
|
|
return false
|
|
} else if !forEachSource(src.Y, fn) {
|
|
return false
|
|
}
|
|
case *SelectStatement:
|
|
if !forEachSource(src.Source, fn) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// JoinConstraint represents either an ON or USING join constraint.
|
|
type JoinConstraint interface {
|
|
Node
|
|
joinConstraint()
|
|
}
|
|
|
|
func (*OnConstraint) joinConstraint() {}
|
|
func (*UsingConstraint) joinConstraint() {}
|
|
|
|
// CloneJoinConstraint returns a deep copy cons.
|
|
func CloneJoinConstraint(cons JoinConstraint) JoinConstraint {
|
|
if cons == nil {
|
|
return nil
|
|
}
|
|
|
|
switch cons := cons.(type) {
|
|
case *OnConstraint:
|
|
return cons.Clone()
|
|
case *UsingConstraint:
|
|
return cons.Clone()
|
|
default:
|
|
panic(fmt.Sprintf("invalid join constraint type: %T", cons))
|
|
}
|
|
}
|
|
|
|
type ExplainStatement struct {
|
|
Explain Pos // position of EXPLAIN
|
|
Query Pos // position of QUERY (optional)
|
|
QueryPlan Pos // position of PLAN after QUERY (optional)
|
|
Stmt Statement // target statement
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *ExplainStatement) Clone() *ExplainStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Stmt = CloneStatement(s.Stmt)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *ExplainStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("EXPLAIN")
|
|
if s.QueryPlan.IsValid() {
|
|
buf.WriteString(" QUERY PLAN")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Stmt.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type ShowTablesStatement struct {
|
|
Show Pos // position of SHOW
|
|
Tables Pos // position of TABLES
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *ShowTablesStatement) String() string {
|
|
return "SHOW TABLES"
|
|
}
|
|
|
|
type ShowColumnsStatement struct {
|
|
Show Pos // position of SHOW
|
|
Columns Pos // position of COLUMNS
|
|
From Pos // position of FROM
|
|
TableName *Ident // name of table
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *ShowColumnsStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("SHOW COLUMNS ")
|
|
if s.TableName != nil {
|
|
buf.WriteString(" FROM")
|
|
fmt.Fprintf(&buf, " %s", s.TableName.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type BeginStatement struct {
|
|
Begin Pos // position of BEGIN
|
|
Deferred Pos // position of DEFERRED keyword
|
|
Immediate Pos // position of IMMEDIATE keyword
|
|
Exclusive Pos // position of EXCLUSIVE keyword
|
|
Transaction Pos // position of TRANSACTION keyword (optional)
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *BeginStatement) Clone() *BeginStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *BeginStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("BEGIN")
|
|
if s.Deferred.IsValid() {
|
|
buf.WriteString(" DEFERRED")
|
|
} else if s.Immediate.IsValid() {
|
|
buf.WriteString(" IMMEDIATE")
|
|
} else if s.Exclusive.IsValid() {
|
|
buf.WriteString(" EXCLUSIVE")
|
|
}
|
|
if s.Transaction.IsValid() {
|
|
buf.WriteString(" TRANSACTION")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type CommitStatement struct {
|
|
Commit Pos // position of COMMIT keyword
|
|
End Pos // position of END keyword
|
|
Transaction Pos // position of TRANSACTION keyword (optional)
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *CommitStatement) Clone() *CommitStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *CommitStatement) String() string {
|
|
var buf bytes.Buffer
|
|
if s.End.IsValid() {
|
|
buf.WriteString("END")
|
|
} else {
|
|
buf.WriteString("COMMIT")
|
|
}
|
|
|
|
if s.Transaction.IsValid() {
|
|
buf.WriteString(" TRANSACTION")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type RollbackStatement struct {
|
|
Rollback Pos // position of ROLLBACK keyword
|
|
Transaction Pos // position of TRANSACTION keyword (optional)
|
|
To Pos // position of TO keyword (optional)
|
|
Savepoint Pos // position of SAVEPOINT keyword (optional)
|
|
SavepointName *Ident // name of savepoint
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *RollbackStatement) Clone() *RollbackStatement {
|
|
if s == nil {
|
|
return s
|
|
}
|
|
other := *s
|
|
other.SavepointName = s.SavepointName.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *RollbackStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("ROLLBACK")
|
|
if s.Transaction.IsValid() {
|
|
buf.WriteString(" TRANSACTION")
|
|
}
|
|
|
|
if s.SavepointName != nil {
|
|
buf.WriteString(" TO")
|
|
if s.Savepoint.IsValid() {
|
|
buf.WriteString(" SAVEPOINT")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.SavepointName.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type SavepointStatement struct {
|
|
Savepoint Pos // position of SAVEPOINT keyword
|
|
Name *Ident // name of savepoint
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *SavepointStatement) Clone() *SavepointStatement {
|
|
if s == nil {
|
|
return s
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *SavepointStatement) String() string {
|
|
return fmt.Sprintf("SAVEPOINT %s", s.Name.String())
|
|
}
|
|
|
|
type ReleaseStatement struct {
|
|
Release Pos // position of RELEASE keyword
|
|
Savepoint Pos // position of SAVEPOINT keyword (optional)
|
|
Name *Ident // name of savepoint
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *ReleaseStatement) Clone() *ReleaseStatement {
|
|
if s == nil {
|
|
return s
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *ReleaseStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("RELEASE")
|
|
if s.Savepoint.IsValid() {
|
|
buf.WriteString(" SAVEPOINT")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type CreateTableStatement struct {
|
|
Create Pos // position of CREATE keyword
|
|
Table Pos // position of CREATE keyword
|
|
If Pos // position of IF keyword (optional)
|
|
IfNot Pos // position of NOT keyword (optional)
|
|
IfNotExists Pos // position of EXISTS keyword (optional)
|
|
Name *Ident // table name
|
|
|
|
Lparen Pos // position of left paren of column list
|
|
Columns []*ColumnDefinition // column definitions
|
|
Constraints []Constraint // table constraints
|
|
Rparen Pos // position of right paren of column list
|
|
|
|
As Pos // position of AS keyword (optional)
|
|
Select *SelectStatement // select stmt to build from
|
|
Options []TableOption // table options
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *CreateTableStatement) Clone() *CreateTableStatement {
|
|
if s == nil {
|
|
return s
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
other.Columns = cloneColumnDefinitions(s.Columns)
|
|
other.Constraints = cloneConstraints(s.Constraints)
|
|
other.Select = s.Select.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *CreateTableStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CREATE TABLE")
|
|
if s.IfNotExists.IsValid() {
|
|
buf.WriteString(" IF NOT EXISTS")
|
|
}
|
|
buf.WriteString(" ")
|
|
buf.WriteString(s.Name.String())
|
|
|
|
if s.Select != nil {
|
|
buf.WriteString(" AS ")
|
|
buf.WriteString(s.Select.String())
|
|
} else {
|
|
buf.WriteString(" (")
|
|
for i := range s.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(s.Columns[i].String())
|
|
}
|
|
for i := range s.Constraints {
|
|
buf.WriteString(", ")
|
|
buf.WriteString(s.Constraints[i].String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type ColumnDefinition struct {
|
|
Name *Ident // column name
|
|
Type *Type // data type
|
|
Constraints []Constraint // column constraints
|
|
}
|
|
|
|
// Clone returns a deep copy of d.
|
|
func (d *ColumnDefinition) Clone() *ColumnDefinition {
|
|
if d == nil {
|
|
return d
|
|
}
|
|
other := *d
|
|
other.Name = d.Name.Clone()
|
|
other.Type = d.Type.Clone()
|
|
other.Constraints = cloneConstraints(d.Constraints)
|
|
return &other
|
|
}
|
|
|
|
func cloneColumnDefinitions(a []*ColumnDefinition) []*ColumnDefinition {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*ColumnDefinition, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (c *ColumnDefinition) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
buf.WriteString(c.Type.String())
|
|
for i := range c.Constraints {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(c.Constraints[i].String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type TableOption interface {
|
|
Node
|
|
option()
|
|
}
|
|
|
|
func (*KeyPartitionsOption) option() {}
|
|
func (*ShardWidthOption) option() {}
|
|
|
|
type KeyPartitionsOption struct {
|
|
KeyPartitions Pos // position of KEYPARTITIONS keyword
|
|
Expr Expr // expression
|
|
}
|
|
|
|
func (o *KeyPartitionsOption) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("KEYPARTITIONS (")
|
|
buf.WriteString(o.Expr.String())
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type ShardWidthOption struct {
|
|
ShardWidth Pos // position of SHARDWIDTH keyword
|
|
Expr Expr // expression
|
|
}
|
|
|
|
func (o *ShardWidthOption) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("SHARDWIDTH (")
|
|
buf.WriteString(o.Expr.String())
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type Constraint interface {
|
|
Node
|
|
constraint()
|
|
}
|
|
|
|
func (*PrimaryKeyConstraint) constraint() {}
|
|
func (*NotNullConstraint) constraint() {}
|
|
func (*UniqueConstraint) constraint() {}
|
|
func (*CheckConstraint) constraint() {}
|
|
func (*DefaultConstraint) constraint() {}
|
|
func (*ForeignKeyConstraint) constraint() {}
|
|
func (*MinConstraint) constraint() {}
|
|
func (*MaxConstraint) constraint() {}
|
|
func (*CacheTypeConstraint) constraint() {}
|
|
func (*TimeUnitConstraint) constraint() {}
|
|
func (*TimeQuantumConstraint) constraint() {}
|
|
|
|
// CloneConstraint returns a deep copy cons.
|
|
func CloneConstraint(cons Constraint) Constraint {
|
|
if cons == nil {
|
|
return nil
|
|
}
|
|
|
|
switch cons := cons.(type) {
|
|
case *PrimaryKeyConstraint:
|
|
return cons.Clone()
|
|
case *NotNullConstraint:
|
|
return cons.Clone()
|
|
case *UniqueConstraint:
|
|
return cons.Clone()
|
|
case *CheckConstraint:
|
|
return cons.Clone()
|
|
case *DefaultConstraint:
|
|
return cons.Clone()
|
|
case *ForeignKeyConstraint:
|
|
return cons.Clone()
|
|
default:
|
|
panic(fmt.Sprintf("invalid constraint type: %T", cons))
|
|
}
|
|
}
|
|
|
|
func cloneConstraints(a []Constraint) []Constraint {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]Constraint, len(a))
|
|
for i := range a {
|
|
other[i] = CloneConstraint(a[i])
|
|
}
|
|
return other
|
|
}
|
|
|
|
type PrimaryKeyConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
Primary Pos // position of PRIMARY keyword
|
|
Key Pos // position of KEY keyword
|
|
|
|
Lparen Pos // position of left paren (table only)
|
|
Columns []*Ident // indexed columns (table only)
|
|
Rparen Pos // position of right paren (table only)
|
|
|
|
Autoincrement Pos // position of AUTOINCREMENT keyword (column only)
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *PrimaryKeyConstraint) Clone() *PrimaryKeyConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Columns = cloneIdents(c.Columns)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *PrimaryKeyConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("PRIMARY KEY")
|
|
|
|
if len(c.Columns) > 0 {
|
|
buf.WriteString(" (")
|
|
for i := range c.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(c.Columns[i].String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
if c.Autoincrement.IsValid() {
|
|
buf.WriteString(" AUTOINCREMENT")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type NotNullConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
Not Pos // position of NOT keyword
|
|
Null Pos // position of NULL keyword
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *NotNullConstraint) Clone() *NotNullConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *NotNullConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("NOT NULL")
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type UniqueConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
Unique Pos // position of UNIQUE keyword
|
|
|
|
Lparen Pos // position of left paren (table only)
|
|
Columns []*Ident // indexed columns (table only)
|
|
Rparen Pos // position of right paren (table only)
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *UniqueConstraint) Clone() *UniqueConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Columns = cloneIdents(c.Columns)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *UniqueConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("UNIQUE")
|
|
|
|
if len(c.Columns) > 0 {
|
|
buf.WriteString(" (")
|
|
for i := range c.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(c.Columns[i].String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type MinConstraint struct {
|
|
Min Pos // position of MIN keyword
|
|
Expr Expr // min expression
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *MinConstraint) Clone() *MinConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *MinConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("MIN ")
|
|
buf.WriteString(c.Expr.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type MaxConstraint struct {
|
|
Max Pos // position of MAX keyword
|
|
Expr Expr // check expression
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *MaxConstraint) Clone() *MaxConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *MaxConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("MAX ")
|
|
buf.WriteString(c.Expr.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type CacheTypeConstraint struct {
|
|
CacheType Pos // position of CACHETYPE keyword
|
|
CacheTypeValue string
|
|
Size Pos // position of SIZE keyword
|
|
SizeExpr Expr // check expression
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *CacheTypeConstraint) Clone() *CacheTypeConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.SizeExpr = CloneExpr(c.SizeExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *CacheTypeConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CACHETYPE ")
|
|
buf.WriteString(c.CacheTypeValue)
|
|
if c.Size.IsValid() {
|
|
buf.WriteString(" SIZE ")
|
|
buf.WriteString(c.SizeExpr.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type TimeUnitConstraint struct {
|
|
TimeUnit Pos // position of TIMEUNIT keyword
|
|
Expr Expr // expression
|
|
Epoch Pos // position of TIMEUNIT keyword
|
|
EpochExpr Expr // expression
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *TimeUnitConstraint) Clone() *TimeUnitConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *TimeUnitConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("TIMEUNIT ")
|
|
buf.WriteString(c.Expr.String())
|
|
if c.Epoch.IsValid() {
|
|
buf.WriteString(" EPOCH ")
|
|
buf.WriteString(c.EpochExpr.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type TimeQuantumConstraint struct {
|
|
TimeQuantum Pos // position of TIMEQUANTUM keyword
|
|
Expr Expr // expression
|
|
Ttl Pos
|
|
TtlExpr Expr
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *TimeQuantumConstraint) Clone() *TimeQuantumConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *TimeQuantumConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("TIMEQUANTUM (")
|
|
buf.WriteString(c.Expr.String())
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type CheckConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
Check Pos // position of UNIQUE keyword
|
|
Lparen Pos // position of left paren
|
|
Expr Expr // check expression
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *CheckConstraint) Clone() *CheckConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *CheckConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("CHECK (")
|
|
buf.WriteString(c.Expr.String())
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type DefaultConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
Default Pos // position of DEFAULT keyword
|
|
Lparen Pos // position of left paren
|
|
Expr Expr // default expression
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *DefaultConstraint) Clone() *DefaultConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Expr = CloneExpr(c.Expr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *DefaultConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("DEFAULT ")
|
|
|
|
if c.Lparen.IsValid() {
|
|
buf.WriteString("(")
|
|
buf.WriteString(c.Expr.String())
|
|
buf.WriteString(")")
|
|
} else {
|
|
buf.WriteString(c.Expr.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type ForeignKeyConstraint struct {
|
|
Constraint Pos // position of CONSTRAINT keyword
|
|
Name *Ident // constraint name
|
|
|
|
Foreign Pos // position of FOREIGN keyword (table only)
|
|
ForeignKey Pos // position of KEY keyword after FOREIGN (table only)
|
|
Lparen Pos // position of left paren (table only)
|
|
Columns []*Ident // indexed columns (table only)
|
|
Rparen Pos // position of right paren (table only)
|
|
|
|
References Pos // position of REFERENCES keyword
|
|
ForeignTable *Ident // foreign table name
|
|
ForeignLparen Pos // position of left paren
|
|
ForeignColumns []*Ident // column list
|
|
ForeignRparen Pos // position of right paren
|
|
Args []*ForeignKeyArg // arguments
|
|
Deferrable Pos // position of DEFERRABLE keyword
|
|
Not Pos // position of NOT keyword
|
|
NotDeferrable Pos // position of DEFERRABLE keyword after NOT
|
|
Initially Pos // position of INITIALLY keyword
|
|
InitiallyDeferred Pos // position of DEFERRED keyword after INITIALLY
|
|
InitiallyImmediate Pos // position of IMMEDIATE keyword after INITIALLY
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *ForeignKeyConstraint) Clone() *ForeignKeyConstraint {
|
|
if c == nil {
|
|
return c
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Columns = cloneIdents(c.Columns)
|
|
other.ForeignTable = c.ForeignTable.Clone()
|
|
other.ForeignColumns = cloneIdents(c.ForeignColumns)
|
|
other.Args = cloneForeignKeyArgs(c.Args)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *ForeignKeyConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
if c.Name != nil {
|
|
buf.WriteString("CONSTRAINT ")
|
|
buf.WriteString(c.Name.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
if len(c.Columns) > 0 {
|
|
buf.WriteString("FOREIGN KEY (")
|
|
for i := range c.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(c.Columns[i].String())
|
|
}
|
|
buf.WriteString(") ")
|
|
}
|
|
|
|
buf.WriteString("REFERENCES ")
|
|
buf.WriteString(c.ForeignTable.String())
|
|
if len(c.ForeignColumns) > 0 {
|
|
buf.WriteString(" (")
|
|
for i := range c.ForeignColumns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(c.ForeignColumns[i].String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
for i := range c.Args {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(c.Args[i].String())
|
|
}
|
|
|
|
if c.Deferrable.IsValid() || c.NotDeferrable.IsValid() {
|
|
if c.Deferrable.IsValid() {
|
|
buf.WriteString(" DEFERRABLE")
|
|
} else {
|
|
buf.WriteString(" NOT DEFERRABLE")
|
|
}
|
|
|
|
if c.InitiallyDeferred.IsValid() {
|
|
buf.WriteString(" INITIALLY DEFERRED")
|
|
} else if c.InitiallyImmediate.IsValid() {
|
|
buf.WriteString(" INITIALLY IMMEDIATE")
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type ForeignKeyArg struct {
|
|
On Pos // position of ON keyword
|
|
OnUpdate Pos // position of the UPDATE keyword
|
|
OnDelete Pos // position of the DELETE keyword
|
|
Set Pos // position of the SET keyword
|
|
SetNull Pos // position of the NULL keyword after SET
|
|
SetDefault Pos // position of the DEFAULT keyword after SET
|
|
Cascade Pos // position of the CASCADE keyword
|
|
Restrict Pos // position of the RESTRICT keyword
|
|
No Pos // position of the NO keyword
|
|
NoAction Pos // position of the ACTION keyword after NO
|
|
}
|
|
|
|
// Clone returns a deep copy of arg.
|
|
func (arg *ForeignKeyArg) Clone() *ForeignKeyArg {
|
|
if arg == nil {
|
|
return nil
|
|
}
|
|
other := *arg
|
|
return &other
|
|
}
|
|
|
|
func cloneForeignKeyArgs(a []*ForeignKeyArg) []*ForeignKeyArg {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*ForeignKeyArg, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the argument.
|
|
func (c *ForeignKeyArg) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("ON")
|
|
if c.OnUpdate.IsValid() {
|
|
buf.WriteString(" UPDATE")
|
|
} else {
|
|
buf.WriteString(" DELETE")
|
|
}
|
|
|
|
if c.SetNull.IsValid() {
|
|
buf.WriteString(" SET NULL")
|
|
} else if c.SetDefault.IsValid() {
|
|
buf.WriteString(" SET DEFAULT")
|
|
} else if c.Cascade.IsValid() {
|
|
buf.WriteString(" CASCADE")
|
|
} else if c.Restrict.IsValid() {
|
|
buf.WriteString(" RESTRICT")
|
|
} else if c.NoAction.IsValid() {
|
|
buf.WriteString(" NO ACTION")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type AnalyzeStatement struct {
|
|
Analyze Pos // position of ANALYZE keyword
|
|
Name *Ident // table name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *AnalyzeStatement) Clone() *AnalyzeStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *AnalyzeStatement) String() string {
|
|
return fmt.Sprintf("ANALYZE %s", s.Name.String())
|
|
}
|
|
|
|
type AlterTableStatement struct {
|
|
Alter Pos // position of ALTER keyword
|
|
Table Pos // position of TABLE keyword
|
|
Name *Ident // table name
|
|
|
|
Rename Pos // position of RENAME keyword
|
|
//RenameTo Pos // position of TO keyword after RENAME
|
|
//NewName *Ident // new table name
|
|
|
|
RenameColumn Pos // position of COLUMN keyword after RENAME
|
|
OldColumnName *Ident // old column name
|
|
To Pos // position of TO keyword
|
|
NewColumnName *Ident // new column name
|
|
|
|
Add Pos // position of ADD keyword
|
|
AddColumn Pos // position of COLUMN keyword after ADD
|
|
ColumnDef *ColumnDefinition // new column definition
|
|
|
|
Drop Pos // position of ADD keyword
|
|
DropColumn Pos // position of COLUMN keyword after ADD
|
|
DropColumnName *Ident // drop column name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *AlterTableStatement) Clone() *AlterTableStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = other.Name.Clone()
|
|
//other.NewName = s.NewName.Clone()
|
|
other.OldColumnName = s.OldColumnName.Clone()
|
|
other.NewColumnName = s.NewColumnName.Clone()
|
|
other.ColumnDef = s.ColumnDef.Clone()
|
|
other.DropColumnName = s.DropColumnName.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *AlterTableStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("ALTER TABLE ")
|
|
buf.WriteString(s.Name.String())
|
|
|
|
if s.OldColumnName != nil {
|
|
buf.WriteString(" RENAME COLUMN ")
|
|
buf.WriteString(s.OldColumnName.String())
|
|
buf.WriteString(" TO ")
|
|
buf.WriteString(s.NewColumnName.String())
|
|
} else if s.DropColumnName != nil {
|
|
buf.WriteString(" DROP COLUMN ")
|
|
buf.WriteString(s.DropColumnName.String())
|
|
} else if s.ColumnDef != nil {
|
|
buf.WriteString(" ADD COLUMN ")
|
|
if s.AddColumn.IsValid() {
|
|
buf.WriteString(" COLUMN ")
|
|
}
|
|
buf.WriteString(s.ColumnDef.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type Ident struct {
|
|
NamePos Pos // identifier position
|
|
Name string // identifier name
|
|
Quoted bool // true if double quoted
|
|
}
|
|
|
|
func (expr *Ident) IsLiteral() bool { return false }
|
|
|
|
func (expr *Ident) DataType() ExprDataType {
|
|
return NewDataTypeVoid()
|
|
}
|
|
|
|
func (expr *Ident) Pos() Pos {
|
|
return expr.NamePos
|
|
}
|
|
|
|
// Clone returns a deep copy of i.
|
|
func (i *Ident) Clone() *Ident {
|
|
if i == nil {
|
|
return nil
|
|
}
|
|
other := *i
|
|
return &other
|
|
}
|
|
|
|
func cloneIdents(a []*Ident) []*Ident {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*Ident, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (i *Ident) String() string {
|
|
return `"` + strings.Replace(i.Name, `"`, `""`, -1) + `"`
|
|
}
|
|
|
|
// IdentName returns the name of ident. Returns a blank string if ident is nil.
|
|
func IdentName(ident *Ident) string {
|
|
if ident == nil {
|
|
return ""
|
|
}
|
|
return ident.Name
|
|
}
|
|
|
|
type Type struct {
|
|
Name *Ident // type name
|
|
Lparen Pos // position of left paren (optional)
|
|
Precision *IntegerLit // precision (optional)
|
|
Scale *IntegerLit // scale (optional)
|
|
Rparen Pos // position of right paren (optional)
|
|
}
|
|
|
|
// Clone returns a deep copy of t.
|
|
func (t *Type) Clone() *Type {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
other := *t
|
|
other.Name = t.Name.Clone()
|
|
other.Precision = t.Precision.Clone()
|
|
other.Scale = t.Scale.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the type.
|
|
func (t *Type) String() string {
|
|
if t.Precision != nil && t.Scale != nil {
|
|
return fmt.Sprintf("%s(%s,%s)", t.Name.Name, t.Precision.String(), t.Scale.String())
|
|
} else if t.Precision != nil {
|
|
return fmt.Sprintf("%s(%s)", t.Name.Name, t.Precision.String())
|
|
}
|
|
return t.Name.Name
|
|
}
|
|
|
|
type StringLit struct {
|
|
ValuePos Pos // literal position
|
|
Value string // literal value (without quotes)
|
|
}
|
|
|
|
func (expr *StringLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *StringLit) DataType() ExprDataType {
|
|
return NewDataTypeString()
|
|
}
|
|
|
|
func (expr *StringLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
func (expr *StringLit) ConvertToTimestamp() *DateLit {
|
|
//try to coerce to a date
|
|
if tm, err := time.ParseInLocation(time.RFC3339Nano, expr.Value, time.UTC); err == nil {
|
|
return &DateLit{ValuePos: expr.ValuePos, Value: tm}
|
|
} else if tm, err := time.ParseInLocation(time.RFC3339, expr.Value, time.UTC); err == nil {
|
|
return &DateLit{ValuePos: expr.ValuePos, Value: tm}
|
|
} else if tm, err := time.ParseInLocation("2006-01-02", expr.Value, time.UTC); err == nil {
|
|
return &DateLit{ValuePos: expr.ValuePos, Value: tm}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *StringLit) Clone() *StringLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *StringLit) String() string {
|
|
return `'` + strings.Replace(lit.Value, `'`, `''`, -1) + `'`
|
|
}
|
|
|
|
type IntegerLit struct {
|
|
ValuePos Pos // literal position
|
|
Value string // literal value
|
|
}
|
|
|
|
func (expr *IntegerLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *IntegerLit) DataType() ExprDataType {
|
|
return NewDataTypeInt()
|
|
}
|
|
|
|
func (expr *IntegerLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *IntegerLit) Clone() *IntegerLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *IntegerLit) String() string {
|
|
return lit.Value
|
|
}
|
|
|
|
type FloatLit struct {
|
|
ValuePos Pos // literal position
|
|
Value string // literal value
|
|
}
|
|
|
|
func (expr *FloatLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *FloatLit) DataType() ExprDataType {
|
|
//how many decimal places do we have on the right of the point?
|
|
scale := NumDecimalPlaces(expr.Value)
|
|
return NewDataTypeDecimal(int64(scale))
|
|
}
|
|
|
|
func (expr *FloatLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *FloatLit) Clone() *FloatLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *FloatLit) String() string {
|
|
return lit.Value
|
|
}
|
|
|
|
type NullLit struct {
|
|
ValuePos Pos
|
|
}
|
|
|
|
func (expr *NullLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *NullLit) DataType() ExprDataType {
|
|
return NewDataTypeVoid()
|
|
}
|
|
|
|
func (expr *NullLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *NullLit) Clone() *NullLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *NullLit) String() string {
|
|
return "NULL"
|
|
}
|
|
|
|
type BoolLit struct {
|
|
ValuePos Pos // literal position
|
|
Value bool // literal value
|
|
}
|
|
|
|
func (expr *BoolLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *BoolLit) DataType() ExprDataType {
|
|
return NewDataTypeBool()
|
|
}
|
|
|
|
func (expr *BoolLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *BoolLit) Clone() *BoolLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *BoolLit) String() string {
|
|
if lit.Value {
|
|
return "TRUE"
|
|
}
|
|
return "FALSE"
|
|
}
|
|
|
|
type DateLit struct {
|
|
ValuePos Pos // literal position
|
|
Value time.Time // literal value
|
|
}
|
|
|
|
func (expr *DateLit) IsLiteral() bool { return true }
|
|
|
|
func (expr *DateLit) DataType() ExprDataType {
|
|
return NewDataTypeTimestamp()
|
|
}
|
|
|
|
func (expr *DateLit) Pos() Pos {
|
|
return expr.ValuePos
|
|
}
|
|
|
|
// Clone returns a deep copy of lit.
|
|
func (lit *DateLit) Clone() *DateLit {
|
|
if lit == nil {
|
|
return nil
|
|
}
|
|
other := *lit
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (lit *DateLit) String() string {
|
|
return lit.Value.Format(time.RFC3339)
|
|
}
|
|
|
|
type UnaryExpr struct {
|
|
OpPos Pos // operation position
|
|
Op Token // operation
|
|
X Expr // target expression
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *UnaryExpr) IsLiteral() bool {
|
|
return expr.X.IsLiteral()
|
|
}
|
|
|
|
func (expr *UnaryExpr) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *UnaryExpr) Pos() Pos {
|
|
return expr.OpPos
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *UnaryExpr) Clone() *UnaryExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.X = CloneExpr(expr.X)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *UnaryExpr) String() string {
|
|
switch expr.Op {
|
|
case PLUS:
|
|
return "+" + expr.X.String()
|
|
case MINUS:
|
|
return "-" + expr.X.String()
|
|
case BITNOT:
|
|
return "!" + expr.X.String()
|
|
default:
|
|
panic(fmt.Sprintf("sql.UnaryExpr.String(): invalid op %s", expr.Op))
|
|
}
|
|
}
|
|
|
|
type BinaryExpr struct {
|
|
X Expr // lhs
|
|
OpPos Pos // position of Op
|
|
Op Token // operator
|
|
Y Expr // rhs
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *BinaryExpr) IsLiteral() bool {
|
|
return expr.X.IsLiteral() && expr.Y.IsLiteral()
|
|
}
|
|
|
|
func (expr *BinaryExpr) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *BinaryExpr) Pos() Pos {
|
|
return expr.X.Pos()
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *BinaryExpr) Clone() *BinaryExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.X = CloneExpr(expr.X)
|
|
other.Y = CloneExpr(expr.Y)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *BinaryExpr) String() string {
|
|
switch expr.Op {
|
|
case PLUS:
|
|
return expr.X.String() + " + " + expr.Y.String()
|
|
case MINUS:
|
|
return expr.X.String() + " - " + expr.Y.String()
|
|
case STAR:
|
|
return expr.X.String() + " * " + expr.Y.String()
|
|
case SLASH:
|
|
return expr.X.String() + " / " + expr.Y.String()
|
|
case REM:
|
|
return expr.X.String() + " % " + expr.Y.String()
|
|
case CONCAT:
|
|
return expr.X.String() + " || " + expr.Y.String()
|
|
case BETWEEN:
|
|
return expr.X.String() + " BETWEEN " + expr.Y.String()
|
|
case NOTBETWEEN:
|
|
return expr.X.String() + " NOT BETWEEN " + expr.Y.String()
|
|
case LSHIFT:
|
|
return expr.X.String() + " << " + expr.Y.String()
|
|
case RSHIFT:
|
|
return expr.X.String() + " >> " + expr.Y.String()
|
|
case BITAND:
|
|
return expr.X.String() + " & " + expr.Y.String()
|
|
case BITOR:
|
|
return expr.X.String() + " | " + expr.Y.String()
|
|
case LT:
|
|
return expr.X.String() + " < " + expr.Y.String()
|
|
case LE:
|
|
return expr.X.String() + " <= " + expr.Y.String()
|
|
case GT:
|
|
return expr.X.String() + " > " + expr.Y.String()
|
|
case GE:
|
|
return expr.X.String() + " >= " + expr.Y.String()
|
|
case EQ:
|
|
return expr.X.String() + " = " + expr.Y.String()
|
|
case NE:
|
|
return expr.X.String() + " != " + expr.Y.String()
|
|
case IS:
|
|
return expr.X.String() + " IS " + expr.Y.String()
|
|
case ISNOT:
|
|
return expr.X.String() + " IS NOT " + expr.Y.String()
|
|
case IN:
|
|
return expr.X.String() + " IN " + expr.Y.String()
|
|
case NOTIN:
|
|
return expr.X.String() + " NOT IN " + expr.Y.String()
|
|
case LIKE:
|
|
return expr.X.String() + " LIKE " + expr.Y.String()
|
|
case NOTLIKE:
|
|
return expr.X.String() + " NOT LIKE " + expr.Y.String()
|
|
case GLOB:
|
|
return expr.X.String() + " GLOB " + expr.Y.String()
|
|
case NOTGLOB:
|
|
return expr.X.String() + " NOT GLOB " + expr.Y.String()
|
|
case MATCH:
|
|
return expr.X.String() + " MATCH " + expr.Y.String()
|
|
case NOTMATCH:
|
|
return expr.X.String() + " NOT MATCH " + expr.Y.String()
|
|
case REGEXP:
|
|
return expr.X.String() + " REGEXP " + expr.Y.String()
|
|
case NOTREGEXP:
|
|
return expr.X.String() + " NOT REGEXP " + expr.Y.String()
|
|
case AND:
|
|
return expr.X.String() + " AND " + expr.Y.String()
|
|
case OR:
|
|
return expr.X.String() + " OR " + expr.Y.String()
|
|
default:
|
|
panic(fmt.Sprintf("sql.BinaryExpr.String(): invalid op %s", expr.Op))
|
|
}
|
|
}
|
|
|
|
type CastExpr struct {
|
|
Cast Pos // position of CAST keyword
|
|
Lparen Pos // position of left paren
|
|
X Expr // target expression
|
|
As Pos // position of AS keyword
|
|
Type *Type // cast type
|
|
Rparen Pos // position of right paren
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *CastExpr) IsLiteral() bool {
|
|
return expr.X.IsLiteral()
|
|
}
|
|
|
|
func (expr *CastExpr) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *CastExpr) Pos() Pos {
|
|
return expr.Cast
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *CastExpr) Clone() *CastExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.X = CloneExpr(expr.X)
|
|
other.Type = expr.Type.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *CastExpr) String() string {
|
|
return fmt.Sprintf("CAST(%s AS %s)", expr.X.String(), expr.Type.String())
|
|
}
|
|
|
|
type CaseExpr struct {
|
|
Case Pos // position of CASE keyword
|
|
Operand Expr // optional condition after the CASE keyword
|
|
Blocks []*CaseBlock // list of WHEN/THEN pairs
|
|
Else Pos // position of ELSE keyword
|
|
ElseExpr Expr // expression used by default case
|
|
End Pos // position of END keyword
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *CaseExpr) IsLiteral() bool { return false }
|
|
|
|
func (expr *CaseExpr) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *CaseExpr) Pos() Pos {
|
|
return expr.Case
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *CaseExpr) Clone() *CaseExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.Operand = CloneExpr(expr.Operand)
|
|
other.Blocks = cloneCaseBlocks(expr.Blocks)
|
|
other.ElseExpr = CloneExpr(expr.ElseExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *CaseExpr) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CASE")
|
|
if expr.Operand != nil {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(expr.Operand.String())
|
|
}
|
|
for _, blk := range expr.Blocks {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(blk.String())
|
|
}
|
|
if expr.ElseExpr != nil {
|
|
buf.WriteString(" ELSE ")
|
|
buf.WriteString(expr.ElseExpr.String())
|
|
}
|
|
buf.WriteString(" END")
|
|
return buf.String()
|
|
}
|
|
|
|
type CaseBlock struct {
|
|
When Pos // position of WHEN keyword
|
|
Condition Expr // block condition
|
|
Then Pos // position of THEN keyword
|
|
Body Expr // result expression
|
|
}
|
|
|
|
func (expr *CaseBlock) IsLiteral() bool { return false }
|
|
|
|
func (expr *CaseBlock) DataType() ExprDataType {
|
|
return expr.Body.DataType()
|
|
}
|
|
|
|
func (expr *CaseBlock) Pos() Pos {
|
|
return expr.When
|
|
}
|
|
|
|
// Clone returns a deep copy of blk.
|
|
func (blk *CaseBlock) Clone() *CaseBlock {
|
|
if blk == nil {
|
|
return nil
|
|
}
|
|
other := *blk
|
|
other.Condition = CloneExpr(blk.Condition)
|
|
other.Body = CloneExpr(blk.Body)
|
|
return &other
|
|
}
|
|
|
|
func cloneCaseBlocks(a []*CaseBlock) []*CaseBlock {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*CaseBlock, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the block.
|
|
func (b *CaseBlock) String() string {
|
|
return fmt.Sprintf("WHEN %s THEN %s", b.Condition.String(), b.Body.String())
|
|
}
|
|
|
|
type Exists struct {
|
|
Not Pos // position of optional NOT keyword
|
|
Exists Pos // position of EXISTS keyword
|
|
Lparen Pos // position of left paren
|
|
Select *SelectStatement // select statement
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
func (expr *Exists) IsLiteral() bool { return false }
|
|
|
|
func (expr *Exists) DataType() ExprDataType {
|
|
return NewDataTypeBool()
|
|
}
|
|
|
|
func (expr *Exists) Pos() Pos {
|
|
if expr.Not.IsValid() {
|
|
return expr.Not
|
|
}
|
|
return expr.Exists
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *Exists) Clone() *Exists {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.Select = expr.Select.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *Exists) String() string {
|
|
if expr.Not.IsValid() {
|
|
return fmt.Sprintf("NOT EXISTS (%s)", expr.Select.String())
|
|
}
|
|
return fmt.Sprintf("EXISTS (%s)", expr.Select.String())
|
|
}
|
|
|
|
type ExprList struct {
|
|
Lparen Pos // position of left paren
|
|
Exprs []Expr // list of expressions
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
func (expr *ExprList) IsLiteral() bool {
|
|
for _, e := range expr.Exprs {
|
|
if !e.IsLiteral() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (expr *ExprList) DataType() ExprDataType {
|
|
return NewDataTypeVoid()
|
|
}
|
|
|
|
func (expr *ExprList) Pos() Pos {
|
|
return expr.Lparen
|
|
}
|
|
|
|
// Clone returns a deep copy of l.
|
|
func (l *ExprList) Clone() *ExprList {
|
|
if l == nil {
|
|
return nil
|
|
}
|
|
other := *l
|
|
other.Exprs = cloneExprs(l.Exprs)
|
|
return &other
|
|
}
|
|
|
|
/*func cloneExprLists(a []*ExprList) []*ExprList {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*ExprList, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}*/
|
|
|
|
// String returns the string representation of the expression.
|
|
func (l *ExprList) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("(")
|
|
for i, expr := range l.Exprs {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(expr.String())
|
|
}
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type Range struct {
|
|
X Expr // lhs expression
|
|
And Pos // position of AND keyword
|
|
Y Expr // rhs expression
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *Range) IsLiteral() bool { return false }
|
|
|
|
func (expr *Range) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *Range) Pos() Pos {
|
|
return expr.X.Pos()
|
|
}
|
|
|
|
// Clone returns a deep copy of r.
|
|
func (r *Range) Clone() *Range {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
other := *r
|
|
other.X = CloneExpr(r.X)
|
|
other.Y = CloneExpr(r.Y)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (r *Range) String() string {
|
|
return fmt.Sprintf("%s AND %s", r.X.String(), r.Y.String())
|
|
}
|
|
|
|
type QualifiedRef struct {
|
|
Table *Ident // table name
|
|
Dot Pos // position of dot
|
|
Star Pos // position of * (result column only)
|
|
Column *Ident // column name
|
|
ColumnIndex int
|
|
|
|
// Set by the planner; not at parse-time
|
|
RefDataType ExprDataType
|
|
}
|
|
|
|
func (expr *QualifiedRef) IsLiteral() bool { return false }
|
|
|
|
func (expr *QualifiedRef) DataType() ExprDataType {
|
|
return expr.RefDataType
|
|
}
|
|
|
|
func (expr *QualifiedRef) Pos() Pos {
|
|
return expr.Table.Pos()
|
|
}
|
|
|
|
// Clone returns a deep copy of r.
|
|
func (r *QualifiedRef) Clone() *QualifiedRef {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
other := *r
|
|
other.Table = r.Table.Clone()
|
|
other.Column = r.Column.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (r *QualifiedRef) String() string {
|
|
if r.Star.IsValid() {
|
|
return fmt.Sprintf("%s.*", r.Table.String())
|
|
}
|
|
return fmt.Sprintf("%s.%s", r.Table.String(), r.Column.String())
|
|
}
|
|
|
|
type Call struct {
|
|
Name *Ident // function name
|
|
Lparen Pos // position of left paren
|
|
Star Pos // position of *
|
|
Distinct Pos // position of DISTINCT keyword
|
|
Args []Expr // argument list
|
|
Rparen Pos // position of right paren
|
|
Filter *FilterClause // filter clause
|
|
Over *OverClause // over clause
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *Call) IsLiteral() bool { return false }
|
|
|
|
func (expr *Call) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *Call) Pos() Pos {
|
|
return expr.Name.Pos()
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *Call) Clone() *Call {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Args = cloneExprs(c.Args)
|
|
other.Filter = c.Filter.Clone()
|
|
other.Over = c.Over.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (c *Call) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(c.Name.Name)
|
|
buf.WriteString("(")
|
|
if c.Star.IsValid() {
|
|
buf.WriteString("*")
|
|
} else {
|
|
if c.Distinct.IsValid() {
|
|
buf.WriteString("DISTINCT")
|
|
if len(c.Args) != 0 {
|
|
buf.WriteString(" ")
|
|
}
|
|
}
|
|
for i, arg := range c.Args {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(arg.String())
|
|
}
|
|
}
|
|
buf.WriteString(")")
|
|
|
|
if c.Filter != nil {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(c.Filter.String())
|
|
}
|
|
|
|
if c.Over != nil {
|
|
buf.WriteString(" ")
|
|
buf.WriteString(c.Over.String())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type FilterClause struct {
|
|
Filter Pos // position of FILTER keyword
|
|
Lparen Pos // position of left paren
|
|
Where Pos // position of WHERE keyword
|
|
X Expr // filter expression
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *FilterClause) Clone() *FilterClause {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.X = CloneExpr(c.X)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (c *FilterClause) String() string {
|
|
return fmt.Sprintf("FILTER (WHERE %s)", c.X.String())
|
|
}
|
|
|
|
type OverClause struct {
|
|
Over Pos // position of OVER keyword
|
|
Name *Ident // window name
|
|
Definition *WindowDefinition // window definition
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *OverClause) Clone() *OverClause {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.Name = c.Name.Clone()
|
|
other.Definition = c.Definition.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (c *OverClause) String() string {
|
|
if c.Name != nil {
|
|
return fmt.Sprintf("OVER %s", c.Name.String())
|
|
}
|
|
return fmt.Sprintf("OVER %s", c.Definition.String())
|
|
}
|
|
|
|
type OrderingTerm struct {
|
|
X Expr // ordering expression
|
|
|
|
Asc Pos // position of ASC keyword
|
|
Desc Pos // position of DESC keyword
|
|
|
|
Nulls Pos // position of NULLS keyword
|
|
NullsFirst Pos // position of FIRST keyword
|
|
NullsLast Pos // position of LAST keyword
|
|
}
|
|
|
|
// Clone returns a deep copy of t.
|
|
func (t *OrderingTerm) Clone() *OrderingTerm {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
other := *t
|
|
other.X = CloneExpr(t.X)
|
|
return &other
|
|
}
|
|
|
|
func cloneOrderingTerms(a []*OrderingTerm) []*OrderingTerm {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*OrderingTerm, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the term.
|
|
func (t *OrderingTerm) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(t.X.String())
|
|
|
|
if t.Asc.IsValid() {
|
|
buf.WriteString(" ASC")
|
|
} else if t.Desc.IsValid() {
|
|
buf.WriteString(" DESC")
|
|
}
|
|
|
|
if t.NullsFirst.IsValid() {
|
|
buf.WriteString(" NULLS FIRST")
|
|
} else if t.NullsLast.IsValid() {
|
|
buf.WriteString(" NULLS LAST")
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type FrameSpec struct {
|
|
Range Pos // position of RANGE keyword
|
|
Rows Pos // position of ROWS keyword
|
|
Groups Pos // position of GROUPS keyword
|
|
|
|
Between Pos // position of BETWEEN keyword
|
|
|
|
X Expr // lhs expression
|
|
UnboundedX Pos // position of lhs UNBOUNDED keyword
|
|
PrecedingX Pos // position of lhs PRECEDING keyword
|
|
CurrentX Pos // position of lhs CURRENT keyword
|
|
CurrentRowX Pos // position of lhs ROW keyword
|
|
FollowingX Pos // position of lhs FOLLOWING keyword
|
|
|
|
And Pos // position of AND keyword
|
|
|
|
Y Expr // lhs expression
|
|
UnboundedY Pos // position of rhs UNBOUNDED keyword
|
|
FollowingY Pos // position of rhs FOLLOWING keyword
|
|
CurrentY Pos // position of rhs CURRENT keyword
|
|
CurrentRowY Pos // position of rhs ROW keyword
|
|
PrecedingY Pos // position of rhs PRECEDING keyword
|
|
|
|
Exclude Pos // position of EXCLUDE keyword
|
|
ExcludeNo Pos // position of NO keyword after EXCLUDE
|
|
ExcludeNoOthers Pos // position of OTHERS keyword after EXCLUDE NO
|
|
ExcludeCurrent Pos // position of CURRENT keyword after EXCLUDE
|
|
ExcludeCurrentRow Pos // position of ROW keyword after EXCLUDE CURRENT
|
|
ExcludeGroup Pos // position of GROUP keyword after EXCLUDE
|
|
ExcludeTies Pos // position of TIES keyword after EXCLUDE
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *FrameSpec) Clone() *FrameSpec {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.X = CloneExpr(s.X)
|
|
other.X = CloneExpr(s.Y)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the frame spec.
|
|
func (s *FrameSpec) String() string {
|
|
var buf bytes.Buffer
|
|
if s.Range.IsValid() {
|
|
buf.WriteString("RANGE")
|
|
} else if s.Rows.IsValid() {
|
|
buf.WriteString("ROWS")
|
|
} else if s.Groups.IsValid() {
|
|
buf.WriteString("GROUPS")
|
|
}
|
|
|
|
if s.Between.IsValid() {
|
|
buf.WriteString(" BETWEEN")
|
|
if s.UnboundedX.IsValid() && s.PrecedingX.IsValid() {
|
|
buf.WriteString(" UNBOUNDED PRECEDING")
|
|
} else if s.X != nil && s.PrecedingX.IsValid() {
|
|
fmt.Fprintf(&buf, " %s PRECEDING", s.X.String())
|
|
} else if s.CurrentRowX.IsValid() {
|
|
buf.WriteString(" CURRENT ROW")
|
|
} else if s.X != nil && s.FollowingX.IsValid() {
|
|
fmt.Fprintf(&buf, " %s FOLLOWING", s.X.String())
|
|
}
|
|
|
|
buf.WriteString(" AND")
|
|
|
|
if s.Y != nil && s.PrecedingY.IsValid() {
|
|
fmt.Fprintf(&buf, " %s PRECEDING", s.Y.String())
|
|
} else if s.CurrentRowY.IsValid() {
|
|
buf.WriteString(" CURRENT ROW")
|
|
} else if s.Y != nil && s.FollowingY.IsValid() {
|
|
fmt.Fprintf(&buf, " %s FOLLOWING", s.Y.String())
|
|
} else if s.UnboundedY.IsValid() && s.FollowingY.IsValid() {
|
|
buf.WriteString(" UNBOUNDED FOLLOWING")
|
|
}
|
|
} else {
|
|
if s.UnboundedX.IsValid() && s.PrecedingX.IsValid() {
|
|
buf.WriteString(" UNBOUNDED PRECEDING")
|
|
} else if s.X != nil && s.PrecedingX.IsValid() {
|
|
fmt.Fprintf(&buf, " %s PRECEDING", s.X.String())
|
|
} else if s.CurrentRowX.IsValid() {
|
|
buf.WriteString(" CURRENT ROW")
|
|
}
|
|
}
|
|
|
|
if s.ExcludeNoOthers.IsValid() {
|
|
buf.WriteString(" EXCLUDE NO OTHERS")
|
|
} else if s.ExcludeCurrentRow.IsValid() {
|
|
buf.WriteString(" EXCLUDE CURRENT ROW")
|
|
} else if s.ExcludeGroup.IsValid() {
|
|
buf.WriteString(" EXCLUDE GROUP")
|
|
} else if s.ExcludeTies.IsValid() {
|
|
buf.WriteString(" EXCLUDE TIES")
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type ColumnArg interface {
|
|
Node
|
|
columnArg()
|
|
}
|
|
|
|
type DropTableStatement struct {
|
|
Drop Pos // position of DROP keyword
|
|
Table Pos // position of TABLE keyword
|
|
If Pos // position of IF keyword
|
|
IfExists Pos // position of EXISTS keyword after IF
|
|
Name *Ident // view name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *DropTableStatement) Clone() *DropTableStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *DropTableStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("DROP TABLE")
|
|
if s.IfExists.IsValid() {
|
|
buf.WriteString(" IF EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type CreateViewStatement struct {
|
|
Create Pos // position of CREATE keyword
|
|
View Pos // position of VIEW keyword
|
|
If Pos // position of IF keyword
|
|
IfNot Pos // position of NOT keyword after IF
|
|
IfNotExists Pos // position of EXISTS keyword after IF NOT
|
|
Name *Ident // view name
|
|
Lparen Pos // position of column list left paren
|
|
Columns []*Ident // column list
|
|
Rparen Pos // position of column list right paren
|
|
As Pos // position of AS keyword
|
|
Select *SelectStatement // source statement
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *CreateViewStatement) Clone() *CreateViewStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
other.Columns = cloneIdents(s.Columns)
|
|
other.Select = s.Select.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *CreateViewStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CREATE VIEW")
|
|
if s.IfNotExists.IsValid() {
|
|
buf.WriteString(" IF NOT EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
|
|
if len(s.Columns) > 0 {
|
|
buf.WriteString(" (")
|
|
for i, col := range s.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " AS %s", s.Select.String())
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type DropViewStatement struct {
|
|
Drop Pos // position of DROP keyword
|
|
View Pos // position of VIEW keyword
|
|
If Pos // position of IF keyword
|
|
IfExists Pos // position of EXISTS keyword after IF
|
|
Name *Ident // view name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *DropViewStatement) Clone() *DropViewStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *DropViewStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("DROP VIEW")
|
|
if s.IfExists.IsValid() {
|
|
buf.WriteString(" IF EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type CreateIndexStatement struct {
|
|
Create Pos // position of CREATE keyword
|
|
Unique Pos // position of optional UNIQUE keyword
|
|
Index Pos // position of INDEX keyword
|
|
If Pos // position of IF keyword
|
|
IfNot Pos // position of NOT keyword after IF
|
|
IfNotExists Pos // position of EXISTS keyword after IF NOT
|
|
Name *Ident // index name
|
|
On Pos // position of ON keyword
|
|
Table *Ident // index name
|
|
Lparen Pos // position of column list left paren
|
|
Columns []*IndexedColumn // column list
|
|
Rparen Pos // position of column list right paren
|
|
Where Pos // position of WHERE keyword
|
|
WhereExpr Expr // conditional expression
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *CreateIndexStatement) Clone() *CreateIndexStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
other.Table = s.Table.Clone()
|
|
other.Columns = cloneIndexedColumns(s.Columns)
|
|
other.WhereExpr = CloneExpr(s.WhereExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *CreateIndexStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CREATE")
|
|
if s.Unique.IsValid() {
|
|
buf.WriteString(" UNIQUE")
|
|
}
|
|
buf.WriteString(" INDEX")
|
|
if s.IfNotExists.IsValid() {
|
|
buf.WriteString(" IF NOT EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s ON %s ", s.Name.String(), s.Table.String())
|
|
|
|
buf.WriteString("(")
|
|
for i, col := range s.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
|
|
if s.WhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", s.WhereExpr.String())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type DropIndexStatement struct {
|
|
Drop Pos // position of DROP keyword
|
|
Index Pos // position of INDEX keyword
|
|
If Pos // position of IF keyword
|
|
IfExists Pos // position of EXISTS keyword after IF
|
|
Name *Ident // index name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *DropIndexStatement) Clone() *DropIndexStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *DropIndexStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("DROP INDEX")
|
|
if s.IfExists.IsValid() {
|
|
buf.WriteString(" IF EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type CreateTriggerStatement struct {
|
|
Create Pos // position of CREATE keyword
|
|
Trigger Pos // position of TRIGGER keyword
|
|
If Pos // position of IF keyword
|
|
IfNot Pos // position of NOT keyword after IF
|
|
IfNotExists Pos // position of EXISTS keyword after IF NOT
|
|
Name *Ident // index name
|
|
|
|
Before Pos // position of BEFORE keyword
|
|
After Pos // position of AFTER keyword
|
|
Instead Pos // position of INSTEAD keyword
|
|
InsteadOf Pos // position of OF keyword after INSTEAD
|
|
|
|
Delete Pos // position of DELETE keyword
|
|
Insert Pos // position of INSERT keyword
|
|
Update Pos // position of UPDATE keyword
|
|
UpdateOf Pos // position of OF keyword after UPDATE
|
|
UpdateOfColumns []*Ident // columns list for UPDATE OF
|
|
On Pos // position of ON keyword
|
|
Table *Ident // table name
|
|
|
|
For Pos // position of FOR keyword
|
|
ForEach Pos // position of EACH keyword after FOR
|
|
ForEachRow Pos // position of ROW keyword after FOR EACH
|
|
|
|
When Pos // position of WHEN keyword
|
|
WhenExpr Expr // conditional expression
|
|
|
|
Begin Pos // position of BEGIN keyword
|
|
Body []Statement // trigger body
|
|
End Pos // position of END keyword
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *CreateTriggerStatement) Clone() *CreateTriggerStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
other.UpdateOfColumns = cloneIdents(s.UpdateOfColumns)
|
|
other.Table = s.Table.Clone()
|
|
other.WhenExpr = CloneExpr(s.WhenExpr)
|
|
other.Body = cloneStatements(s.Body)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *CreateTriggerStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("CREATE TRIGGER")
|
|
if s.IfNotExists.IsValid() {
|
|
buf.WriteString(" IF NOT EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
|
|
if s.Before.IsValid() {
|
|
buf.WriteString(" BEFORE")
|
|
} else if s.After.IsValid() {
|
|
buf.WriteString(" AFTER")
|
|
} else if s.InsteadOf.IsValid() {
|
|
buf.WriteString(" INSTEAD OF")
|
|
}
|
|
|
|
if s.Delete.IsValid() {
|
|
buf.WriteString(" DELETE")
|
|
} else if s.Insert.IsValid() {
|
|
buf.WriteString(" INSERT")
|
|
} else if s.Update.IsValid() {
|
|
buf.WriteString(" UPDATE")
|
|
if s.UpdateOf.IsValid() {
|
|
buf.WriteString(" OF ")
|
|
for i, col := range s.UpdateOfColumns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " ON %s", s.Table.String())
|
|
|
|
if s.ForEachRow.IsValid() {
|
|
buf.WriteString(" FOR EACH ROW")
|
|
}
|
|
|
|
if s.WhenExpr != nil {
|
|
fmt.Fprintf(&buf, " WHEN %s", s.WhenExpr.String())
|
|
}
|
|
|
|
buf.WriteString(" BEGIN")
|
|
for i := range s.Body {
|
|
fmt.Fprintf(&buf, " %s;", s.Body[i].String())
|
|
}
|
|
buf.WriteString(" END")
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type DropTriggerStatement struct {
|
|
Drop Pos // position of DROP keyword
|
|
Trigger Pos // position of TRIGGER keyword
|
|
If Pos // position of IF keyword
|
|
IfExists Pos // position of EXISTS keyword after IF
|
|
Name *Ident // trigger name
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *DropTriggerStatement) Clone() *DropTriggerStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.Name = s.Name.Clone()
|
|
return &other
|
|
}
|
|
|
|
func (s *DropTriggerStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("DROP TRIGGER")
|
|
if s.IfExists.IsValid() {
|
|
buf.WriteString(" IF EXISTS")
|
|
}
|
|
fmt.Fprintf(&buf, " %s", s.Name.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type BulkInsertStatement struct {
|
|
Bulk Pos // position of BULK keyword
|
|
Insert Pos // position of INSERT keyword
|
|
|
|
Table *Ident // table name
|
|
|
|
From Pos // position of FROM keyword
|
|
DataFile Expr // data file name
|
|
With Pos // position of WITH keyword
|
|
}
|
|
|
|
func (s *BulkInsertStatement) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("BULK INSERT ")
|
|
fmt.Fprintf(&buf, " %s", s.Table.String())
|
|
buf.WriteString(" FROM ")
|
|
fmt.Fprintf(&buf, " %s", s.DataFile.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type InsertStatement struct {
|
|
//WithClause *WithClause // clause containing CTEs
|
|
|
|
Insert Pos // position of INSERT keyword
|
|
Replace Pos // position of REPLACE keyword
|
|
InsertOr Pos // position of OR keyword after INSERT
|
|
InsertOrReplace Pos // position of REPLACE keyword after INSERT OR
|
|
// InsertOrRollback Pos // position of ROLLBACK keyword after INSERT OR
|
|
// InsertOrAbort Pos // position of ABORT keyword after INSERT OR
|
|
// InsertOrFail Pos // position of FAIL keyword after INSERT OR
|
|
// InsertOrIgnore Pos // position of IGNORE keyword after INSERT OR
|
|
Into Pos // position of INTO keyword
|
|
|
|
Table *Ident // table name
|
|
As Pos // position of AS keyword
|
|
Alias *Ident // optional alias
|
|
|
|
ColumnsLparen Pos // position of column list left paren
|
|
Columns []*Ident // optional column list
|
|
ColumnsRparen Pos // position of column list right paren
|
|
|
|
Values Pos // position of VALUES keyword
|
|
ValueList *ExprList // list of values
|
|
|
|
// Select *SelectStatement // SELECT statement
|
|
|
|
// Default Pos // position of DEFAULT keyword
|
|
// DefaultValues Pos // position of VALUES keyword after DEFAULT
|
|
|
|
// UpsertClause *UpsertClause // optional upsert clause
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *InsertStatement) Clone() *InsertStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
//other.WithClause = s.WithClause.Clone()
|
|
other.Table = s.Table.Clone()
|
|
other.Alias = s.Alias.Clone()
|
|
other.Columns = cloneIdents(s.Columns)
|
|
other.ValueList = s.ValueList.Clone()
|
|
//other.Select = s.Select.Clone()
|
|
//other.UpsertClause = s.UpsertClause.Clone()
|
|
return &other
|
|
}
|
|
|
|
func (s *InsertStatement) String() string {
|
|
var buf bytes.Buffer
|
|
//if s.WithClause != nil {
|
|
// buf.WriteString(s.WithClause.String())
|
|
// buf.WriteString(" ")
|
|
//}
|
|
|
|
//if s.Replace.IsValid() {
|
|
// buf.WriteString("REPLACE")
|
|
//} else {
|
|
buf.WriteString("INSERT")
|
|
if s.InsertOrReplace.IsValid() {
|
|
buf.WriteString(" OR REPLACE")
|
|
//} else if s.InsertOrRollback.IsValid() {
|
|
// buf.WriteString(" OR ROLLBACK")
|
|
//} else if s.InsertOrAbort.IsValid() {
|
|
// buf.WriteString(" OR ABORT")
|
|
//} else if s.InsertOrFail.IsValid() {
|
|
// buf.WriteString(" OR FAIL")
|
|
//} else if s.InsertOrIgnore.IsValid() {
|
|
// buf.WriteString(" OR IGNORE")
|
|
//}
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " INTO %s", s.Table.String())
|
|
if s.Alias != nil {
|
|
fmt.Fprintf(&buf, " AS %s", s.Alias.String())
|
|
}
|
|
|
|
if len(s.Columns) != 0 {
|
|
buf.WriteString(" (")
|
|
for i, col := range s.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
//if s.DefaultValues.IsValid() {
|
|
// buf.WriteString(" DEFAULT VALUES")
|
|
//} else if s.Select != nil {
|
|
// fmt.Fprintf(&buf, " %s", s.Select.String())
|
|
//} else {
|
|
buf.WriteString(" VALUES")
|
|
buf.WriteString(" (")
|
|
for j, expr := range s.ValueList.Exprs {
|
|
if j != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(expr.String())
|
|
}
|
|
buf.WriteString(")")
|
|
//}
|
|
|
|
//if s.UpsertClause != nil {
|
|
// fmt.Fprintf(&buf, " %s", s.UpsertClause.String())
|
|
//}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type UpsertClause struct {
|
|
On Pos // position of ON keyword
|
|
OnConflict Pos // position of CONFLICT keyword after ON
|
|
|
|
Lparen Pos // position of column list left paren
|
|
Columns []*IndexedColumn // optional indexed column list
|
|
Rparen Pos // position of column list right paren
|
|
Where Pos // position of WHERE keyword
|
|
WhereExpr Expr // optional conditional expression
|
|
|
|
Do Pos // position of DO keyword
|
|
DoNothing Pos // position of NOTHING keyword after DO
|
|
DoUpdate Pos // position of UPDATE keyword after DO
|
|
DoUpdateSet Pos // position of SET keyword after DO UPDATE
|
|
Assignments []*Assignment // list of column assignments
|
|
UpdateWhere Pos // position of WHERE keyword for DO UPDATE SET
|
|
UpdateWhereExpr Expr // optional conditional expression for DO UPDATE SET
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *UpsertClause) Clone() *UpsertClause {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.Columns = cloneIndexedColumns(c.Columns)
|
|
other.WhereExpr = CloneExpr(c.WhereExpr)
|
|
other.Assignments = cloneAssignments(c.Assignments)
|
|
other.UpdateWhereExpr = CloneExpr(c.UpdateWhereExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (c *UpsertClause) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("ON CONFLICT")
|
|
|
|
if len(c.Columns) != 0 {
|
|
buf.WriteString(" (")
|
|
for i, col := range c.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
|
|
if c.WhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", c.WhereExpr.String())
|
|
}
|
|
}
|
|
|
|
buf.WriteString(" DO")
|
|
if c.DoNothing.IsValid() {
|
|
buf.WriteString(" NOTHING")
|
|
} else {
|
|
buf.WriteString(" UPDATE SET ")
|
|
for i := range c.Assignments {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(c.Assignments[i].String())
|
|
}
|
|
|
|
if c.UpdateWhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", c.UpdateWhereExpr.String())
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type UpdateStatement struct {
|
|
WithClause *WithClause // clause containing CTEs
|
|
|
|
Update Pos // position of UPDATE keyword
|
|
UpdateOr Pos // position of OR keyword after UPDATE
|
|
UpdateOrReplace Pos // position of REPLACE keyword after UPDATE OR
|
|
UpdateOrRollback Pos // position of ROLLBACK keyword after UPDATE OR
|
|
UpdateOrAbort Pos // position of ABORT keyword after UPDATE OR
|
|
UpdateOrFail Pos // position of FAIL keyword after UPDATE OR
|
|
UpdateOrIgnore Pos // position of IGNORE keyword after UPDATE OR
|
|
|
|
Table *QualifiedTableName // table name
|
|
|
|
Set Pos // position of SET keyword
|
|
Assignments []*Assignment // list of column assignments
|
|
Where Pos // position of WHERE keyword
|
|
WhereExpr Expr // conditional expression
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *UpdateStatement) Clone() *UpdateStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.WithClause = s.WithClause.Clone()
|
|
other.Table = s.Table.Clone()
|
|
other.Assignments = cloneAssignments(s.Assignments)
|
|
other.WhereExpr = CloneExpr(s.WhereExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (s *UpdateStatement) String() string {
|
|
var buf bytes.Buffer
|
|
if s.WithClause != nil {
|
|
buf.WriteString(s.WithClause.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
buf.WriteString("UPDATE")
|
|
if s.UpdateOrRollback.IsValid() {
|
|
buf.WriteString(" OR ROLLBACK")
|
|
} else if s.UpdateOrAbort.IsValid() {
|
|
buf.WriteString(" OR ABORT")
|
|
} else if s.UpdateOrReplace.IsValid() {
|
|
buf.WriteString(" OR REPLACE")
|
|
} else if s.UpdateOrFail.IsValid() {
|
|
buf.WriteString(" OR FAIL")
|
|
} else if s.UpdateOrIgnore.IsValid() {
|
|
buf.WriteString(" OR IGNORE")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " %s ", s.Table.String())
|
|
|
|
buf.WriteString("SET ")
|
|
for i := range s.Assignments {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(s.Assignments[i].String())
|
|
}
|
|
|
|
if s.WhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", s.WhereExpr.String())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type DeleteStatement struct {
|
|
WithClause *WithClause // clause containing CTEs
|
|
Delete Pos // position of UPDATE keyword
|
|
From Pos // position of FROM keyword
|
|
Table *QualifiedTableName // table name
|
|
|
|
Where Pos // position of WHERE keyword
|
|
WhereExpr Expr // conditional expression
|
|
|
|
Order Pos // position of ORDER keyword
|
|
OrderBy Pos // position of BY keyword after ORDER
|
|
OrderingTerms []*OrderingTerm // terms of ORDER BY clause
|
|
|
|
Limit Pos // position of LIMIT keyword
|
|
LimitExpr Expr // limit expression
|
|
Offset Pos // position of OFFSET keyword
|
|
OffsetComma Pos // position of COMMA (instead of OFFSET)
|
|
OffsetExpr Expr // offset expression
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *DeleteStatement) Clone() *DeleteStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.WithClause = s.WithClause.Clone()
|
|
other.Table = s.Table.Clone()
|
|
other.WhereExpr = CloneExpr(s.WhereExpr)
|
|
other.OrderingTerms = cloneOrderingTerms(s.OrderingTerms)
|
|
other.LimitExpr = CloneExpr(s.LimitExpr)
|
|
other.OffsetExpr = CloneExpr(s.OffsetExpr)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (s *DeleteStatement) String() string {
|
|
var buf bytes.Buffer
|
|
if s.WithClause != nil {
|
|
buf.WriteString(s.WithClause.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, "DELETE FROM %s", s.Table.String())
|
|
if s.WhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", s.WhereExpr.String())
|
|
}
|
|
|
|
// Write ORDER BY.
|
|
if len(s.OrderingTerms) != 0 {
|
|
buf.WriteString(" ORDER BY ")
|
|
for i, term := range s.OrderingTerms {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(term.String())
|
|
}
|
|
}
|
|
|
|
// Write LIMIT/OFFSET.
|
|
if s.LimitExpr != nil {
|
|
fmt.Fprintf(&buf, " LIMIT %s", s.LimitExpr.String())
|
|
if s.OffsetExpr != nil {
|
|
fmt.Fprintf(&buf, " OFFSET %s", s.OffsetExpr.String())
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// Assignment is used within the UPDATE statement & upsert clause.
|
|
// It is similiar to an expression except that it must be an equality.
|
|
type Assignment struct {
|
|
Lparen Pos // position of column list left paren
|
|
Columns []*Ident // column list
|
|
Rparen Pos // position of column list right paren
|
|
Eq Pos // position of =
|
|
Expr Expr // assigned expression
|
|
}
|
|
|
|
// Clone returns a deep copy of a.
|
|
func (a *Assignment) Clone() *Assignment {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := *a
|
|
other.Columns = cloneIdents(a.Columns)
|
|
other.Expr = CloneExpr(a.Expr)
|
|
return &other
|
|
}
|
|
|
|
func cloneAssignments(a []*Assignment) []*Assignment {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*Assignment, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (a *Assignment) String() string {
|
|
var buf bytes.Buffer
|
|
if len(a.Columns) == 1 {
|
|
buf.WriteString(a.Columns[0].String())
|
|
} else if len(a.Columns) > 1 {
|
|
buf.WriteString("(")
|
|
for i, col := range a.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " = %s", a.Expr.String())
|
|
return buf.String()
|
|
}
|
|
|
|
type IndexedColumn struct {
|
|
X Expr // column expression
|
|
Asc Pos // position of optional ASC keyword
|
|
Desc Pos // position of optional DESC keyword
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *IndexedColumn) Clone() *IndexedColumn {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.X = CloneExpr(c.X)
|
|
return &other
|
|
}
|
|
|
|
func cloneIndexedColumns(a []*IndexedColumn) []*IndexedColumn {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*IndexedColumn, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the column.
|
|
func (c *IndexedColumn) String() string {
|
|
if c.Asc.IsValid() {
|
|
return fmt.Sprintf("%s ASC", c.X.String())
|
|
} else if c.Desc.IsValid() {
|
|
return fmt.Sprintf("%s DESC", c.X.String())
|
|
}
|
|
return c.X.String()
|
|
}
|
|
|
|
type SelectStatement struct {
|
|
WithClause *WithClause // clause containing CTEs
|
|
|
|
// Values Pos // position of VALUES keyword
|
|
// ValueLists []*ExprList // lists of lists of values
|
|
|
|
Select Pos // position of SELECT keyword
|
|
Distinct Pos // position of DISTINCT keyword
|
|
// All Pos // position of ALL keyword
|
|
Columns []*ResultColumn // list of result columns in the SELECT clause
|
|
|
|
Top Pos // position of TOP keyword
|
|
TopN Pos // position of TOPN keyword
|
|
TopExpr Expr // TOP expr
|
|
|
|
From Pos // position of FROM keyword
|
|
Source Source // chain of tables & subqueries in FROM clause
|
|
|
|
Where Pos // position of WHERE keyword
|
|
WhereExpr Expr // condition for WHERE clause
|
|
|
|
Group Pos // position of GROUP keyword
|
|
GroupBy Pos // position of BY keyword after GROUP
|
|
GroupByExprs []Expr // group by expression list
|
|
Having Pos // position of HAVING keyword
|
|
HavingExpr Expr // HAVING expression
|
|
|
|
Window Pos // position of WINDOW keyword
|
|
Windows []*Window // window list
|
|
|
|
Union Pos // position of UNION keyword
|
|
UnionAll Pos // position of ALL keyword after UNION
|
|
Intersect Pos // position of INTERSECT keyword
|
|
Except Pos // position of EXCEPT keyword
|
|
Compound *SelectStatement // compounded SELECT statement
|
|
|
|
Order Pos // position of ORDER keyword
|
|
OrderBy Pos // position of BY keyword after ORDER
|
|
OrderingTerms []*OrderingTerm // terms of ORDER BY clause
|
|
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *SelectStatement) Clone() *SelectStatement {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.WithClause = s.WithClause.Clone()
|
|
//other.ValueLists = cloneExprLists(s.ValueLists)
|
|
other.TopExpr = CloneExpr(s.TopExpr)
|
|
other.Columns = cloneResultColumns(s.Columns)
|
|
other.Source = CloneSource(s.Source)
|
|
other.WhereExpr = CloneExpr(s.WhereExpr)
|
|
other.GroupByExprs = cloneExprs(s.GroupByExprs)
|
|
other.HavingExpr = CloneExpr(s.HavingExpr)
|
|
other.Windows = cloneWindows(s.Windows)
|
|
other.Compound = s.Compound.Clone()
|
|
other.OrderingTerms = cloneOrderingTerms(s.OrderingTerms)
|
|
return &other
|
|
}
|
|
|
|
func (expr *SelectStatement) IsLiteral() bool { return false }
|
|
|
|
// HasWildcard returns true any result column contains a wildcard (STAR).
|
|
func (s *SelectStatement) HasWildcard() bool {
|
|
for _, col := range s.Columns {
|
|
// Unqualified wildcard.
|
|
if col.Star.IsValid() {
|
|
return true
|
|
}
|
|
|
|
// Table-qualified wildcard.
|
|
if ref, ok := col.Expr.(*QualifiedRef); ok && ref.Star.IsValid() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (s *SelectStatement) DataType() ExprDataType {
|
|
return nil
|
|
}
|
|
|
|
func (s *SelectStatement) Pos() Pos {
|
|
return s.Select
|
|
}
|
|
|
|
// String returns the string representation of the statement.
|
|
func (s *SelectStatement) String() string {
|
|
var buf bytes.Buffer
|
|
if s.WithClause != nil {
|
|
buf.WriteString(s.WithClause.String())
|
|
buf.WriteString(" ")
|
|
}
|
|
|
|
/*if len(s.ValueLists) > 0 {
|
|
buf.WriteString("VALUES ")
|
|
for i, exprs := range s.ValueLists {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
|
|
buf.WriteString("(")
|
|
for j, expr := range exprs.Exprs {
|
|
if j != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(expr.String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
} else {*/
|
|
buf.WriteString("SELECT ")
|
|
if s.Distinct.IsValid() {
|
|
buf.WriteString("DISTINCT ")
|
|
} //else if s.All.IsValid() {
|
|
// buf.WriteString("ALL ")
|
|
//}
|
|
if s.Top.IsValid() {
|
|
fmt.Fprintf(&buf, "TOP(%s) ", s.TopExpr.String())
|
|
}
|
|
if s.TopN.IsValid() {
|
|
fmt.Fprintf(&buf, "TOPN(%s) ", s.TopExpr.String())
|
|
}
|
|
|
|
for i, col := range s.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
|
|
if s.Source != nil {
|
|
fmt.Fprintf(&buf, " FROM %s", s.Source.String())
|
|
}
|
|
|
|
if s.WhereExpr != nil {
|
|
fmt.Fprintf(&buf, " WHERE %s", s.WhereExpr.String())
|
|
}
|
|
|
|
if len(s.GroupByExprs) != 0 {
|
|
buf.WriteString(" GROUP BY ")
|
|
for i, expr := range s.GroupByExprs {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(expr.String())
|
|
}
|
|
|
|
if s.HavingExpr != nil {
|
|
fmt.Fprintf(&buf, " HAVING %s", s.HavingExpr.String())
|
|
}
|
|
}
|
|
|
|
if len(s.Windows) != 0 {
|
|
buf.WriteString(" WINDOW ")
|
|
for i, window := range s.Windows {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(window.String())
|
|
}
|
|
}
|
|
// }
|
|
|
|
// Write compound operator.
|
|
if s.Compound != nil {
|
|
switch {
|
|
case s.Union.IsValid():
|
|
buf.WriteString(" UNION")
|
|
if s.UnionAll.IsValid() {
|
|
buf.WriteString(" ALL")
|
|
}
|
|
case s.Intersect.IsValid():
|
|
buf.WriteString(" INTERSECT")
|
|
case s.Except.IsValid():
|
|
buf.WriteString(" EXCEPT")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " %s", s.Compound.String())
|
|
}
|
|
|
|
// Write ORDER BY.
|
|
if len(s.OrderingTerms) != 0 {
|
|
buf.WriteString(" ORDER BY ")
|
|
for i, term := range s.OrderingTerms {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(term.String())
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (c *SelectStatement) SourceFromAlias(alias string) Source {
|
|
return nil
|
|
}
|
|
|
|
func (c *SelectStatement) PossibleOutputColumns() []*SourceOutputColumn {
|
|
result := make([]*SourceOutputColumn, 0)
|
|
// populate the output columns from the columns in the select list
|
|
for idx, col := range c.Columns {
|
|
soc := &SourceOutputColumn{
|
|
TableName: "",
|
|
ColumnName: col.Name(),
|
|
ColumnIndex: idx,
|
|
Datatype: col.Expr.DataType(),
|
|
}
|
|
result = append(result, soc)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (c *SelectStatement) OutputColumnNamed(name string) (*SourceOutputColumn, error) {
|
|
ocs := c.PossibleOutputColumns()
|
|
|
|
for _, oc := range ocs {
|
|
if strings.EqualFold(oc.ColumnName, name) {
|
|
return oc, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *SelectStatement) OutputColumnQualifierNamed(qualifier string, name string) (*SourceOutputColumn, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type ResultColumn struct {
|
|
Star Pos // position of *
|
|
Expr Expr // column expression (may be "tbl.*")
|
|
As Pos // position of AS keyword
|
|
Alias *Ident // alias name
|
|
}
|
|
|
|
// Name returns the column name. Uses the alias, if specified.
|
|
// Otherwise returns a generated name.
|
|
func (c *ResultColumn) Name() string {
|
|
if c.Alias != nil {
|
|
return IdentName(c.Alias)
|
|
}
|
|
|
|
switch expr := c.Expr.(type) {
|
|
case *Ident:
|
|
return IdentName(expr)
|
|
case *QualifiedRef:
|
|
return IdentName(expr.Column)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (expr *ResultColumn) IsLiteral() bool { return false }
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *ResultColumn) Clone() *ResultColumn {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.Expr = CloneExpr(c.Expr)
|
|
other.Alias = c.Alias.Clone()
|
|
return &other
|
|
}
|
|
|
|
func cloneResultColumns(a []*ResultColumn) []*ResultColumn {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*ResultColumn, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the column.
|
|
func (c *ResultColumn) String() string {
|
|
if c.Star.IsValid() {
|
|
return "*"
|
|
} else if c.Alias != nil {
|
|
return fmt.Sprintf("%s AS %s", c.Expr.String(), c.Alias.String())
|
|
}
|
|
return c.Expr.String()
|
|
}
|
|
|
|
type QualifiedTableName struct {
|
|
Name *Ident // table name
|
|
As Pos // position of AS keyword
|
|
Alias *Ident // optional table alias
|
|
Indexed Pos // position of INDEXED keyword
|
|
IndexedBy Pos // position of BY keyword after INDEXED
|
|
Not Pos // position of NOT keyword before INDEXED
|
|
NotIndexed Pos // position of NOT keyword before INDEXED
|
|
Index *Ident // name of index
|
|
OutputColumns []*SourceOutputColumn // output columns - populated during analysis
|
|
}
|
|
|
|
// TableName returns the name used to identify n.
|
|
// Returns the alias, if one is specified. Otherwise returns the name.
|
|
func (n *QualifiedTableName) TableName() string {
|
|
if s := IdentName(n.Alias); s != "" {
|
|
return s
|
|
}
|
|
return IdentName(n.Name)
|
|
}
|
|
|
|
func (n *QualifiedTableName) MatchesTablenameOrAlias(match string) bool {
|
|
return strings.EqualFold(IdentName(n.Alias), match) || strings.EqualFold(IdentName(n.Name), match)
|
|
}
|
|
|
|
// Clone returns a deep copy of n.
|
|
func (n *QualifiedTableName) Clone() *QualifiedTableName {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
other := *n
|
|
other.Name = n.Name.Clone()
|
|
other.Alias = n.Alias.Clone()
|
|
other.Index = n.Index.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the table name.
|
|
func (n *QualifiedTableName) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(n.Name.String())
|
|
if n.Alias != nil {
|
|
fmt.Fprintf(&buf, " AS %s", n.Alias.String())
|
|
}
|
|
|
|
if n.Index != nil {
|
|
fmt.Fprintf(&buf, " INDEXED BY %s", n.Index.String())
|
|
} else if n.NotIndexed.IsValid() {
|
|
buf.WriteString(" NOT INDEXED")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (c *QualifiedTableName) SourceFromAlias(alias string) Source {
|
|
if strings.EqualFold(IdentName(c.Alias), alias) {
|
|
return c
|
|
}
|
|
if strings.EqualFold(IdentName(c.Name), alias) {
|
|
return c
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *QualifiedTableName) PossibleOutputColumns() []*SourceOutputColumn {
|
|
return c.OutputColumns
|
|
}
|
|
|
|
func (c *QualifiedTableName) OutputColumnNamed(name string) (*SourceOutputColumn, error) {
|
|
for _, oc := range c.OutputColumns {
|
|
if strings.EqualFold(oc.ColumnName, name) {
|
|
return oc, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *QualifiedTableName) OutputColumnQualifierNamed(qualifier string, name string) (*SourceOutputColumn, error) {
|
|
if strings.EqualFold(IdentName(c.Alias), qualifier) || strings.EqualFold(IdentName(c.Name), qualifier) {
|
|
return c.OutputColumnNamed(name)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
type ParenSource struct {
|
|
Lparen Pos // position of left paren
|
|
X Source // nested source
|
|
Rparen Pos // position of right paren
|
|
As Pos // position of AS keyword (select source only)
|
|
Alias *Ident // optional table alias (select source only)
|
|
}
|
|
|
|
// Clone returns a deep copy of s.
|
|
func (s *ParenSource) Clone() *ParenSource {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
other := *s
|
|
other.X = CloneSource(s.X)
|
|
other.Alias = s.Alias.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the source.
|
|
func (s *ParenSource) String() string {
|
|
if s.Alias != nil {
|
|
return fmt.Sprintf("(%s) AS %s", s.X.String(), s.Alias.String())
|
|
}
|
|
return fmt.Sprintf("(%s)", s.X.String())
|
|
}
|
|
|
|
func (c *ParenSource) SourceFromAlias(alias string) Source {
|
|
if strings.EqualFold(IdentName(c.Alias), alias) {
|
|
return c
|
|
}
|
|
return c.X.SourceFromAlias(alias)
|
|
}
|
|
|
|
func (c *ParenSource) PossibleOutputColumns() []*SourceOutputColumn {
|
|
return c.X.PossibleOutputColumns()
|
|
}
|
|
|
|
func (c *ParenSource) OutputColumnNamed(name string) (*SourceOutputColumn, error) {
|
|
return c.X.OutputColumnNamed(name)
|
|
}
|
|
|
|
func (c *ParenSource) OutputColumnQualifierNamed(qualifier string, name string) (*SourceOutputColumn, error) {
|
|
if strings.EqualFold(IdentName(c.Alias), qualifier) {
|
|
return c.OutputColumnNamed(name)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
type JoinClause struct {
|
|
X Source // lhs source
|
|
Operator *JoinOperator // join operator
|
|
Y Source // rhs source
|
|
Constraint JoinConstraint // join constraint
|
|
OutputColumns []*SourceOutputColumn // output columns - populated during analysis
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *JoinClause) Clone() *JoinClause {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.X = CloneSource(c.X)
|
|
other.Y = CloneSource(c.Y)
|
|
other.Constraint = CloneJoinConstraint(c.Constraint)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (c *JoinClause) String() string {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "%s%s%s", c.X.String(), c.Operator.String(), c.Y.String())
|
|
if c.Constraint != nil {
|
|
fmt.Fprintf(&buf, " %s", c.Constraint.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (c *JoinClause) PossibleOutputColumns() []*SourceOutputColumn {
|
|
return c.OutputColumns
|
|
}
|
|
|
|
func (c *JoinClause) OutputColumnNamed(name string) (*SourceOutputColumn, error) {
|
|
if col, err := c.X.OutputColumnNamed(name); err != nil {
|
|
return nil, err
|
|
} else if col != nil {
|
|
return col, nil
|
|
}
|
|
|
|
if col, err := c.Y.OutputColumnNamed(name); err != nil {
|
|
return nil, err
|
|
} else if col != nil {
|
|
return col, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *JoinClause) OutputColumnQualifierNamed(qualifier string, name string) (*SourceOutputColumn, error) {
|
|
if col, err := c.X.OutputColumnQualifierNamed(qualifier, name); err != nil {
|
|
return nil, err
|
|
} else if col != nil {
|
|
return col, nil
|
|
}
|
|
|
|
if col, err := c.Y.OutputColumnQualifierNamed(qualifier, name); err != nil {
|
|
return nil, err
|
|
} else if col != nil {
|
|
return col, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *JoinClause) SourceFromAlias(alias string) Source {
|
|
if src := c.X.SourceFromAlias(alias); src != nil {
|
|
return src
|
|
}
|
|
if src := c.Y.SourceFromAlias(alias); src != nil {
|
|
return src
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type JoinOperator struct {
|
|
Comma Pos // position of comma
|
|
Natural Pos // position of NATURAL keyword
|
|
Left Pos // position of LEFT keyword
|
|
Outer Pos // position of OUTER keyword
|
|
Inner Pos // position of INNER keyword
|
|
Cross Pos // position of CROSS keyword
|
|
Join Pos // position of JOIN keyword
|
|
}
|
|
|
|
// Clone returns a deep copy of op.
|
|
func (op *JoinOperator) Clone() *JoinOperator {
|
|
if op == nil {
|
|
return nil
|
|
}
|
|
other := *op
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the operator.
|
|
func (op *JoinOperator) String() string {
|
|
if op.Comma.IsValid() {
|
|
return ", "
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if op.Natural.IsValid() {
|
|
buf.WriteString(" NATURAL")
|
|
}
|
|
if op.Left.IsValid() {
|
|
buf.WriteString(" LEFT")
|
|
if op.Outer.IsValid() {
|
|
buf.WriteString(" OUTER")
|
|
}
|
|
} else if op.Inner.IsValid() {
|
|
buf.WriteString(" INNER")
|
|
} else if op.Cross.IsValid() {
|
|
buf.WriteString(" CROSS")
|
|
}
|
|
buf.WriteString(" JOIN ")
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type OnConstraint struct {
|
|
On Pos // position of ON keyword
|
|
X Expr // constraint expression
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *OnConstraint) Clone() *OnConstraint {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.X = CloneExpr(c.X)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *OnConstraint) String() string {
|
|
return "ON " + c.X.String()
|
|
}
|
|
|
|
type UsingConstraint struct {
|
|
Using Pos // position of USING keyword
|
|
Lparen Pos // position of left paren
|
|
Columns []*Ident // column list
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *UsingConstraint) Clone() *UsingConstraint {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.Columns = cloneIdents(c.Columns)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the constraint.
|
|
func (c *UsingConstraint) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("USING (")
|
|
for i, col := range c.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
return buf.String()
|
|
}
|
|
|
|
type WithClause struct {
|
|
With Pos // position of WITH keyword
|
|
Recursive Pos // position of RECURSIVE keyword
|
|
CTEs []*CTE // common table expressions
|
|
}
|
|
|
|
// Clone returns a deep copy of c.
|
|
func (c *WithClause) Clone() *WithClause {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
other := *c
|
|
other.CTEs = cloneCTEs(c.CTEs)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the clause.
|
|
func (c *WithClause) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("WITH ")
|
|
if c.Recursive.IsValid() {
|
|
buf.WriteString("RECURSIVE ")
|
|
}
|
|
|
|
for i, cte := range c.CTEs {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(cte.String())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// CTE represents an AST node for a common table expression.
|
|
type CTE struct {
|
|
TableName *Ident // table name
|
|
ColumnsLparen Pos // position of column list left paren
|
|
Columns []*Ident // optional column list
|
|
ColumnsRparen Pos // position of column list right paren
|
|
As Pos // position of AS keyword
|
|
SelectLparen Pos // position of select left paren
|
|
Select *SelectStatement // select statement
|
|
SelectRparen Pos // position of select right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of cte.
|
|
func (cte *CTE) Clone() *CTE {
|
|
if cte == nil {
|
|
return nil
|
|
}
|
|
other := *cte
|
|
other.TableName = cte.TableName.Clone()
|
|
other.Columns = cloneIdents(cte.Columns)
|
|
other.Select = cte.Select.Clone()
|
|
return &other
|
|
}
|
|
|
|
func cloneCTEs(a []*CTE) []*CTE {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*CTE, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the CTE.
|
|
func (cte *CTE) String() string {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "%s", cte.TableName.String())
|
|
|
|
if len(cte.Columns) != 0 {
|
|
buf.WriteString(" (")
|
|
for i, col := range cte.Columns {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString(")")
|
|
}
|
|
|
|
fmt.Fprintf(&buf, " AS (%s)", cte.Select.String())
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type ParenExpr struct {
|
|
Lparen Pos // position of left paren
|
|
X Expr // parenthesized expression
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
func (expr *ParenExpr) IsLiteral() bool {
|
|
return expr.X.IsLiteral()
|
|
}
|
|
|
|
func (expr *ParenExpr) DataType() ExprDataType {
|
|
return expr.X.DataType()
|
|
}
|
|
|
|
func (expr *ParenExpr) Pos() Pos {
|
|
return expr.Lparen
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *ParenExpr) Clone() *ParenExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.X = CloneExpr(expr.X)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *ParenExpr) String() string {
|
|
return fmt.Sprintf("(%s)", expr.X.String())
|
|
}
|
|
|
|
type SetLiteralExpr struct {
|
|
Lbracket Pos // position of left bracket
|
|
Members []Expr // bracketed expression
|
|
Rbracket Pos // position of right bracket
|
|
|
|
ResultDataType ExprDataType
|
|
}
|
|
|
|
func (expr *SetLiteralExpr) IsLiteral() bool {
|
|
return true
|
|
}
|
|
|
|
func (expr *SetLiteralExpr) DataType() ExprDataType {
|
|
return expr.ResultDataType
|
|
}
|
|
|
|
func (expr *SetLiteralExpr) Pos() Pos {
|
|
return expr.Lbracket
|
|
}
|
|
|
|
// Clone returns a deep copy of expr.
|
|
func (expr *SetLiteralExpr) Clone() *SetLiteralExpr {
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
other := *expr
|
|
other.Members = cloneExprs(expr.Members)
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the expression.
|
|
func (expr *SetLiteralExpr) String() string {
|
|
var buf bytes.Buffer
|
|
|
|
if len(expr.Members) != 0 {
|
|
buf.WriteString("[")
|
|
for i, col := range expr.Members {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(col.String())
|
|
}
|
|
buf.WriteString("]")
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type Window struct {
|
|
Name *Ident // name of window
|
|
As Pos // position of AS keyword
|
|
Definition *WindowDefinition // window definition
|
|
}
|
|
|
|
// Clone returns a deep copy of w.
|
|
func (w *Window) Clone() *Window {
|
|
if w == nil {
|
|
return nil
|
|
}
|
|
other := *w
|
|
other.Name = w.Name.Clone()
|
|
other.Definition = w.Definition.Clone()
|
|
return &other
|
|
}
|
|
|
|
func cloneWindows(a []*Window) []*Window {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
other := make([]*Window, len(a))
|
|
for i := range a {
|
|
other[i] = a[i].Clone()
|
|
}
|
|
return other
|
|
}
|
|
|
|
// String returns the string representation of the window.
|
|
func (w *Window) String() string {
|
|
return fmt.Sprintf("%s AS %s", w.Name.String(), w.Definition.String())
|
|
}
|
|
|
|
type WindowDefinition struct {
|
|
Lparen Pos // position of left paren
|
|
Base *Ident // base window name
|
|
Partition Pos // position of PARTITION keyword
|
|
PartitionBy Pos // position of BY keyword (after PARTITION)
|
|
Partitions []Expr // partition expressions
|
|
Order Pos // position of ORDER keyword
|
|
OrderBy Pos // position of BY keyword (after ORDER)
|
|
OrderingTerms []*OrderingTerm // ordering terms
|
|
Frame *FrameSpec // frame
|
|
Rparen Pos // position of right paren
|
|
}
|
|
|
|
// Clone returns a deep copy of d.
|
|
func (d *WindowDefinition) Clone() *WindowDefinition {
|
|
if d == nil {
|
|
return nil
|
|
}
|
|
other := *d
|
|
other.Base = d.Base.Clone()
|
|
other.Partitions = cloneExprs(d.Partitions)
|
|
other.OrderingTerms = cloneOrderingTerms(d.OrderingTerms)
|
|
other.Frame = d.Frame.Clone()
|
|
return &other
|
|
}
|
|
|
|
// String returns the string representation of the window definition.
|
|
func (d *WindowDefinition) String() string {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("(")
|
|
if d.Base != nil {
|
|
buf.WriteString(d.Base.String())
|
|
}
|
|
|
|
if len(d.Partitions) != 0 {
|
|
if buf.Len() > 1 {
|
|
buf.WriteString(" ")
|
|
}
|
|
buf.WriteString("PARTITION BY ")
|
|
|
|
for i, p := range d.Partitions {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(p.String())
|
|
}
|
|
}
|
|
|
|
if len(d.OrderingTerms) != 0 {
|
|
if buf.Len() > 1 {
|
|
buf.WriteString(" ")
|
|
}
|
|
buf.WriteString("ORDER BY ")
|
|
|
|
for i, term := range d.OrderingTerms {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(term.String())
|
|
}
|
|
}
|
|
|
|
if d.Frame != nil {
|
|
if buf.Len() > 1 {
|
|
buf.WriteString(" ")
|
|
}
|
|
buf.WriteString(d.Frame.String())
|
|
}
|
|
|
|
buf.WriteString(")")
|
|
|
|
return buf.String()
|
|
}
|