Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
2267 lines
54 KiB
Go
2267 lines
54 KiB
Go
package dolphin
|
|
|
|
import (
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
|
|
pcast "github.com/sqlc-dev/marino/ast"
|
|
"github.com/sqlc-dev/marino/format"
|
|
"github.com/sqlc-dev/marino/mysql"
|
|
"github.com/sqlc-dev/marino/opcode"
|
|
"github.com/sqlc-dev/marino/types"
|
|
|
|
"github.com/sqlc-dev/sqlc/internal/debug"
|
|
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
|
)
|
|
|
|
type cc struct {
|
|
paramCount int
|
|
// preserveCase keeps identifiers exactly as written instead of
|
|
// lowercasing them for case-insensitive catalog matching; the format
|
|
// parser sets it (see NewFormatParser).
|
|
preserveCase bool
|
|
}
|
|
|
|
func todo(n pcast.Node) *ast.TODO {
|
|
if debug.Active {
|
|
log.Printf("dolphin.convert: Unknown node type %T\n", n)
|
|
}
|
|
return &ast.TODO{}
|
|
}
|
|
|
|
// identifier normalizes an identifier for the compiler, which matches
|
|
// case-insensitively by lowercasing everything. The format parser preserves
|
|
// the author's case instead: reprinting `Event` as `event` renames the
|
|
// table wherever table names are case-sensitive.
|
|
func (c *cc) identifier(id string) string {
|
|
if c.preserveCase {
|
|
return id
|
|
}
|
|
return strings.ToLower(id)
|
|
}
|
|
|
|
func (c *cc) NewIdentifier(t string) *ast.String {
|
|
return &ast.String{Str: c.identifier(t)}
|
|
}
|
|
|
|
func (c *cc) convertAlterTableStmt(n *pcast.AlterTableStmt) ast.Node {
|
|
alt := &ast.AlterTableStmt{
|
|
Table: c.parseTableName(n.Table),
|
|
Cmds: &ast.List{},
|
|
}
|
|
for _, spec := range n.Specs {
|
|
switch spec.Tp {
|
|
case pcast.AlterTableAddColumns:
|
|
for _, def := range spec.NewColumns {
|
|
name := def.Name.String()
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &name,
|
|
Subtype: ast.AT_AddColumn,
|
|
Def: convertColumnDef(def),
|
|
})
|
|
}
|
|
|
|
case pcast.AlterTableDropColumn:
|
|
name := spec.OldColumnName.String()
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &name,
|
|
Subtype: ast.AT_DropColumn,
|
|
MissingOk: spec.IfExists,
|
|
})
|
|
|
|
case pcast.AlterTableChangeColumn:
|
|
oldName := spec.OldColumnName.String()
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &oldName,
|
|
Subtype: ast.AT_DropColumn,
|
|
})
|
|
|
|
for _, def := range spec.NewColumns {
|
|
name := def.Name.String()
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &name,
|
|
Subtype: ast.AT_AddColumn,
|
|
Def: convertColumnDef(def),
|
|
})
|
|
}
|
|
|
|
case pcast.AlterTableModifyColumn:
|
|
for _, def := range spec.NewColumns {
|
|
name := def.Name.String()
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &name,
|
|
Subtype: ast.AT_DropColumn,
|
|
})
|
|
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
|
|
Name: &name,
|
|
Subtype: ast.AT_AddColumn,
|
|
Def: convertColumnDef(def),
|
|
})
|
|
}
|
|
|
|
case pcast.AlterTableAlterColumn:
|
|
// spew.Dump("alter column", spec)
|
|
|
|
case pcast.AlterTableAddConstraint:
|
|
// spew.Dump("add const", spec)
|
|
|
|
case pcast.AlterTableRenameColumn:
|
|
// TODO: Returning here may be incorrect if there are multiple specs
|
|
oldName := spec.OldColumnName.String()
|
|
newName := spec.NewColumnName.String()
|
|
return &ast.RenameColumnStmt{
|
|
Table: c.parseTableName(n.Table),
|
|
Col: &ast.ColumnRef{Name: oldName},
|
|
NewName: &newName,
|
|
}
|
|
|
|
case pcast.AlterTableRenameTable:
|
|
// TODO: Returning here may be incorrect if there are multiple specs
|
|
return &ast.RenameTableStmt{
|
|
Table: c.parseTableName(n.Table),
|
|
NewName: &c.parseTableName(spec.NewTable).Name,
|
|
}
|
|
|
|
default:
|
|
if debug.Active {
|
|
log.Printf("dolphin.convert: Unknown alter table cmd %v\n", spec.Tp)
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
return alt
|
|
}
|
|
|
|
func (c *cc) convertAssignment(n *pcast.Assignment) *ast.ResTarget {
|
|
name := c.identifier(n.Column.Name.String())
|
|
target := &ast.ResTarget{
|
|
Name: &name,
|
|
Val: c.convert(n.Expr),
|
|
}
|
|
// A multi-table UPDATE qualifies its SET columns (SET a.x = ..., b.x =
|
|
// ...); analysis matches on Name alone, but printing must keep the
|
|
// qualifier or both assignments collapse onto whichever table resolves.
|
|
if table := n.Column.Table.String(); table != "" {
|
|
table = c.identifier(table)
|
|
target.Relation = &table
|
|
}
|
|
return target
|
|
}
|
|
|
|
// TODO: These codes should be defined in the sql/lang package
|
|
func opToName(o opcode.Op) string {
|
|
switch o {
|
|
// case opcode.And:
|
|
// case opcode.BitNeg:
|
|
// case opcode.Case:
|
|
// case opcode.Div:
|
|
case opcode.EQ:
|
|
return "="
|
|
case opcode.GE:
|
|
return ">="
|
|
case opcode.GT:
|
|
return ">"
|
|
// case opcode.In:
|
|
case opcode.IntDiv:
|
|
return "/"
|
|
// case opcode.IsFalsity:
|
|
// case opcode.IsNull:
|
|
// case opcode.IsTruth:
|
|
case opcode.LE:
|
|
return "<="
|
|
case opcode.LT:
|
|
return "<"
|
|
case opcode.LeftShift:
|
|
return "<<"
|
|
// case opcode.Like:
|
|
case opcode.LogicAnd:
|
|
return "&"
|
|
case opcode.LogicOr:
|
|
return "|"
|
|
// case opcode.LogicXor:
|
|
case opcode.Minus:
|
|
return "-"
|
|
case opcode.Mod:
|
|
return "%"
|
|
case opcode.Mul:
|
|
return "*"
|
|
case opcode.NE:
|
|
return "!="
|
|
case opcode.Not:
|
|
return "!"
|
|
// case opcode.NullEQ:
|
|
// case opcode.Or:
|
|
case opcode.Plus:
|
|
return "+"
|
|
case opcode.Regexp:
|
|
return "~"
|
|
case opcode.RightShift:
|
|
return ">>"
|
|
case opcode.Xor:
|
|
return "#"
|
|
default:
|
|
return o.String()
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertBinaryOperationExpr(n *pcast.BinaryOperationExpr) ast.Node {
|
|
if n.Op == opcode.LogicAnd || n.Op == opcode.LogicOr {
|
|
var boolop ast.BoolExprType
|
|
if n.Op == opcode.LogicAnd {
|
|
boolop = ast.BoolExprTypeAnd
|
|
} else {
|
|
boolop = ast.BoolExprTypeOr
|
|
}
|
|
return &ast.BoolExpr{
|
|
Boolop: boolop,
|
|
Args: &ast.List{
|
|
Items: []ast.Node{
|
|
c.convert(n.L),
|
|
c.convert(n.R),
|
|
},
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
} else {
|
|
return &ast.A_Expr{
|
|
// TODO: Set kind
|
|
Name: &ast.List{
|
|
Items: []ast.Node{
|
|
&ast.String{Str: opToName(n.Op)},
|
|
},
|
|
},
|
|
Lexpr: c.convert(n.L),
|
|
Rexpr: c.convert(n.R),
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertCreateTableStmt(n *pcast.CreateTableStmt) ast.Node {
|
|
create := &ast.CreateTableStmt{
|
|
Name: c.parseTableName(n.Table),
|
|
IfNotExists: n.IfNotExists,
|
|
}
|
|
if n.ReferTable != nil {
|
|
create.ReferTable = c.parseTableName(n.ReferTable)
|
|
}
|
|
for _, def := range n.Cols {
|
|
create.Cols = append(create.Cols, convertColumnDef(def))
|
|
}
|
|
for _, opt := range n.Options {
|
|
switch opt.Tp {
|
|
case pcast.TableOptionComment:
|
|
create.Comment = opt.StrValue
|
|
}
|
|
}
|
|
return create
|
|
}
|
|
|
|
func convertColumnDef(def *pcast.ColumnDef) *ast.ColumnDef {
|
|
var vals *ast.List
|
|
if len(def.Tp.GetElems()) > 0 {
|
|
vals = &ast.List{}
|
|
for i := range def.Tp.GetElems() {
|
|
vals.Items = append(vals.Items, &ast.String{
|
|
Str: def.Tp.GetElems()[i],
|
|
})
|
|
}
|
|
}
|
|
comment := ""
|
|
for _, opt := range def.Options {
|
|
switch opt.Tp {
|
|
case pcast.ColumnOptionComment:
|
|
if value, ok := opt.Expr.(*pcast.ValueExprBase); ok {
|
|
comment = value.GetString()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build TypeName with modifiers for proper formatting
|
|
typeName := &ast.TypeName{Name: types.TypeToStr(def.Tp.GetType(), def.Tp.GetCharset())}
|
|
|
|
// Add type modifiers (e.g., length for varchar(255), char(32))
|
|
// Only for types where length is meaningful and user-specified
|
|
tp := def.Tp.GetType()
|
|
flen := def.Tp.GetFlen()
|
|
needsLength := false
|
|
switch tp {
|
|
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString:
|
|
// VARCHAR(n), CHAR(n) - always need length
|
|
needsLength = flen >= 0
|
|
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
|
|
// BLOB types - only if user specified length (VARBINARY(n), BINARY(n))
|
|
// Default blob types don't need length
|
|
needsLength = false
|
|
}
|
|
|
|
if needsLength {
|
|
typeName.Typmods = &ast.List{
|
|
Items: []ast.Node{
|
|
&ast.Integer{Ival: int64(flen)},
|
|
},
|
|
}
|
|
}
|
|
|
|
// DECIMAL(p,s) / NUMERIC(p,s) and FLOAT/DOUBLE with explicit precision:
|
|
// the parser leaves flen and decimal at -1 when the author wrote none,
|
|
// so their presence is the author's, and dropping them would change the
|
|
// column's type.
|
|
switch tp {
|
|
case mysql.TypeNewDecimal, mysql.TypeFloat, mysql.TypeDouble:
|
|
if flen >= 0 {
|
|
mods := []ast.Node{&ast.Integer{Ival: int64(flen)}}
|
|
if dec := def.Tp.GetDecimal(); dec > 0 {
|
|
mods = append(mods, &ast.Integer{Ival: int64(dec)})
|
|
}
|
|
typeName.Typmods = &ast.List{Items: mods}
|
|
}
|
|
// MySQL drops an integer's display width except the one that means
|
|
// something: tinyint(1) is what drivers and codegen read as a boolean,
|
|
// and BOOLEAN itself is spelled that way.
|
|
case mysql.TypeTiny:
|
|
if flen == 1 {
|
|
typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: 1}}}
|
|
}
|
|
// A bit column's width is part of its type, and is 1 when left out.
|
|
case mysql.TypeBit:
|
|
if flen >= 0 {
|
|
typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(flen)}}}
|
|
}
|
|
// A fractional-seconds precision is part of the type; zero is the
|
|
// default and is not written.
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration:
|
|
if fsp := def.Tp.GetDecimal(); fsp > 0 {
|
|
typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(fsp)}}}
|
|
}
|
|
}
|
|
|
|
columnDef := ast.ColumnDef{
|
|
Colname: def.Name.String(),
|
|
TypeName: typeName,
|
|
IsNotNull: isNotNull(def),
|
|
IsUnsigned: isUnsigned(def),
|
|
Comment: comment,
|
|
Vals: vals,
|
|
}
|
|
if def.Tp.GetFlen() >= 0 {
|
|
length := def.Tp.GetFlen()
|
|
columnDef.Length = &length
|
|
}
|
|
|
|
return &columnDef
|
|
}
|
|
|
|
func (c *cc) convertColumnNameExpr(n *pcast.ColumnNameExpr) *ast.ColumnRef {
|
|
var items []ast.Node
|
|
if schema := n.Name.Schema.String(); schema != "" {
|
|
items = append(items, c.NewIdentifier(schema))
|
|
}
|
|
if table := n.Name.Table.String(); table != "" {
|
|
items = append(items, c.NewIdentifier(table))
|
|
}
|
|
items = append(items, c.NewIdentifier(n.Name.Name.String()))
|
|
return &ast.ColumnRef{
|
|
Fields: &ast.List{
|
|
Items: items,
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertColumnNames(cols []*pcast.ColumnName) *ast.List {
|
|
list := &ast.List{Items: []ast.Node{}}
|
|
for i := range cols {
|
|
name := c.identifier(cols[i].Name.String())
|
|
list.Items = append(list.Items, &ast.ResTarget{
|
|
Name: &name,
|
|
Location: cols[i].OriginTextPosition(),
|
|
})
|
|
}
|
|
return list
|
|
}
|
|
|
|
func (c *cc) convertDeleteStmt(n *pcast.DeleteStmt) *ast.DeleteStmt {
|
|
stmt := &ast.DeleteStmt{
|
|
WhereClause: c.convert(n.Where),
|
|
ReturningList: &ast.List{},
|
|
WithClause: c.convertWithClause(n.With),
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
stmt.LimitCount = c.convert(n.Limit.Count)
|
|
}
|
|
|
|
// Handle multi-table DELETE (DELETE t1, t2 FROM t1 JOIN t2 ...)
|
|
if n.IsMultiTable && n.Tables != nil && len(n.Tables.Tables) > 0 {
|
|
// Convert delete targets (e.g., jt.*, pt.*)
|
|
targets := &ast.List{}
|
|
for _, table := range n.Tables.Tables {
|
|
// Each table in the delete list is a ColumnRef like "jt.*" or "pt.*"
|
|
items := []ast.Node{}
|
|
if table.Schema.String() != "" {
|
|
items = append(items, c.NewIdentifier(table.Schema.String()))
|
|
}
|
|
items = append(items, c.NewIdentifier(table.Name.String()))
|
|
items = append(items, &ast.A_Star{})
|
|
targets.Items = append(targets.Items, &ast.ColumnRef{
|
|
Fields: &ast.List{Items: items},
|
|
})
|
|
}
|
|
stmt.Targets = targets
|
|
|
|
// Convert FROM clause preserving JOINs
|
|
if n.TableRefs != nil {
|
|
fromList := c.convertTableRefsClause(n.TableRefs)
|
|
if len(fromList.Items) == 1 {
|
|
stmt.FromClause = fromList.Items[0]
|
|
} else {
|
|
stmt.FromClause = fromList
|
|
}
|
|
}
|
|
} else {
|
|
// Single-table DELETE
|
|
rels := c.convertTableRefsClause(n.TableRefs)
|
|
if len(rels.Items) != 1 {
|
|
panic("expected one range var")
|
|
}
|
|
relations := &ast.List{}
|
|
convertToRangeVarList(rels, relations)
|
|
stmt.Relations = relations
|
|
}
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (c *cc) convertDropTableStmt(n *pcast.DropTableStmt) ast.Node {
|
|
drop := &ast.DropTableStmt{IfExists: n.IfExists}
|
|
for _, name := range n.Tables {
|
|
drop.Tables = append(drop.Tables, c.parseTableName(name))
|
|
}
|
|
return drop
|
|
}
|
|
|
|
func (c *cc) convertRenameTableStmt(n *pcast.RenameTableStmt) ast.Node {
|
|
list := &ast.List{Items: []ast.Node{}}
|
|
for _, table := range n.TableToTables {
|
|
list.Items = append(list.Items, &ast.RenameTableStmt{
|
|
Table: c.parseTableName(table.OldTable),
|
|
NewName: &c.parseTableName(table.NewTable).Name,
|
|
})
|
|
}
|
|
return list
|
|
}
|
|
|
|
func (c *cc) convertExistsSubqueryExpr(n *pcast.ExistsSubqueryExpr) *ast.SubLink {
|
|
sublink := &ast.SubLink{
|
|
SubLinkType: ast.EXISTS_SUBLINK,
|
|
}
|
|
if n.Sel != nil {
|
|
sublink.Subselect = c.convert(n.Sel)
|
|
}
|
|
return sublink
|
|
}
|
|
|
|
func (c *cc) convertFieldList(n *pcast.FieldList) *ast.List {
|
|
fields := make([]ast.Node, len(n.Fields))
|
|
for i := range n.Fields {
|
|
fields[i] = c.convertSelectField(n.Fields[i])
|
|
}
|
|
return &ast.List{Items: fields}
|
|
}
|
|
|
|
func (c *cc) convertFuncCallExpr(n *pcast.FuncCallExpr) ast.Node {
|
|
schema := n.Schema.String()
|
|
// Dispatch below compares against lowercase names; the emitted name
|
|
// goes through identifier so the format parser keeps the author's case.
|
|
name := strings.ToLower(n.FnName.String())
|
|
emitted := c.identifier(n.FnName.String())
|
|
|
|
// TODO: Deprecate the usage of Funcname
|
|
items := []ast.Node{}
|
|
if schema != "" {
|
|
items = append(items, c.NewIdentifier(schema))
|
|
}
|
|
items = append(items, c.NewIdentifier(emitted))
|
|
|
|
// Handle DATE_ADD/DATE_SUB specially to construct INTERVAL expressions
|
|
// These functions have args: [date, interval_value, TimeUnitExpr]
|
|
if (name == "date_add" || name == "date_sub") && len(n.Args) == 3 {
|
|
if timeUnit, ok := n.Args[2].(*pcast.TimeUnitExpr); ok {
|
|
args := &ast.List{
|
|
Items: []ast.Node{
|
|
c.convert(n.Args[0]),
|
|
&ast.IntervalExpr{
|
|
Value: c.convert(n.Args[1]),
|
|
Unit: timeUnit.Unit.String(),
|
|
},
|
|
},
|
|
}
|
|
return &ast.FuncCall{
|
|
Args: args,
|
|
Func: &ast.FuncName{
|
|
Schema: schema,
|
|
Name: emitted,
|
|
},
|
|
Funcname: &ast.List{
|
|
Items: items,
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
}
|
|
|
|
args := &ast.List{}
|
|
for _, arg := range n.Args {
|
|
args.Items = append(args.Items, c.convert(arg))
|
|
}
|
|
|
|
if schema == "" && name == "coalesce" {
|
|
return &ast.CoalesceExpr{
|
|
Args: args,
|
|
}
|
|
} else {
|
|
return &ast.FuncCall{
|
|
Args: args,
|
|
Func: &ast.FuncName{
|
|
Schema: schema,
|
|
Name: emitted,
|
|
},
|
|
Funcname: &ast.List{
|
|
Items: items,
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertInsertStmt(n *pcast.InsertStmt) *ast.InsertStmt {
|
|
rels := c.convertTableRefsClause(n.Table)
|
|
if len(rels.Items) != 1 {
|
|
panic("expected one range var")
|
|
}
|
|
rel := rels.Items[0]
|
|
rangeVar, ok := rel.(*ast.RangeVar)
|
|
if !ok {
|
|
panic("expected range var")
|
|
}
|
|
|
|
insert := &ast.InsertStmt{
|
|
Relation: rangeVar,
|
|
Cols: c.convertColumnNames(n.Columns),
|
|
ReturningList: &ast.List{},
|
|
}
|
|
if ss, ok := c.convert(n.Select).(*ast.SelectStmt); ok {
|
|
ss.ValuesLists = c.convertLists(n.Lists)
|
|
insert.SelectStmt = ss
|
|
} else {
|
|
insert.SelectStmt = &ast.SelectStmt{
|
|
FromClause: &ast.List{},
|
|
TargetList: &ast.List{},
|
|
ValuesLists: c.convertLists(n.Lists),
|
|
}
|
|
}
|
|
|
|
if n.OnDuplicate != nil {
|
|
targetList := &ast.List{}
|
|
for _, a := range n.OnDuplicate {
|
|
targetList.Items = append(targetList.Items, c.convertAssignment(a))
|
|
}
|
|
insert.OnDuplicateKeyUpdate = &ast.OnDuplicateKeyUpdate{
|
|
TargetList: targetList,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
return insert
|
|
}
|
|
|
|
func (c *cc) convertLists(lists [][]pcast.ExprNode) *ast.List {
|
|
list := &ast.List{Items: []ast.Node{}}
|
|
for _, exprs := range lists {
|
|
inner := &ast.List{Items: []ast.Node{}}
|
|
for _, expr := range exprs {
|
|
inner.Items = append(inner.Items, c.convert(expr))
|
|
}
|
|
list.Items = append(list.Items, inner)
|
|
}
|
|
return list
|
|
}
|
|
|
|
func (c *cc) convertParamMarkerExpr(n *pcast.ParamMarkerExprBase) *ast.ParamRef {
|
|
// Parameter numbers start at one
|
|
c.paramCount += 1
|
|
return &ast.ParamRef{
|
|
Number: c.paramCount,
|
|
Location: n.Offset,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertSelectField(n *pcast.SelectField) *ast.ResTarget {
|
|
var val ast.Node
|
|
if n.WildCard != nil {
|
|
val = c.convertWildCardField(n.WildCard)
|
|
} else {
|
|
val = c.convert(n.Expr)
|
|
}
|
|
var name *string
|
|
if n.AsName.O != "" {
|
|
asname := c.identifier(n.AsName.O)
|
|
name = &asname
|
|
}
|
|
return &ast.ResTarget{
|
|
// TODO: Populate Indirection field
|
|
Name: name,
|
|
Val: val,
|
|
Location: n.Offset,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertSelectStmt(n *pcast.SelectStmt) *ast.SelectStmt {
|
|
// The ORDER BY expressions are recorded twice: bare in WindowClause,
|
|
// where the compiler's strict_order_by validation expects them, and
|
|
// wrapped in SortBy nodes in SortClause, which carries the sort
|
|
// direction and is what formatting prints. Both share the same
|
|
// converted nodes so parameter numbering stays intact.
|
|
windowClause := &ast.List{Items: make([]ast.Node, 0)}
|
|
var sortClause *ast.List
|
|
if n.OrderBy != nil {
|
|
sortClause = &ast.List{Items: make([]ast.Node, 0)}
|
|
orderByClause := &ast.List{Items: []ast.Node{}}
|
|
for _, item := range n.OrderBy.Items {
|
|
expr := c.convert(item.Expr)
|
|
orderByClause.Items = append(orderByClause.Items, expr)
|
|
dir := ast.SortByDirDefault
|
|
if item.Desc {
|
|
dir = ast.SortByDirDesc
|
|
}
|
|
sortClause.Items = append(sortClause.Items, &ast.SortBy{
|
|
Node: expr,
|
|
SortbyDir: dir,
|
|
})
|
|
}
|
|
windowClause.Items = append(windowClause.Items, orderByClause)
|
|
}
|
|
|
|
op, all := c.convertSetOprType(n.AfterSetOperator)
|
|
stmt := &ast.SelectStmt{
|
|
TargetList: c.convertFieldList(n.Fields),
|
|
FromClause: c.convertTableRefsClause(n.From),
|
|
GroupClause: c.convertGroupByClause(n.GroupBy),
|
|
HavingClause: c.convertHavingClause(n.Having),
|
|
WhereClause: c.convert(n.Where),
|
|
WithClause: c.convertWithClause(n.With),
|
|
WindowClause: windowClause,
|
|
SortClause: sortClause,
|
|
Op: op,
|
|
All: all,
|
|
}
|
|
if n.Distinct {
|
|
// A plain DISTINCT, pg-style: a non-empty clause whose single TODO
|
|
// item means "no ON (...) expressions".
|
|
stmt.DistinctClause = &ast.List{Items: []ast.Node{&ast.TODO{}}}
|
|
}
|
|
stmt.TableHints = restoreTableHints(n.TableHints)
|
|
if n.Limit != nil {
|
|
stmt.LimitCount = c.convert(n.Limit.Count)
|
|
stmt.LimitOffset = c.convert(n.Limit.Offset)
|
|
}
|
|
return stmt
|
|
}
|
|
|
|
// restoreTableHints renders a statement's optimizer hints back to the text
|
|
// inside their /*+ ... */ comment. The compiler has no use for hints, but
|
|
// dropping them from a query would change how the server runs it, so the
|
|
// statement carries them through to printing as text.
|
|
func restoreTableHints(hints []*pcast.TableOptimizerHint) string {
|
|
if len(hints) == 0 {
|
|
return ""
|
|
}
|
|
var sb strings.Builder
|
|
ctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)
|
|
for i, h := range hints {
|
|
if i != 0 {
|
|
sb.WriteString(" ")
|
|
}
|
|
if err := h.Restore(ctx); err != nil {
|
|
return ""
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func (c *cc) convertSubqueryExpr(n *pcast.SubqueryExpr) ast.Node {
|
|
// Wrap subquery in SubLink to ensure parentheses are added
|
|
return &ast.SubLink{
|
|
SubLinkType: ast.EXPR_SUBLINK,
|
|
Subselect: c.convert(n.Query),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertTableRefsClause(n *pcast.TableRefsClause) *ast.List {
|
|
if n == nil {
|
|
return &ast.List{}
|
|
}
|
|
return c.convertJoin(n.TableRefs)
|
|
}
|
|
|
|
func (c *cc) convertCommonTableExpression(n *pcast.CommonTableExpression) *ast.CommonTableExpr {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
|
|
name := n.Name.String()
|
|
|
|
columns := &ast.List{}
|
|
for _, col := range n.ColNameList {
|
|
columns.Items = append(columns.Items, c.NewIdentifier(col.String()))
|
|
}
|
|
|
|
// CTE Query is wrapped in SubqueryExpr by TiDB parser.
|
|
// We need to unwrap it to get the SelectStmt directly,
|
|
// otherwise it would be double-wrapped with parentheses.
|
|
var cteQuery ast.Node
|
|
if n.Query != nil {
|
|
cteQuery = c.convert(n.Query.Query)
|
|
}
|
|
|
|
return &ast.CommonTableExpr{
|
|
Ctename: &name,
|
|
Ctequery: cteQuery,
|
|
Ctecolnames: columns,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertWithClause(n *pcast.WithClause) *ast.WithClause {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
list := &ast.List{}
|
|
for _, n := range n.CTEs {
|
|
list.Items = append(list.Items, c.convertCommonTableExpression(n))
|
|
}
|
|
|
|
return &ast.WithClause{
|
|
Ctes: list,
|
|
Recursive: n.IsRecursive,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertUpdateStmt(n *pcast.UpdateStmt) *ast.UpdateStmt {
|
|
rels := c.convertTableRefsClause(n.TableRefs)
|
|
if len(rels.Items) != 1 {
|
|
panic("expected one range var")
|
|
}
|
|
|
|
relations := &ast.List{}
|
|
convertToRangeVarList(rels, relations)
|
|
|
|
// TargetList
|
|
list := &ast.List{}
|
|
for _, a := range n.List {
|
|
list.Items = append(list.Items, c.convertAssignment(a))
|
|
}
|
|
stmt := &ast.UpdateStmt{
|
|
Relations: relations,
|
|
TargetList: list,
|
|
WhereClause: c.convert(n.Where),
|
|
FromClause: &ast.List{},
|
|
ReturningList: &ast.List{},
|
|
WithClause: c.convertWithClause(n.With),
|
|
// The table-reference tree as written. Relations above flattens a
|
|
// multi-table UPDATE's join to bare tables for analysis, which
|
|
// would print `UPDATE a JOIN b ON ...` without its ON condition —
|
|
// an update of the whole cross product.
|
|
TableRefs: rels,
|
|
}
|
|
if n.Limit != nil {
|
|
stmt.LimitCount = c.convert(n.Limit.Count)
|
|
}
|
|
return stmt
|
|
}
|
|
|
|
func (c *cc) convertValueExpr(n *pcast.ValueExprBase) *ast.A_Const {
|
|
// The datum's kind, not the field type, says what the literal is: a
|
|
// NULL arrives with a string field type, a decimal's value lives in
|
|
// its decimal — not float — slot, and TRUE is an int64 whose field
|
|
// type carries the boolean flag.
|
|
switch n.Datum.Kind() {
|
|
case pcast.KindNull:
|
|
return &ast.A_Const{
|
|
Val: &ast.Null{},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
case pcast.KindInt64:
|
|
if mysql.HasIsBooleanFlag(n.Type.GetFlag()) {
|
|
return &ast.A_Const{
|
|
Val: &ast.Boolean{Boolval: n.Datum.GetInt64() > 0},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
return &ast.A_Const{
|
|
Val: &ast.Integer{
|
|
Ival: n.Datum.GetInt64(),
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
case pcast.KindUint64:
|
|
return &ast.A_Const{
|
|
Val: &ast.Integer{
|
|
Ival: int64(n.Datum.GetUint64()),
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
case pcast.KindFloat32, pcast.KindFloat64:
|
|
return &ast.A_Const{
|
|
Val: &ast.Float{
|
|
Str: strconv.FormatFloat(n.Datum.GetFloat64(), 'f', -1, 64),
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
case pcast.KindMysqlDecimal:
|
|
// The decimal's own rendering keeps the written scale: 1.0 stays
|
|
// 1.0.
|
|
return &ast.A_Const{
|
|
Val: &ast.Float{
|
|
Str: n.Datum.GetMysqlDecimal().String(),
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
return &ast.A_Const{
|
|
Val: &ast.String{
|
|
Str: n.Datum.GetString(),
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertWildCardField(n *pcast.WildCardField) *ast.ColumnRef {
|
|
items := []ast.Node{}
|
|
if t := n.Table.String(); t != "" {
|
|
items = append(items, c.NewIdentifier(t))
|
|
}
|
|
items = append(items, &ast.A_Star{})
|
|
|
|
return &ast.ColumnRef{
|
|
Fields: &ast.List{
|
|
Items: items,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertAdminStmt(n *pcast.AdminStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertAggregateFuncExpr(n *pcast.AggregateFuncExpr) *ast.FuncCall {
|
|
// Comparisons below use the lowercase name; the emitted name keeps the
|
|
// author's case under the format parser.
|
|
name := strings.ToLower(n.F)
|
|
emitted := c.identifier(n.F)
|
|
fn := &ast.FuncCall{
|
|
Func: &ast.FuncName{
|
|
Name: emitted,
|
|
},
|
|
Funcname: &ast.List{
|
|
Items: []ast.Node{
|
|
c.NewIdentifier(emitted),
|
|
},
|
|
},
|
|
Args: &ast.List{},
|
|
AggOrder: &ast.List{},
|
|
}
|
|
|
|
// GROUP_CONCAT has special handling:
|
|
// TiDB always adds the separator as the last argument
|
|
// We need to extract it and use SEPARATOR syntax
|
|
args := n.Args
|
|
var separator string
|
|
if name == "group_concat" && len(args) >= 2 {
|
|
// The last arg is always the separator
|
|
if value, ok := args[len(args)-1].(*pcast.ValueExprBase); ok {
|
|
separator = value.GetString()
|
|
args = args[:len(args)-1]
|
|
}
|
|
}
|
|
|
|
for _, a := range args {
|
|
if value, ok := a.(*pcast.ValueExprBase); ok {
|
|
if value.GetInt64() == int64(1) {
|
|
fn.AggStar = true
|
|
continue
|
|
}
|
|
}
|
|
fn.Args.Items = append(fn.Args.Items, c.convert(a))
|
|
}
|
|
if n.Distinct {
|
|
fn.AggDistinct = true
|
|
}
|
|
// GROUP_CONCAT(x ORDER BY y): the ordering decides the string that
|
|
// comes back, so it must survive into the printed call.
|
|
if n.Order != nil {
|
|
fn.AggOrder = c.convertOrderByItems(n.Order.Items)
|
|
}
|
|
|
|
// Store separator for GROUP_CONCAT (only if non-default)
|
|
if name == "group_concat" && separator != "" && separator != "," {
|
|
fn.Separator = &separator
|
|
}
|
|
|
|
return fn
|
|
}
|
|
|
|
func (c *cc) convertAlterDatabaseStmt(n *pcast.AlterDatabaseStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertAlterInstanceStmt(n *pcast.AlterInstanceStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertAlterTableSpec(n *pcast.AlterTableSpec) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertAlterUserStmt(n *pcast.AlterUserStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertAnalyzeTableStmt(n *pcast.AnalyzeTableStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertBRIEStmt(n *pcast.BRIEStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertBeginStmt(n *pcast.BeginStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertBetweenExpr(n *pcast.BetweenExpr) ast.Node {
|
|
return &ast.BetweenExpr{
|
|
Expr: c.convert(n.Expr),
|
|
Left: c.convert(n.Left),
|
|
Right: c.convert(n.Right),
|
|
Location: n.OriginTextPosition(),
|
|
Not: n.Not,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertBinlogStmt(n *pcast.BinlogStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertByItem(n *pcast.ByItem) ast.Node {
|
|
switch n.Expr.(type) {
|
|
case *pcast.PositionExpr:
|
|
return c.convertPositionExpr(n.Expr.(*pcast.PositionExpr))
|
|
case *pcast.ColumnNameExpr:
|
|
return c.convertColumnNameExpr(n.Expr.(*pcast.ColumnNameExpr))
|
|
default:
|
|
return todo(n)
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertCaseExpr(n *pcast.CaseExpr) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
list := &ast.List{Items: []ast.Node{}}
|
|
for _, n := range n.WhenClauses {
|
|
list.Items = append(list.Items, c.convertWhenClause(n))
|
|
}
|
|
return &ast.CaseExpr{
|
|
Arg: c.convert(n.Value),
|
|
Args: list,
|
|
Defresult: c.convert(n.ElseClause),
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertCleanupTableLockStmt(n *pcast.CleanupTableLockStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertColumnDef(n *pcast.ColumnDef) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertColumnName(n *pcast.ColumnName) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertColumnPosition(n *pcast.ColumnPosition) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCommitStmt(n *pcast.CommitStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCompareSubqueryExpr(n *pcast.CompareSubqueryExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertConstraint(n *pcast.Constraint) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateBindingStmt(n *pcast.CreateBindingStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateDatabaseStmt(n *pcast.CreateDatabaseStmt) ast.Node {
|
|
return &ast.CreateSchemaStmt{
|
|
Name: &n.Name.O,
|
|
IfNotExists: n.IfNotExists,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertCreateIndexStmt(n *pcast.CreateIndexStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateSequenceStmt(n *pcast.CreateSequenceStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateStatisticsStmt(n *pcast.CreateStatisticsStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateUserStmt(n *pcast.CreateUserStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCreateViewStmt(n *pcast.CreateViewStmt) ast.Node {
|
|
return &ast.ViewStmt{
|
|
View: c.convertTableName(n.ViewName),
|
|
Aliases: &ast.List{},
|
|
Query: c.convert(n.Select),
|
|
Replace: n.OrReplace,
|
|
Options: &ast.List{},
|
|
WithCheckOption: ast.ViewCheckOption(n.CheckOption),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertDeallocateStmt(n *pcast.DeallocateStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDefaultExpr(n *pcast.DefaultExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDeleteTableList(n *pcast.DeleteTableList) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDoStmt(n *pcast.DoStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropBindingStmt(n *pcast.DropBindingStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropDatabaseStmt(n *pcast.DropDatabaseStmt) ast.Node {
|
|
return &ast.DropSchemaStmt{
|
|
MissingOk: !n.IfExists,
|
|
Schemas: []*ast.String{
|
|
c.NewIdentifier(n.Name.O),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertDropIndexStmt(n *pcast.DropIndexStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropSequenceStmt(n *pcast.DropSequenceStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropStatisticsStmt(n *pcast.DropStatisticsStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropStatsStmt(n *pcast.DropStatsStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertDropUserStmt(n *pcast.DropUserStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertExecuteStmt(n *pcast.ExecuteStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertExplainForStmt(n *pcast.ExplainForStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertExplainStmt(n *pcast.ExplainStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertFlashBackTableStmt(n *pcast.FlashBackTableStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertFlushStmt(n *pcast.FlushStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertFrameBound(n *pcast.FrameBound) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertFrameClause(n *pcast.FrameClause) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node {
|
|
tp := n.Tp.GetType()
|
|
typeName := types.TypeToStr(tp, n.Tp.GetCharset())
|
|
|
|
// MySQL CAST AS UNSIGNED/SIGNED uses bigint internally.
|
|
// We need to preserve the signed/unsigned info for formatting.
|
|
if typeName == "bigint" {
|
|
if mysql.HasUnsignedFlag(n.Tp.GetFlag()) {
|
|
typeName = "bigint unsigned"
|
|
} else {
|
|
typeName = "bigint signed"
|
|
}
|
|
}
|
|
// CAST(x AS CHAR) and CAST(x AS BINARY) are typed by the parser as the
|
|
// wire's var_string, which is no SQL type, or as a char or binary.
|
|
// MySQL types the result as a varchar or a varbinary — its metadata
|
|
// and a view over it both say so.
|
|
switch typeName {
|
|
case "var_string", "char":
|
|
typeName = "varchar"
|
|
if n.Tp.GetCharset() == "binary" {
|
|
typeName = "varbinary"
|
|
}
|
|
case "binary":
|
|
typeName = "varbinary"
|
|
}
|
|
|
|
out := &ast.TypeName{Name: typeName}
|
|
flen, dec := n.Tp.GetFlen(), n.Tp.GetDecimal()
|
|
switch tp {
|
|
case mysql.TypeNewDecimal:
|
|
// A decimal's precision and scale are part of its type, and a
|
|
// scale left out is 0: CAST(x AS DECIMAL(5)) is a decimal(5,0).
|
|
if flen >= 0 && flen != types.UnspecifiedLength {
|
|
mods := []ast.Node{&ast.Integer{Ival: int64(flen)}}
|
|
if dec >= 0 && dec != types.UnspecifiedLength {
|
|
mods = append(mods, &ast.Integer{Ival: int64(dec)})
|
|
}
|
|
out.Typmods = &ast.List{Items: mods}
|
|
}
|
|
case mysql.TypeFloat, mysql.TypeDouble:
|
|
// The parser fills in a display width for a float or double, and
|
|
// a precision written as FLOAT(p) only picks between the two; the
|
|
// result is a plain float or double.
|
|
case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString:
|
|
if flen > 0 && flen != types.UnspecifiedLength {
|
|
out.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(flen)}}}
|
|
}
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration:
|
|
if dec > 0 && dec != types.UnspecifiedLength {
|
|
out.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(dec)}}}
|
|
}
|
|
}
|
|
|
|
return &ast.TypeCast{
|
|
Arg: c.convert(n.Expr),
|
|
TypeName: out,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertGetFormatSelectorExpr(n *pcast.GetFormatSelectorExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertGrantRoleStmt(n *pcast.GrantRoleStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertGrantStmt(n *pcast.GrantStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertGroupByClause(n *pcast.GroupByClause) *ast.List {
|
|
if n == nil {
|
|
return &ast.List{}
|
|
}
|
|
|
|
var items []ast.Node
|
|
for _, item := range n.Items {
|
|
items = append(items, c.convertByItem(item))
|
|
}
|
|
|
|
return &ast.List{
|
|
Items: items,
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertHavingClause(n *pcast.HavingClause) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
return c.convert(n.Expr)
|
|
}
|
|
|
|
func (c *cc) convertIndexLockAndAlgorithm(n *pcast.IndexLockAndAlgorithm) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertIndexPartSpecification(n *pcast.IndexPartSpecification) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertIsNullExpr(n *pcast.IsNullExpr) ast.Node {
|
|
op := ast.BoolExprTypeIsNull
|
|
if n.Not {
|
|
op = ast.BoolExprTypeIsNotNull
|
|
}
|
|
return &ast.BoolExpr{
|
|
Boolop: op,
|
|
Args: &ast.List{
|
|
Items: []ast.Node{
|
|
c.convert(n.Expr),
|
|
},
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertIsTruthExpr(n *pcast.IsTruthExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertJoin(n *pcast.Join) *ast.List {
|
|
if n == nil {
|
|
return &ast.List{}
|
|
}
|
|
if n.Right != nil && n.Left != nil {
|
|
// MySQL doesn't have a FULL join type
|
|
joinType := ast.JoinType(n.Tp)
|
|
if joinType >= ast.JoinTypeFull {
|
|
joinType++
|
|
}
|
|
|
|
// Convert USING clause
|
|
var usingClause *ast.List
|
|
if len(n.Using) > 0 {
|
|
items := make([]ast.Node, len(n.Using))
|
|
for i, col := range n.Using {
|
|
items[i] = &ast.String{Str: col.Name.O}
|
|
}
|
|
usingClause = &ast.List{Items: items}
|
|
}
|
|
|
|
return &ast.List{
|
|
Items: []ast.Node{&ast.JoinExpr{
|
|
Jointype: joinType,
|
|
IsNatural: n.NaturalJoin,
|
|
Larg: c.convert(n.Left),
|
|
Rarg: c.convert(n.Right),
|
|
UsingClause: usingClause,
|
|
Quals: c.convert(n.On),
|
|
}},
|
|
}
|
|
}
|
|
var tables []ast.Node
|
|
if n.Right != nil {
|
|
tables = append(tables, c.convert(n.Right))
|
|
}
|
|
if n.Left != nil {
|
|
tables = append(tables, c.convert(n.Left))
|
|
}
|
|
return &ast.List{Items: tables}
|
|
}
|
|
|
|
func (c *cc) convertKillStmt(n *pcast.KillStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertLimit(n *pcast.Limit) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertLoadDataStmt(n *pcast.LoadDataStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertLoadStatsStmt(n *pcast.LoadStatsStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertLockTablesStmt(n *pcast.LockTablesStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertMatchAgainst(n *pcast.MatchAgainst) ast.Node {
|
|
searchTerm := c.convert(n.Against)
|
|
|
|
stringSearchTerm := &ast.TypeCast{
|
|
Arg: searchTerm,
|
|
TypeName: &ast.TypeName{
|
|
Name: "text", // Use 'text' type which maps to string in Go
|
|
},
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
matchOperation := &ast.A_Const{
|
|
Val: &ast.String{Str: "MATCH_AGAINST"},
|
|
}
|
|
|
|
return &ast.A_Expr{
|
|
Name: &ast.List{
|
|
Items: []ast.Node{
|
|
&ast.String{Str: "AGAINST"},
|
|
},
|
|
},
|
|
Lexpr: matchOperation,
|
|
Rexpr: stringSearchTerm,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertMaxValueExpr(n *pcast.MaxValueExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertOnCondition(n *pcast.OnCondition) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
return c.convert(n.Expr)
|
|
}
|
|
|
|
func (c *cc) convertOnDeleteOpt(n *pcast.OnDeleteOpt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertOnUpdateOpt(n *pcast.OnUpdateOpt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertOrderByClause(n *pcast.OrderByClause) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
list := &ast.List{Items: []ast.Node{}}
|
|
for _, item := range n.Items {
|
|
list.Items = append(list.Items, c.convert(item.Expr))
|
|
}
|
|
return list
|
|
}
|
|
|
|
func (c *cc) convertParenthesesExpr(n *pcast.ParenthesesExpr) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
inner := c.convert(n.Expr)
|
|
// Only wrap in ParenExpr for SELECT statements (needed for UNION with parenthesized subqueries)
|
|
// For other expressions, the BoolExpr already adds parentheses
|
|
if _, ok := inner.(*ast.SelectStmt); ok {
|
|
return &ast.ParenExpr{
|
|
Expr: inner,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
return inner
|
|
}
|
|
|
|
func (c *cc) convertPartitionByClause(n *pcast.PartitionByClause) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertPatternInExpr(n *pcast.PatternInExpr) ast.Node {
|
|
var list []ast.Node
|
|
var val ast.Node
|
|
|
|
expr := c.convert(n.Expr)
|
|
|
|
for _, v := range n.List {
|
|
val = c.convert(v)
|
|
if val != nil {
|
|
list = append(list, val)
|
|
}
|
|
}
|
|
|
|
sel := c.convert(n.Sel)
|
|
|
|
in := &ast.In{
|
|
Expr: expr,
|
|
List: list,
|
|
Not: n.Not,
|
|
Sel: sel,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
|
|
return in
|
|
}
|
|
|
|
func (c *cc) convertPatternLikeExpr(n *pcast.PatternLikeOrIlikeExpr) ast.Node {
|
|
kind := ast.A_Expr_Kind_ILIKE
|
|
op := "~~*"
|
|
if n.IsLike {
|
|
kind = ast.A_Expr_Kind_LIKE
|
|
op = "~~"
|
|
}
|
|
if n.Not {
|
|
op = "!" + op
|
|
}
|
|
return &ast.A_Expr{
|
|
Kind: kind,
|
|
Name: &ast.List{
|
|
Items: []ast.Node{
|
|
&ast.String{Str: op},
|
|
},
|
|
},
|
|
Lexpr: c.convert(n.Expr),
|
|
Rexpr: c.convert(n.Pattern),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertPatternRegexpExpr(n *pcast.PatternRegexpExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertPositionExpr(n *pcast.PositionExpr) ast.Node {
|
|
return &ast.Integer{Ival: int64(n.N)}
|
|
}
|
|
|
|
func (c *cc) convertPrepareStmt(n *pcast.PrepareStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertPrivElem(n *pcast.PrivElem) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRecoverTableStmt(n *pcast.RecoverTableStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertReferenceDef(n *pcast.ReferenceDef) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRepairTableStmt(n *pcast.RepairTableStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRevokeRoleStmt(n *pcast.RevokeRoleStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRevokeStmt(n *pcast.RevokeStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRollbackStmt(n *pcast.RollbackStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertRowExpr(n *pcast.RowExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetCollationExpr(n *pcast.SetCollationExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetConfigStmt(n *pcast.SetConfigStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetDefaultRoleStmt(n *pcast.SetDefaultRoleStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetOprType(n *pcast.SetOprType) (op ast.SetOperation, all bool) {
|
|
if n == nil {
|
|
return
|
|
}
|
|
|
|
switch *n {
|
|
case pcast.Union:
|
|
op = ast.Union
|
|
case pcast.UnionAll:
|
|
op = ast.Union
|
|
all = true
|
|
case pcast.Intersect:
|
|
op = ast.Intersect
|
|
case pcast.IntersectAll:
|
|
op = ast.Intersect
|
|
all = true
|
|
case pcast.Except:
|
|
op = ast.Except
|
|
case pcast.ExceptAll:
|
|
op = ast.Except
|
|
all = true
|
|
}
|
|
return
|
|
}
|
|
|
|
// convertSetOprSelectList converts a list of SELECT from the Pingcap parser
|
|
// into a tree. It is called for UNION, INTERSECT or EXCLUDE operation.
|
|
//
|
|
// Given an union with the following nodes:
|
|
//
|
|
// [Select{1}, Select{2}, Select{3}, Select{4}]
|
|
//
|
|
// The function will return:
|
|
//
|
|
// Select{
|
|
// Larg: Select{
|
|
// Larg: Select{
|
|
// Larg: Select{1},
|
|
// Rarg: Select{2},
|
|
// Op: Union
|
|
// },
|
|
// Rarg: Select{3},
|
|
// Op: Union,
|
|
// },
|
|
// Rarg: Select{4},
|
|
// Op: Union,
|
|
// }
|
|
func (c *cc) convertSetOprSelectList(n *pcast.SetOprSelectList) ast.Node {
|
|
selectStmts := make([]*ast.SelectStmt, len(n.Selects))
|
|
for i, node := range n.Selects {
|
|
switch node := node.(type) {
|
|
case *pcast.SelectStmt:
|
|
selectStmts[i] = c.convertSelectStmt(node)
|
|
case *pcast.SetOprSelectList:
|
|
// If this is a single-select SetOprSelectList (e.g., from parenthesized SELECT),
|
|
// extract the inner select instead of building a UNION tree
|
|
if len(node.Selects) == 1 {
|
|
if innerSelect, ok := node.Selects[0].(*pcast.SelectStmt); ok {
|
|
selectStmts[i] = c.convertSelectStmt(innerSelect)
|
|
} else {
|
|
selectStmts[i] = c.convertSetOprSelectList(node).(*ast.SelectStmt)
|
|
}
|
|
} else {
|
|
selectStmts[i] = c.convertSetOprSelectList(node).(*ast.SelectStmt)
|
|
}
|
|
default:
|
|
// Handle other node types like ParenthesesExpr wrapping a SELECT
|
|
converted := c.convert(node)
|
|
if ss, ok := converted.(*ast.SelectStmt); ok {
|
|
selectStmts[i] = ss
|
|
} else if pe, ok := converted.(*ast.ParenExpr); ok {
|
|
// Unwrap ParenExpr to get the inner SelectStmt
|
|
if inner, ok := pe.Expr.(*ast.SelectStmt); ok {
|
|
selectStmts[i] = inner
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
op, all := c.convertSetOprType(n.AfterSetOperator)
|
|
tree := &ast.SelectStmt{
|
|
TargetList: &ast.List{},
|
|
FromClause: &ast.List{},
|
|
WhereClause: nil,
|
|
Op: op,
|
|
All: all,
|
|
WithClause: c.convertWithClause(n.With),
|
|
}
|
|
for _, stmt := range selectStmts {
|
|
// We move Op and All from the child to the parent.
|
|
op, all := stmt.Op, stmt.All
|
|
stmt.Op, stmt.All = ast.None, false
|
|
|
|
switch {
|
|
case tree.Larg == nil:
|
|
tree.Larg = stmt
|
|
case tree.Rarg == nil:
|
|
tree.Rarg = stmt
|
|
tree.Op = op
|
|
tree.All = all
|
|
default:
|
|
tree = &ast.SelectStmt{
|
|
TargetList: &ast.List{},
|
|
FromClause: &ast.List{},
|
|
WhereClause: nil,
|
|
Larg: tree,
|
|
Rarg: stmt,
|
|
Op: op,
|
|
All: all,
|
|
WithClause: c.convertWithClause(n.With),
|
|
}
|
|
}
|
|
}
|
|
|
|
return tree
|
|
}
|
|
|
|
func (c *cc) convertSetOprStmt(n *pcast.SetOprStmt) ast.Node {
|
|
if n.SelectList != nil {
|
|
sn := c.convertSetOprSelectList(n.SelectList)
|
|
if ss, ok := sn.(*ast.SelectStmt); ok {
|
|
if n.Limit != nil {
|
|
ss.LimitOffset = c.convert(n.Limit.Offset)
|
|
ss.LimitCount = c.convert(n.Limit.Count)
|
|
}
|
|
// The ORDER BY that applies to the whole compound statement
|
|
// (SELECT ... UNION SELECT ... ORDER BY x) lives on the set
|
|
// operation, not on either branch.
|
|
if n.OrderBy != nil {
|
|
ss.SortClause = c.convertOrderByItems(n.OrderBy.Items)
|
|
}
|
|
}
|
|
return sn
|
|
}
|
|
return todo(n)
|
|
}
|
|
|
|
// convertOrderByItems converts ORDER BY items into the SortBy list a
|
|
// SelectStmt's SortClause carries.
|
|
func (c *cc) convertOrderByItems(items []*pcast.ByItem) *ast.List {
|
|
list := &ast.List{Items: make([]ast.Node, 0, len(items))}
|
|
for _, item := range items {
|
|
dir := ast.SortByDirDefault
|
|
if item.Desc {
|
|
dir = ast.SortByDirDesc
|
|
}
|
|
list.Items = append(list.Items, &ast.SortBy{
|
|
Node: c.convert(item.Expr),
|
|
SortbyDir: dir,
|
|
})
|
|
}
|
|
return list
|
|
}
|
|
|
|
func (c *cc) convertSetPwdStmt(n *pcast.SetPwdStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetRoleStmt(n *pcast.SetRoleStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSetStmt(n *pcast.SetStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertShowStmt(n *pcast.ShowStmt) ast.Node {
|
|
if n.Tp != pcast.ShowWarnings {
|
|
return todo(n)
|
|
}
|
|
level := "level"
|
|
code := "code"
|
|
message := "message"
|
|
stmt := &ast.SelectStmt{
|
|
FromClause: &ast.List{},
|
|
TargetList: &ast.List{
|
|
Items: []ast.Node{
|
|
&ast.ResTarget{
|
|
Name: &level,
|
|
Val: &ast.A_Const{Val: &ast.String{}},
|
|
},
|
|
&ast.ResTarget{
|
|
Name: &code,
|
|
Val: &ast.A_Const{Val: &ast.Integer{}},
|
|
},
|
|
&ast.ResTarget{
|
|
Name: &message,
|
|
Val: &ast.A_Const{Val: &ast.String{}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
return stmt
|
|
}
|
|
|
|
func (c *cc) convertShutdownStmt(n *pcast.ShutdownStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertSplitRegionStmt(n *pcast.SplitRegionStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTableName(n *pcast.TableName) *ast.RangeVar {
|
|
schema := c.identifier(n.Schema.String())
|
|
rel := c.identifier(n.Name.String())
|
|
return &ast.RangeVar{
|
|
Schemaname: &schema,
|
|
Relname: &rel,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertTableNameExpr(n *pcast.TableNameExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTableOptimizerHint(n *pcast.TableOptimizerHint) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTableSource(node *pcast.TableSource) ast.Node {
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
alias := node.AsName.String()
|
|
switch n := node.Source.(type) {
|
|
|
|
case *pcast.SelectStmt, *pcast.SetOprStmt:
|
|
rs := &ast.RangeSubselect{
|
|
Subquery: c.convert(n),
|
|
}
|
|
if alias != "" {
|
|
rs.Alias = &ast.Alias{Aliasname: &alias}
|
|
}
|
|
return rs
|
|
|
|
case *pcast.TableName:
|
|
rv := c.convertTableName(n)
|
|
if alias != "" {
|
|
rv.Alias = &ast.Alias{Aliasname: &alias}
|
|
}
|
|
return rv
|
|
|
|
default:
|
|
return todo(n)
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertTableToTable(n *pcast.TableToTable) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTimeUnitExpr(n *pcast.TimeUnitExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTraceStmt(n *pcast.TraceStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTrimDirectionExpr(n *pcast.TrimDirectionExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertTruncateTableStmt(n *pcast.TruncateTableStmt) *ast.TruncateStmt {
|
|
return &ast.TruncateStmt{
|
|
Relations: c.toList(n.Table),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertUnaryOperationExpr(n *pcast.UnaryOperationExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertUnlockTablesStmt(n *pcast.UnlockTablesStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertUseStmt(n *pcast.UseStmt) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertValuesExpr(n *pcast.ValuesExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertVariableAssignment(n *pcast.VariableAssignment) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertVariableExpr(n *pcast.VariableExpr) ast.Node {
|
|
// MySQL @variable references are user-defined variables, NOT sqlc named parameters.
|
|
// Use VariableExpr to preserve them as-is in the output.
|
|
return &ast.VariableExpr{
|
|
Name: n.Name,
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertWhenClause(n *pcast.WhenClause) ast.Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
return &ast.CaseWhen{
|
|
Expr: c.convert(n.Expr),
|
|
Result: c.convert(n.Result),
|
|
Location: n.OriginTextPosition(),
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertWindowFuncExpr(n *pcast.WindowFuncExpr) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertWindowSpec(n *pcast.WindowSpec) ast.Node {
|
|
return todo(n)
|
|
}
|
|
|
|
func (c *cc) convertCallStmt(n *pcast.CallStmt) ast.Node {
|
|
var funcname ast.List
|
|
for _, s := range []string{n.Procedure.Schema.L, n.Procedure.FnName.L} {
|
|
if s != "" {
|
|
funcname.Items = append(funcname.Items, c.NewIdentifier(s))
|
|
}
|
|
}
|
|
var args ast.List
|
|
for _, a := range n.Procedure.Args {
|
|
args.Items = append(args.Items, c.convert(a))
|
|
}
|
|
return &ast.CallStmt{
|
|
FuncCall: &ast.FuncCall{
|
|
Func: &ast.FuncName{
|
|
Schema: n.Procedure.Schema.L,
|
|
Name: n.Procedure.FnName.L,
|
|
},
|
|
Funcname: &funcname,
|
|
Args: &args,
|
|
Location: n.OriginTextPosition(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *cc) convertProcedureInfo(n *pcast.ProcedureInfo) ast.Node {
|
|
var params ast.List
|
|
for _, sp := range n.ProcedureParam {
|
|
paramName := sp.ParamName
|
|
params.Items = append(params.Items, &ast.FuncParam{
|
|
Name: ¶mName,
|
|
Type: &ast.TypeName{Name: types.TypeToStr(sp.ParamType.GetType(), sp.ParamType.GetCharset())},
|
|
})
|
|
}
|
|
return &ast.CreateFunctionStmt{
|
|
Params: ¶ms,
|
|
Func: &ast.FuncName{
|
|
Schema: n.ProcedureName.Schema.L,
|
|
Name: n.ProcedureName.Name.L,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *cc) convert(node pcast.Node) ast.Node {
|
|
switch n := node.(type) {
|
|
|
|
case *pcast.ParamMarkerExprBase:
|
|
return c.convertParamMarkerExpr(n)
|
|
|
|
case *pcast.ValueExprBase:
|
|
return c.convertValueExpr(n)
|
|
|
|
case *pcast.AdminStmt:
|
|
return c.convertAdminStmt(n)
|
|
|
|
case *pcast.AggregateFuncExpr:
|
|
return c.convertAggregateFuncExpr(n)
|
|
|
|
case *pcast.AlterDatabaseStmt:
|
|
return c.convertAlterDatabaseStmt(n)
|
|
|
|
case *pcast.AlterInstanceStmt:
|
|
return c.convertAlterInstanceStmt(n)
|
|
|
|
case *pcast.AlterTableSpec:
|
|
return c.convertAlterTableSpec(n)
|
|
|
|
case *pcast.AlterTableStmt:
|
|
return c.convertAlterTableStmt(n)
|
|
|
|
case *pcast.AlterUserStmt:
|
|
return c.convertAlterUserStmt(n)
|
|
|
|
case *pcast.AnalyzeTableStmt:
|
|
return c.convertAnalyzeTableStmt(n)
|
|
|
|
case *pcast.Assignment:
|
|
return c.convertAssignment(n)
|
|
|
|
case *pcast.BRIEStmt:
|
|
return c.convertBRIEStmt(n)
|
|
|
|
case *pcast.BeginStmt:
|
|
return c.convertBeginStmt(n)
|
|
|
|
case *pcast.BetweenExpr:
|
|
return c.convertBetweenExpr(n)
|
|
|
|
case *pcast.BinaryOperationExpr:
|
|
return c.convertBinaryOperationExpr(n)
|
|
|
|
case *pcast.BinlogStmt:
|
|
return c.convertBinlogStmt(n)
|
|
|
|
case *pcast.ByItem:
|
|
return c.convertByItem(n)
|
|
|
|
case *pcast.CallStmt:
|
|
return c.convertCallStmt(n)
|
|
|
|
case *pcast.CaseExpr:
|
|
return c.convertCaseExpr(n)
|
|
|
|
case *pcast.CleanupTableLockStmt:
|
|
return c.convertCleanupTableLockStmt(n)
|
|
|
|
case *pcast.ColumnDef:
|
|
return c.convertColumnDef(n)
|
|
|
|
case *pcast.ColumnName:
|
|
return c.convertColumnName(n)
|
|
|
|
case *pcast.ColumnNameExpr:
|
|
return c.convertColumnNameExpr(n)
|
|
|
|
case *pcast.ColumnPosition:
|
|
return c.convertColumnPosition(n)
|
|
|
|
case *pcast.CommitStmt:
|
|
return c.convertCommitStmt(n)
|
|
|
|
case *pcast.CompareSubqueryExpr:
|
|
return c.convertCompareSubqueryExpr(n)
|
|
|
|
case *pcast.Constraint:
|
|
return c.convertConstraint(n)
|
|
|
|
case *pcast.CreateBindingStmt:
|
|
return c.convertCreateBindingStmt(n)
|
|
|
|
case *pcast.CreateDatabaseStmt:
|
|
return c.convertCreateDatabaseStmt(n)
|
|
|
|
case *pcast.CreateIndexStmt:
|
|
return c.convertCreateIndexStmt(n)
|
|
|
|
case *pcast.CreateSequenceStmt:
|
|
return c.convertCreateSequenceStmt(n)
|
|
|
|
case *pcast.CreateStatisticsStmt:
|
|
return c.convertCreateStatisticsStmt(n)
|
|
|
|
case *pcast.CreateTableStmt:
|
|
return c.convertCreateTableStmt(n)
|
|
|
|
case *pcast.CreateUserStmt:
|
|
return c.convertCreateUserStmt(n)
|
|
|
|
case *pcast.CreateViewStmt:
|
|
return c.convertCreateViewStmt(n)
|
|
|
|
case *pcast.DeallocateStmt:
|
|
return c.convertDeallocateStmt(n)
|
|
|
|
case *pcast.DefaultExpr:
|
|
return c.convertDefaultExpr(n)
|
|
|
|
case *pcast.DeleteStmt:
|
|
return c.convertDeleteStmt(n)
|
|
|
|
case *pcast.DeleteTableList:
|
|
return c.convertDeleteTableList(n)
|
|
|
|
case *pcast.DoStmt:
|
|
return c.convertDoStmt(n)
|
|
|
|
case *pcast.DropBindingStmt:
|
|
return c.convertDropBindingStmt(n)
|
|
|
|
case *pcast.DropDatabaseStmt:
|
|
return c.convertDropDatabaseStmt(n)
|
|
|
|
case *pcast.DropIndexStmt:
|
|
return c.convertDropIndexStmt(n)
|
|
|
|
case *pcast.DropSequenceStmt:
|
|
return c.convertDropSequenceStmt(n)
|
|
|
|
case *pcast.DropStatisticsStmt:
|
|
return c.convertDropStatisticsStmt(n)
|
|
|
|
case *pcast.DropStatsStmt:
|
|
return c.convertDropStatsStmt(n)
|
|
|
|
case *pcast.DropTableStmt:
|
|
return c.convertDropTableStmt(n)
|
|
|
|
case *pcast.DropUserStmt:
|
|
return c.convertDropUserStmt(n)
|
|
|
|
case *pcast.ExecuteStmt:
|
|
return c.convertExecuteStmt(n)
|
|
|
|
case *pcast.ExistsSubqueryExpr:
|
|
return c.convertExistsSubqueryExpr(n)
|
|
|
|
case *pcast.ExplainForStmt:
|
|
return c.convertExplainForStmt(n)
|
|
|
|
case *pcast.ExplainStmt:
|
|
return c.convertExplainStmt(n)
|
|
|
|
case *pcast.FieldList:
|
|
return c.convertFieldList(n)
|
|
|
|
case *pcast.FlashBackTableStmt:
|
|
return c.convertFlashBackTableStmt(n)
|
|
|
|
case *pcast.FlushStmt:
|
|
return c.convertFlushStmt(n)
|
|
|
|
case *pcast.FrameBound:
|
|
return c.convertFrameBound(n)
|
|
|
|
case *pcast.FrameClause:
|
|
return c.convertFrameClause(n)
|
|
|
|
case *pcast.FuncCallExpr:
|
|
return c.convertFuncCallExpr(n)
|
|
|
|
case *pcast.FuncCastExpr:
|
|
return c.convertFuncCastExpr(n)
|
|
|
|
case *pcast.GetFormatSelectorExpr:
|
|
return c.convertGetFormatSelectorExpr(n)
|
|
|
|
case *pcast.GrantRoleStmt:
|
|
return c.convertGrantRoleStmt(n)
|
|
|
|
case *pcast.GrantStmt:
|
|
return c.convertGrantStmt(n)
|
|
|
|
case *pcast.GroupByClause:
|
|
return c.convertGroupByClause(n)
|
|
|
|
case *pcast.HavingClause:
|
|
return c.convertHavingClause(n)
|
|
|
|
case *pcast.IndexLockAndAlgorithm:
|
|
return c.convertIndexLockAndAlgorithm(n)
|
|
|
|
case *pcast.IndexPartSpecification:
|
|
return c.convertIndexPartSpecification(n)
|
|
|
|
case *pcast.InsertStmt:
|
|
return c.convertInsertStmt(n)
|
|
|
|
case *pcast.IsNullExpr:
|
|
return c.convertIsNullExpr(n)
|
|
|
|
case *pcast.IsTruthExpr:
|
|
return c.convertIsTruthExpr(n)
|
|
|
|
case *pcast.Join:
|
|
return c.convertJoin(n)
|
|
|
|
case *pcast.KillStmt:
|
|
return c.convertKillStmt(n)
|
|
|
|
case *pcast.Limit:
|
|
return c.convertLimit(n)
|
|
|
|
case *pcast.LoadDataStmt:
|
|
return c.convertLoadDataStmt(n)
|
|
|
|
case *pcast.LoadStatsStmt:
|
|
return c.convertLoadStatsStmt(n)
|
|
|
|
case *pcast.LockTablesStmt:
|
|
return c.convertLockTablesStmt(n)
|
|
|
|
case *pcast.MatchAgainst:
|
|
return c.convertMatchAgainst(n)
|
|
|
|
case *pcast.MaxValueExpr:
|
|
return c.convertMaxValueExpr(n)
|
|
|
|
case *pcast.OnCondition:
|
|
return c.convertOnCondition(n)
|
|
|
|
case *pcast.OnDeleteOpt:
|
|
return c.convertOnDeleteOpt(n)
|
|
|
|
case *pcast.OnUpdateOpt:
|
|
return c.convertOnUpdateOpt(n)
|
|
|
|
case *pcast.OrderByClause:
|
|
return c.convertOrderByClause(n)
|
|
|
|
case *pcast.ParenthesesExpr:
|
|
return c.convertParenthesesExpr(n)
|
|
|
|
case *pcast.PartitionByClause:
|
|
return c.convertPartitionByClause(n)
|
|
|
|
case *pcast.PatternInExpr:
|
|
return c.convertPatternInExpr(n)
|
|
|
|
case *pcast.PatternLikeOrIlikeExpr:
|
|
return c.convertPatternLikeExpr(n)
|
|
|
|
case *pcast.PatternRegexpExpr:
|
|
return c.convertPatternRegexpExpr(n)
|
|
|
|
case *pcast.PositionExpr:
|
|
return c.convertPositionExpr(n)
|
|
|
|
case *pcast.PrepareStmt:
|
|
return c.convertPrepareStmt(n)
|
|
|
|
case *pcast.PrivElem:
|
|
return c.convertPrivElem(n)
|
|
|
|
case *pcast.ProcedureInfo:
|
|
return c.convertProcedureInfo(n)
|
|
|
|
case *pcast.RecoverTableStmt:
|
|
return c.convertRecoverTableStmt(n)
|
|
|
|
case *pcast.ReferenceDef:
|
|
return c.convertReferenceDef(n)
|
|
|
|
case *pcast.RenameTableStmt:
|
|
return c.convertRenameTableStmt(n)
|
|
|
|
case *pcast.RepairTableStmt:
|
|
return c.convertRepairTableStmt(n)
|
|
|
|
case *pcast.RevokeRoleStmt:
|
|
return c.convertRevokeRoleStmt(n)
|
|
|
|
case *pcast.RevokeStmt:
|
|
return c.convertRevokeStmt(n)
|
|
|
|
case *pcast.RollbackStmt:
|
|
return c.convertRollbackStmt(n)
|
|
|
|
case *pcast.RowExpr:
|
|
return c.convertRowExpr(n)
|
|
|
|
case *pcast.SelectField:
|
|
return c.convertSelectField(n)
|
|
|
|
case *pcast.SelectStmt:
|
|
return c.convertSelectStmt(n)
|
|
|
|
case *pcast.SetCollationExpr:
|
|
return c.convertSetCollationExpr(n)
|
|
|
|
case *pcast.SetConfigStmt:
|
|
return c.convertSetConfigStmt(n)
|
|
|
|
case *pcast.SetDefaultRoleStmt:
|
|
return c.convertSetDefaultRoleStmt(n)
|
|
|
|
case *pcast.SetOprSelectList:
|
|
return c.convertSetOprSelectList(n)
|
|
|
|
case *pcast.SetOprStmt:
|
|
return c.convertSetOprStmt(n)
|
|
|
|
case *pcast.SetPwdStmt:
|
|
return c.convertSetPwdStmt(n)
|
|
|
|
case *pcast.SetRoleStmt:
|
|
return c.convertSetRoleStmt(n)
|
|
|
|
case *pcast.SetStmt:
|
|
return c.convertSetStmt(n)
|
|
|
|
case *pcast.ShowStmt:
|
|
return c.convertShowStmt(n)
|
|
|
|
case *pcast.ShutdownStmt:
|
|
return c.convertShutdownStmt(n)
|
|
|
|
case *pcast.SplitRegionStmt:
|
|
return c.convertSplitRegionStmt(n)
|
|
|
|
case *pcast.SubqueryExpr:
|
|
return c.convertSubqueryExpr(n)
|
|
|
|
case *pcast.TableName:
|
|
return c.convertTableName(n)
|
|
|
|
case *pcast.TableNameExpr:
|
|
return c.convertTableNameExpr(n)
|
|
|
|
case *pcast.TableOptimizerHint:
|
|
return c.convertTableOptimizerHint(n)
|
|
|
|
case *pcast.TableRefsClause:
|
|
return c.convertTableRefsClause(n)
|
|
|
|
case *pcast.TableSource:
|
|
return c.convertTableSource(n)
|
|
|
|
case *pcast.TableToTable:
|
|
return c.convertTableToTable(n)
|
|
|
|
case *pcast.TimeUnitExpr:
|
|
return c.convertTimeUnitExpr(n)
|
|
|
|
case *pcast.TraceStmt:
|
|
return c.convertTraceStmt(n)
|
|
|
|
case *pcast.TrimDirectionExpr:
|
|
return c.convertTrimDirectionExpr(n)
|
|
|
|
case *pcast.TruncateTableStmt:
|
|
return c.convertTruncateTableStmt(n)
|
|
|
|
case *pcast.UnaryOperationExpr:
|
|
return c.convertUnaryOperationExpr(n)
|
|
|
|
case *pcast.UnlockTablesStmt:
|
|
return c.convertUnlockTablesStmt(n)
|
|
|
|
case *pcast.UpdateStmt:
|
|
return c.convertUpdateStmt(n)
|
|
|
|
case *pcast.UseStmt:
|
|
return c.convertUseStmt(n)
|
|
|
|
case *pcast.ValuesExpr:
|
|
return c.convertValuesExpr(n)
|
|
|
|
case *pcast.VariableAssignment:
|
|
return c.convertVariableAssignment(n)
|
|
|
|
case *pcast.VariableExpr:
|
|
return c.convertVariableExpr(n)
|
|
|
|
case *pcast.WhenClause:
|
|
return c.convertWhenClause(n)
|
|
|
|
case *pcast.WildCardField:
|
|
return c.convertWildCardField(n)
|
|
|
|
case *pcast.WindowFuncExpr:
|
|
return c.convertWindowFuncExpr(n)
|
|
|
|
case *pcast.WindowSpec:
|
|
return c.convertWindowSpec(n)
|
|
|
|
case nil:
|
|
return nil
|
|
|
|
default:
|
|
return todo(n)
|
|
}
|
|
}
|