Files
upstream-mirror 0f4df23290
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
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

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: &paramName,
Type: &ast.TypeName{Name: types.TypeToStr(sp.ParamType.GetType(), sp.ParamType.GetCharset())},
})
}
return &ast.CreateFunctionStmt{
Params: &params,
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)
}
}