2266 lines
52 KiB
Go
Executable File
2266 lines
52 KiB
Go
Executable File
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package ast
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/parser/auth"
|
|
. "github.com/pingcap/parser/format"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/parser/mysql"
|
|
)
|
|
|
|
var (
|
|
_ DMLNode = &DeleteStmt{}
|
|
_ DMLNode = &InsertStmt{}
|
|
_ DMLNode = &UnionStmt{}
|
|
_ DMLNode = &UpdateStmt{}
|
|
_ DMLNode = &SelectStmt{}
|
|
_ DMLNode = &ShowStmt{}
|
|
_ DMLNode = &LoadDataStmt{}
|
|
|
|
_ Node = &Assignment{}
|
|
_ Node = &ByItem{}
|
|
_ Node = &FieldList{}
|
|
_ Node = &GroupByClause{}
|
|
_ Node = &HavingClause{}
|
|
_ Node = &Join{}
|
|
_ Node = &Limit{}
|
|
_ Node = &OnCondition{}
|
|
_ Node = &OrderByClause{}
|
|
_ Node = &SelectField{}
|
|
_ Node = &TableName{}
|
|
_ Node = &TableRefsClause{}
|
|
_ Node = &TableSource{}
|
|
_ Node = &UnionSelectList{}
|
|
_ Node = &WildCardField{}
|
|
_ Node = &WindowSpec{}
|
|
_ Node = &PartitionByClause{}
|
|
_ Node = &FrameClause{}
|
|
_ Node = &FrameBound{}
|
|
)
|
|
|
|
// JoinType is join type, including cross/left/right/full.
|
|
type JoinType int
|
|
|
|
const (
|
|
// CrossJoin is cross join type.
|
|
CrossJoin JoinType = iota + 1
|
|
// LeftJoin is left Join type.
|
|
LeftJoin
|
|
// RightJoin is right Join type.
|
|
RightJoin
|
|
)
|
|
|
|
// Join represents table join.
|
|
type Join struct {
|
|
node
|
|
resultSetNode
|
|
|
|
// Left table can be TableSource or JoinNode.
|
|
Left ResultSetNode
|
|
// Right table can be TableSource or JoinNode or nil.
|
|
Right ResultSetNode
|
|
// Tp represents join type.
|
|
Tp JoinType
|
|
// On represents join on condition.
|
|
On *OnCondition
|
|
// Using represents join using clause.
|
|
Using []*ColumnName
|
|
// NaturalJoin represents join is natural join.
|
|
NaturalJoin bool
|
|
// StraightJoin represents a straight join.
|
|
StraightJoin bool
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *Join) Restore(ctx *RestoreCtx) error {
|
|
if ctx.JoinLevel != 0 {
|
|
ctx.WritePlain("(")
|
|
defer ctx.WritePlain(")")
|
|
}
|
|
ctx.JoinLevel++
|
|
if err := n.Left.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Join.Left")
|
|
}
|
|
ctx.JoinLevel--
|
|
if n.Right == nil {
|
|
return nil
|
|
}
|
|
if n.NaturalJoin {
|
|
ctx.WriteKeyWord(" NATURAL")
|
|
}
|
|
switch n.Tp {
|
|
case LeftJoin:
|
|
ctx.WriteKeyWord(" LEFT")
|
|
case RightJoin:
|
|
ctx.WriteKeyWord(" RIGHT")
|
|
}
|
|
if n.StraightJoin {
|
|
ctx.WriteKeyWord(" STRAIGHT_JOIN ")
|
|
} else {
|
|
ctx.WriteKeyWord(" JOIN ")
|
|
}
|
|
ctx.JoinLevel++
|
|
if err := n.Right.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Join.Right")
|
|
}
|
|
ctx.JoinLevel--
|
|
|
|
if n.On != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.On.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Join.On")
|
|
}
|
|
}
|
|
if len(n.Using) != 0 {
|
|
ctx.WriteKeyWord(" USING ")
|
|
ctx.WritePlain("(")
|
|
for i, v := range n.Using {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Join.Using")
|
|
}
|
|
}
|
|
ctx.WritePlain(")")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *Join) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*Join)
|
|
node, ok := n.Left.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Left = node.(ResultSetNode)
|
|
if n.Right != nil {
|
|
node, ok = n.Right.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Right = node.(ResultSetNode)
|
|
}
|
|
if n.On != nil {
|
|
node, ok = n.On.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.On = node.(*OnCondition)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// TableName represents a table name.
|
|
type TableName struct {
|
|
node
|
|
resultSetNode
|
|
|
|
Schema model.CIStr
|
|
Name model.CIStr
|
|
|
|
DBInfo *model.DBInfo
|
|
TableInfo *model.TableInfo
|
|
|
|
IndexHints []*IndexHint
|
|
PartitionNames []model.CIStr
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *TableName) Restore(ctx *RestoreCtx) error {
|
|
if n.Schema.String() != "" {
|
|
ctx.WriteName(n.Schema.String())
|
|
ctx.WritePlain(".")
|
|
}
|
|
ctx.WriteName(n.Name.String())
|
|
if len(n.PartitionNames) > 0 {
|
|
ctx.WriteKeyWord(" PARTITION")
|
|
ctx.WritePlain("(")
|
|
for i, v := range n.PartitionNames {
|
|
if i != 0 {
|
|
ctx.WritePlain(", ")
|
|
}
|
|
ctx.WriteName(v.String())
|
|
}
|
|
ctx.WritePlain(")")
|
|
}
|
|
for _, value := range n.IndexHints {
|
|
ctx.WritePlain(" ")
|
|
if err := value.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while splicing IndexHints")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IndexHintType is the type for index hint use, ignore or force.
|
|
type IndexHintType int
|
|
|
|
// IndexHintUseType values.
|
|
const (
|
|
HintUse IndexHintType = 1
|
|
HintIgnore IndexHintType = 2
|
|
HintForce IndexHintType = 3
|
|
)
|
|
|
|
// IndexHintScope is the type for index hint for join, order by or group by.
|
|
type IndexHintScope int
|
|
|
|
// Index hint scopes.
|
|
const (
|
|
HintForScan IndexHintScope = 1
|
|
HintForJoin IndexHintScope = 2
|
|
HintForOrderBy IndexHintScope = 3
|
|
HintForGroupBy IndexHintScope = 4
|
|
)
|
|
|
|
// IndexHint represents a hint for optimizer to use/ignore/force for join/order by/group by.
|
|
type IndexHint struct {
|
|
IndexNames []model.CIStr
|
|
HintType IndexHintType
|
|
HintScope IndexHintScope
|
|
}
|
|
|
|
// IndexHint Restore (The const field uses switch to facilitate understanding)
|
|
func (n *IndexHint) Restore(ctx *RestoreCtx) error {
|
|
indexHintType := ""
|
|
switch n.HintType {
|
|
case 1:
|
|
indexHintType = "USE INDEX"
|
|
case 2:
|
|
indexHintType = "IGNORE INDEX"
|
|
case 3:
|
|
indexHintType = "FORCE INDEX"
|
|
default: // Prevent accidents
|
|
return errors.New("IndexHintType has an error while matching")
|
|
}
|
|
|
|
indexHintScope := ""
|
|
switch n.HintScope {
|
|
case 1:
|
|
indexHintScope = ""
|
|
case 2:
|
|
indexHintScope = " FOR JOIN"
|
|
case 3:
|
|
indexHintScope = " FOR ORDER BY"
|
|
case 4:
|
|
indexHintScope = " FOR GROUP BY"
|
|
default: // Prevent accidents
|
|
return errors.New("IndexHintScope has an error while matching")
|
|
}
|
|
ctx.WriteKeyWord(indexHintType)
|
|
ctx.WriteKeyWord(indexHintScope)
|
|
ctx.WritePlain(" (")
|
|
for i, value := range n.IndexNames {
|
|
if i > 0 {
|
|
ctx.WritePlain(", ")
|
|
}
|
|
ctx.WriteName(value.O)
|
|
}
|
|
ctx.WritePlain(")")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *TableName) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*TableName)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// DeleteTableList is the tablelist used in delete statement multi-table mode.
|
|
type DeleteTableList struct {
|
|
node
|
|
Tables []*TableName
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *DeleteTableList) Restore(ctx *RestoreCtx) error {
|
|
for i, t := range n.Tables {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := t.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore DeleteTableList.Tables[%v]", i)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *DeleteTableList) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*DeleteTableList)
|
|
if n != nil {
|
|
for i, t := range n.Tables {
|
|
node, ok := t.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Tables[i] = node.(*TableName)
|
|
}
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// OnCondition represents JOIN on condition.
|
|
type OnCondition struct {
|
|
node
|
|
|
|
Expr ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *OnCondition) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("ON ")
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore OnCondition.Expr")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *OnCondition) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*OnCondition)
|
|
node, ok := n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// TableSource represents table source with a name.
|
|
type TableSource struct {
|
|
node
|
|
|
|
// Source is the source of the data, can be a TableName,
|
|
// a SelectStmt, a UnionStmt, or a JoinNode.
|
|
Source ResultSetNode
|
|
|
|
// AsName is the alias name of the table source.
|
|
AsName model.CIStr
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *TableSource) Restore(ctx *RestoreCtx) error {
|
|
needParen := false
|
|
switch n.Source.(type) {
|
|
case *SelectStmt, *UnionStmt:
|
|
needParen = true
|
|
}
|
|
if needParen {
|
|
ctx.WritePlain("(")
|
|
}
|
|
if err := n.Source.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore TableSource.Source")
|
|
}
|
|
if needParen {
|
|
ctx.WritePlain(")")
|
|
}
|
|
if asName := n.AsName.String(); asName != "" {
|
|
ctx.WriteKeyWord(" AS ")
|
|
ctx.WriteName(asName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *TableSource) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*TableSource)
|
|
node, ok := n.Source.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Source = node.(ResultSetNode)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// SelectLockType is the lock type for SelectStmt.
|
|
type SelectLockType int
|
|
|
|
// Select lock types.
|
|
const (
|
|
SelectLockNone SelectLockType = iota
|
|
SelectLockForUpdate
|
|
SelectLockInShareMode
|
|
)
|
|
|
|
// String implements fmt.Stringer.
|
|
func (slt SelectLockType) String() string {
|
|
switch slt {
|
|
case SelectLockNone:
|
|
return "none"
|
|
case SelectLockForUpdate:
|
|
return "for update"
|
|
case SelectLockInShareMode:
|
|
return "in share mode"
|
|
}
|
|
return "unsupported select lock type"
|
|
}
|
|
|
|
// WildCardField is a special type of select field content.
|
|
type WildCardField struct {
|
|
node
|
|
|
|
Table model.CIStr
|
|
Schema model.CIStr
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *WildCardField) Restore(ctx *RestoreCtx) error {
|
|
if schema := n.Schema.String(); schema != "" {
|
|
ctx.WriteName(schema)
|
|
ctx.WritePlain(".")
|
|
}
|
|
if table := n.Table.String(); table != "" {
|
|
ctx.WriteName(table)
|
|
ctx.WritePlain(".")
|
|
}
|
|
ctx.WritePlain("*")
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *WildCardField) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*WildCardField)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// SelectField represents fields in select statement.
|
|
// There are two type of select field: wildcard
|
|
// and expression with optional alias name.
|
|
type SelectField struct {
|
|
node
|
|
|
|
// Offset is used to get original text.
|
|
Offset int
|
|
// WildCard is not nil, Expr will be nil.
|
|
WildCard *WildCardField
|
|
// Expr is not nil, WildCard will be nil.
|
|
Expr ExprNode
|
|
// AsName is alias name for Expr.
|
|
AsName model.CIStr
|
|
// Auxiliary stands for if this field is auxiliary.
|
|
// When we add a Field into SelectField list which is used for having/orderby clause but the field is not in select clause,
|
|
// we should set its Auxiliary to true. Then the TrimExec will trim the field.
|
|
Auxiliary bool
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *SelectField) Restore(ctx *RestoreCtx) error {
|
|
if n.WildCard != nil {
|
|
if err := n.WildCard.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore SelectField.WildCard")
|
|
}
|
|
}
|
|
if n.Expr != nil {
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore SelectField.Expr")
|
|
}
|
|
}
|
|
if asName := n.AsName.String(); asName != "" {
|
|
ctx.WriteKeyWord(" AS ")
|
|
ctx.WriteName(asName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *SelectField) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*SelectField)
|
|
if n.Expr != nil {
|
|
node, ok := n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// FieldList represents field list in select statement.
|
|
type FieldList struct {
|
|
node
|
|
|
|
Fields []*SelectField
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *FieldList) Restore(ctx *RestoreCtx) error {
|
|
for i, v := range n.Fields {
|
|
if i != 0 {
|
|
ctx.WritePlain(", ")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore FieldList.Fields[%d]", i)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *FieldList) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*FieldList)
|
|
for i, val := range n.Fields {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Fields[i] = node.(*SelectField)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// TableRefsClause represents table references clause in dml statement.
|
|
type TableRefsClause struct {
|
|
node
|
|
|
|
TableRefs *Join
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *TableRefsClause) Restore(ctx *RestoreCtx) error {
|
|
if err := n.TableRefs.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore TableRefsClause.TableRefs")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *TableRefsClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*TableRefsClause)
|
|
node, ok := n.TableRefs.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.TableRefs = node.(*Join)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// ByItem represents an item in order by or group by.
|
|
type ByItem struct {
|
|
node
|
|
|
|
Expr ExprNode
|
|
Desc bool
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *ByItem) Restore(ctx *RestoreCtx) error {
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ByItem.Expr")
|
|
}
|
|
if n.Desc {
|
|
ctx.WriteKeyWord(" DESC")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *ByItem) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*ByItem)
|
|
node, ok := n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// GroupByClause represents group by clause.
|
|
type GroupByClause struct {
|
|
node
|
|
Items []*ByItem
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *GroupByClause) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("GROUP BY ")
|
|
for i, v := range n.Items {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore GroupByClause.Items[%d]", i)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *GroupByClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*GroupByClause)
|
|
for i, val := range n.Items {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Items[i] = node.(*ByItem)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// HavingClause represents having clause.
|
|
type HavingClause struct {
|
|
node
|
|
Expr ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *HavingClause) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("HAVING ")
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore HavingClause.Expr")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *HavingClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*HavingClause)
|
|
node, ok := n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// OrderByClause represents order by clause.
|
|
type OrderByClause struct {
|
|
node
|
|
Items []*ByItem
|
|
ForUnion bool
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *OrderByClause) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("ORDER BY ")
|
|
for i, item := range n.Items {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := item.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore OrderByClause.Items[%d]", i)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *OrderByClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*OrderByClause)
|
|
for i, val := range n.Items {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Items[i] = node.(*ByItem)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// SelectStmt represents the select query node.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/select.html
|
|
type SelectStmt struct {
|
|
dmlNode
|
|
resultSetNode
|
|
|
|
// SelectStmtOpts wraps around select hints and switches.
|
|
*SelectStmtOpts
|
|
// Distinct represents whether the select has distinct option.
|
|
Distinct bool
|
|
// From is the from clause of the query.
|
|
From *TableRefsClause
|
|
// Where is the where clause in select statement.
|
|
Where ExprNode
|
|
// Fields is the select expression list.
|
|
Fields *FieldList
|
|
// GroupBy is the group by expression list.
|
|
GroupBy *GroupByClause
|
|
// Having is the having condition.
|
|
Having *HavingClause
|
|
// WindowSpecs is the window specification list.
|
|
WindowSpecs []WindowSpec
|
|
// OrderBy is the ordering expression list.
|
|
OrderBy *OrderByClause
|
|
// Limit is the limit clause.
|
|
Limit *Limit
|
|
// LockTp is the lock type
|
|
LockTp SelectLockType
|
|
// TableHints represents the table level Optimizer Hint for join type
|
|
TableHints []*TableOptimizerHint
|
|
// IsAfterUnionDistinct indicates whether it's a stmt after "union distinct".
|
|
IsAfterUnionDistinct bool
|
|
// IsInBraces indicates whether it's a stmt in brace.
|
|
IsInBraces bool
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("SELECT ")
|
|
|
|
if n.SelectStmtOpts.Priority > 0 {
|
|
ctx.WriteKeyWord(mysql.Priority2Str[n.SelectStmtOpts.Priority])
|
|
ctx.WritePlain(" ")
|
|
}
|
|
|
|
if !n.SelectStmtOpts.SQLCache {
|
|
ctx.WriteKeyWord("SQL_NO_CACHE ")
|
|
}
|
|
|
|
if n.TableHints != nil && len(n.TableHints) != 0 {
|
|
ctx.WritePlain("/*+ ")
|
|
for i, tableHint := range n.TableHints {
|
|
if err := tableHint.Restore(ctx); err != nil {
|
|
errors.Annotatef(err, "An error occurred while restore SelectStmt.TableHints[%d]", i)
|
|
}
|
|
}
|
|
ctx.WritePlain("*/ ")
|
|
}
|
|
|
|
if n.Distinct {
|
|
ctx.WriteKeyWord("DISTINCT ")
|
|
}
|
|
if n.SelectStmtOpts.StraightJoin {
|
|
ctx.WriteKeyWord("STRAIGHT_JOIN ")
|
|
}
|
|
if n.Fields != nil {
|
|
for i, field := range n.Fields.Fields {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := field.Restore(ctx); err != nil {
|
|
errors.Annotatef(err, "An error occurred while restore SelectStmt.Fields[%d]", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.From != nil {
|
|
ctx.WriteKeyWord(" FROM ")
|
|
if err := n.From.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.From")
|
|
}
|
|
}
|
|
|
|
if n.From == nil && n.Where != nil {
|
|
ctx.WriteKeyWord(" FROM DUAL")
|
|
}
|
|
if n.Where != nil {
|
|
ctx.WriteKeyWord(" WHERE ")
|
|
if err := n.Where.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.Where")
|
|
}
|
|
}
|
|
|
|
if n.GroupBy != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.GroupBy.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.GroupBy")
|
|
}
|
|
}
|
|
|
|
if n.Having != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Having.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.Having")
|
|
}
|
|
}
|
|
|
|
if n.WindowSpecs != nil {
|
|
ctx.WriteKeyWord(" WINDOW ")
|
|
for i, windowsSpec := range n.WindowSpecs {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := windowsSpec.Restore(ctx); err != nil {
|
|
errors.Annotatef(err, "An error occurred while restore SelectStmt.WindowSpec[%d]", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.OrderBy != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.OrderBy.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.OrderBy")
|
|
}
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Limit.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore SelectStmt.Limit")
|
|
}
|
|
}
|
|
|
|
switch n.LockTp {
|
|
case SelectLockInShareMode:
|
|
ctx.WriteKeyWord(" LOCK ")
|
|
ctx.WriteKeyWord(n.LockTp.String())
|
|
case SelectLockForUpdate:
|
|
ctx.WritePlain(" ")
|
|
ctx.WriteKeyWord(n.LockTp.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *SelectStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
|
|
n = newNode.(*SelectStmt)
|
|
if n.TableHints != nil && len(n.TableHints) != 0 {
|
|
newHints := make([]*TableOptimizerHint, len(n.TableHints))
|
|
for i, hint := range n.TableHints {
|
|
node, ok := hint.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
newHints[i] = node.(*TableOptimizerHint)
|
|
}
|
|
n.TableHints = newHints
|
|
}
|
|
|
|
if n.From != nil {
|
|
node, ok := n.From.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.From = node.(*TableRefsClause)
|
|
}
|
|
|
|
if n.Where != nil {
|
|
node, ok := n.Where.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Where = node.(ExprNode)
|
|
}
|
|
|
|
if n.Fields != nil {
|
|
node, ok := n.Fields.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Fields = node.(*FieldList)
|
|
}
|
|
|
|
if n.GroupBy != nil {
|
|
node, ok := n.GroupBy.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.GroupBy = node.(*GroupByClause)
|
|
}
|
|
|
|
if n.Having != nil {
|
|
node, ok := n.Having.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Having = node.(*HavingClause)
|
|
}
|
|
|
|
for i, spec := range n.WindowSpecs {
|
|
node, ok := spec.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.WindowSpecs[i] = *node.(*WindowSpec)
|
|
}
|
|
|
|
if n.OrderBy != nil {
|
|
node, ok := n.OrderBy.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.OrderBy = node.(*OrderByClause)
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
node, ok := n.Limit.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Limit = node.(*Limit)
|
|
}
|
|
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// UnionSelectList represents the select list in a union statement.
|
|
type UnionSelectList struct {
|
|
node
|
|
|
|
Selects []*SelectStmt
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *UnionSelectList) Restore(ctx *RestoreCtx) error {
|
|
for i, selectStmt := range n.Selects {
|
|
if i != 0 {
|
|
ctx.WriteKeyWord(" UNION ")
|
|
if !selectStmt.IsAfterUnionDistinct {
|
|
ctx.WriteKeyWord("ALL ")
|
|
}
|
|
}
|
|
if selectStmt.IsInBraces {
|
|
ctx.WritePlain("(")
|
|
}
|
|
if err := selectStmt.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore UnionSelectList.SelectStmt")
|
|
}
|
|
if selectStmt.IsInBraces {
|
|
ctx.WritePlain(")")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *UnionSelectList) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*UnionSelectList)
|
|
for i, sel := range n.Selects {
|
|
node, ok := sel.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Selects[i] = node.(*SelectStmt)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// UnionStmt represents "union statement"
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/union.html
|
|
type UnionStmt struct {
|
|
dmlNode
|
|
resultSetNode
|
|
|
|
SelectList *UnionSelectList
|
|
OrderBy *OrderByClause
|
|
Limit *Limit
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *UnionStmt) Restore(ctx *RestoreCtx) error {
|
|
if err := n.SelectList.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore UnionStmt.SelectList")
|
|
}
|
|
|
|
if n.OrderBy != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.OrderBy.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore UnionStmt.OrderBy")
|
|
}
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Limit.Restore(ctx); err != nil {
|
|
errors.Annotate(err, "An error occurred while restore UnionStmt.Limit")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *UnionStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*UnionStmt)
|
|
if n.SelectList != nil {
|
|
node, ok := n.SelectList.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.SelectList = node.(*UnionSelectList)
|
|
}
|
|
if n.OrderBy != nil {
|
|
node, ok := n.OrderBy.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.OrderBy = node.(*OrderByClause)
|
|
}
|
|
if n.Limit != nil {
|
|
node, ok := n.Limit.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Limit = node.(*Limit)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// Assignment is the expression for assignment, like a = 1.
|
|
type Assignment struct {
|
|
node
|
|
// Column is the column name to be assigned.
|
|
Column *ColumnName
|
|
// Expr is the expression assigning to ColName.
|
|
Expr ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *Assignment) Restore(ctx *RestoreCtx) error {
|
|
if err := n.Column.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Assignment.Column")
|
|
}
|
|
ctx.WritePlain("=")
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Assignment.Expr")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *Assignment) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*Assignment)
|
|
node, ok := n.Column.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Column = node.(*ColumnName)
|
|
node, ok = n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// LoadDataStmt is a statement to load data from a specified file, then insert this rows into an existing table.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/load-data.html
|
|
type LoadDataStmt struct {
|
|
dmlNode
|
|
|
|
IsLocal bool
|
|
Path string
|
|
Table *TableName
|
|
Columns []*ColumnName
|
|
FieldsInfo *FieldsClause
|
|
LinesInfo *LinesClause
|
|
IgnoreLines uint64
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *LoadDataStmt) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("LOAD DATA ")
|
|
if n.IsLocal {
|
|
ctx.WriteKeyWord("LOCAL ")
|
|
}
|
|
ctx.WriteKeyWord("INFILE ")
|
|
ctx.WriteString(n.Path)
|
|
ctx.WriteKeyWord(" INTO TABLE ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore LoadDataStmt.Table")
|
|
}
|
|
n.FieldsInfo.Restore(ctx)
|
|
n.LinesInfo.Restore(ctx)
|
|
if n.IgnoreLines != 0 {
|
|
ctx.WriteKeyWord(" IGNORE ")
|
|
ctx.WritePlainf("%d", n.IgnoreLines)
|
|
ctx.WriteKeyWord(" LINES")
|
|
}
|
|
if len(n.Columns) != 0 {
|
|
ctx.WritePlain(" (")
|
|
for i, column := range n.Columns {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := column.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore LoadDataStmt.Columns")
|
|
}
|
|
}
|
|
ctx.WritePlain(")")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *LoadDataStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*LoadDataStmt)
|
|
if n.Table != nil {
|
|
node, ok := n.Table.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Table = node.(*TableName)
|
|
}
|
|
for i, val := range n.Columns {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Columns[i] = node.(*ColumnName)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// FieldsClause represents fields references clause in load data statement.
|
|
type FieldsClause struct {
|
|
Terminated string
|
|
Enclosed byte
|
|
Escaped byte
|
|
}
|
|
|
|
// Restore for FieldsClause
|
|
func (n *FieldsClause) Restore(ctx *RestoreCtx) error {
|
|
if n.Terminated != "\t" || n.Escaped != '\\' {
|
|
ctx.WriteKeyWord(" FIELDS")
|
|
if n.Terminated != "\t" {
|
|
ctx.WriteKeyWord(" TERMINATED BY ")
|
|
ctx.WriteString(n.Terminated)
|
|
}
|
|
if n.Enclosed != 0 {
|
|
ctx.WriteKeyWord(" ENCLOSED BY ")
|
|
ctx.WriteString(string(n.Enclosed))
|
|
}
|
|
if n.Escaped != '\\' {
|
|
ctx.WriteKeyWord(" ESCAPED BY ")
|
|
if n.Escaped == 0 {
|
|
ctx.WritePlain("''")
|
|
} else {
|
|
ctx.WriteString(string(n.Escaped))
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LinesClause represents lines references clause in load data statement.
|
|
type LinesClause struct {
|
|
Starting string
|
|
Terminated string
|
|
}
|
|
|
|
// Restore for LinesClause
|
|
func (n *LinesClause) Restore(ctx *RestoreCtx) error {
|
|
if n.Starting != "" || n.Terminated != "\n" {
|
|
ctx.WriteKeyWord(" LINES")
|
|
if n.Starting != "" {
|
|
ctx.WriteKeyWord(" STARTING BY ")
|
|
ctx.WriteString(n.Starting)
|
|
}
|
|
if n.Terminated != "\n" {
|
|
ctx.WriteKeyWord(" TERMINATED BY ")
|
|
ctx.WriteString(n.Terminated)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InsertStmt is a statement to insert new rows into an existing table.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/insert.html
|
|
type InsertStmt struct {
|
|
dmlNode
|
|
|
|
IsReplace bool
|
|
IgnoreErr bool
|
|
Table *TableRefsClause
|
|
Columns []*ColumnName
|
|
Lists [][]ExprNode
|
|
Setlist []*Assignment
|
|
Priority mysql.PriorityEnum
|
|
OnDuplicate []*Assignment
|
|
Select ResultSetNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *InsertStmt) Restore(ctx *RestoreCtx) error {
|
|
if n.IsReplace {
|
|
ctx.WriteKeyWord("REPLACE ")
|
|
} else {
|
|
ctx.WriteKeyWord("INSERT ")
|
|
}
|
|
if err := n.Priority.Restore(ctx); err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
if n.Priority != mysql.NoPriority {
|
|
ctx.WritePlain(" ")
|
|
}
|
|
if n.IgnoreErr {
|
|
ctx.WriteKeyWord("IGNORE ")
|
|
}
|
|
ctx.WriteKeyWord("INTO ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore InsertStmt.Table")
|
|
}
|
|
if n.Columns != nil {
|
|
ctx.WritePlain(" (")
|
|
for i, v := range n.Columns {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Columns[%d]", i)
|
|
}
|
|
}
|
|
ctx.WritePlain(")")
|
|
}
|
|
if n.Lists != nil {
|
|
ctx.WriteKeyWord(" VALUES ")
|
|
for i, row := range n.Lists {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
ctx.WritePlain("(")
|
|
for j, v := range row {
|
|
if j != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Lists[%d][%d]", i, j)
|
|
}
|
|
}
|
|
ctx.WritePlain(")")
|
|
}
|
|
}
|
|
if n.Select != nil {
|
|
ctx.WritePlain(" ")
|
|
switch v := n.Select.(type) {
|
|
case *SelectStmt, *UnionStmt:
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore InsertStmt.Select")
|
|
}
|
|
default:
|
|
return errors.Errorf("Incorrect type for InsertStmt.Select: %T", v)
|
|
}
|
|
}
|
|
if n.Setlist != nil {
|
|
ctx.WriteKeyWord(" SET ")
|
|
for i, v := range n.Setlist {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Setlist[%d]", i)
|
|
}
|
|
}
|
|
}
|
|
if n.OnDuplicate != nil {
|
|
ctx.WriteKeyWord(" ON DUPLICATE KEY UPDATE ")
|
|
for i, v := range n.OnDuplicate {
|
|
if i != 0 {
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore InsertStmt.OnDuplicate[%d]", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *InsertStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
|
|
n = newNode.(*InsertStmt)
|
|
if n.Select != nil {
|
|
node, ok := n.Select.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Select = node.(ResultSetNode)
|
|
}
|
|
|
|
node, ok := n.Table.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Table = node.(*TableRefsClause)
|
|
|
|
for i, val := range n.Columns {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Columns[i] = node.(*ColumnName)
|
|
}
|
|
for i, list := range n.Lists {
|
|
for j, val := range list {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Lists[i][j] = node.(ExprNode)
|
|
}
|
|
}
|
|
for i, val := range n.Setlist {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Setlist[i] = node.(*Assignment)
|
|
}
|
|
for i, val := range n.OnDuplicate {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.OnDuplicate[i] = node.(*Assignment)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// DeleteStmt is a statement to delete rows from table.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/delete.html
|
|
type DeleteStmt struct {
|
|
dmlNode
|
|
|
|
// TableRefs is used in both single table and multiple table delete statement.
|
|
TableRefs *TableRefsClause
|
|
// Tables is only used in multiple table delete statement.
|
|
Tables *DeleteTableList
|
|
Where ExprNode
|
|
Order *OrderByClause
|
|
Limit *Limit
|
|
Priority mysql.PriorityEnum
|
|
IgnoreErr bool
|
|
Quick bool
|
|
IsMultiTable bool
|
|
BeforeFrom bool
|
|
// TableHints represents the table level Optimizer Hint for join type.
|
|
TableHints []*TableOptimizerHint
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *DeleteStmt) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("DELETE ")
|
|
|
|
if n.TableHints != nil && len(n.TableHints) != 0 {
|
|
ctx.WritePlain("/*+ ")
|
|
for i, tableHint := range n.TableHints {
|
|
if err := tableHint.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore UpdateStmt.TableHints[%d]", i)
|
|
}
|
|
}
|
|
ctx.WritePlain("*/ ")
|
|
}
|
|
|
|
if err := n.Priority.Restore(ctx); err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
if n.Priority != mysql.NoPriority {
|
|
ctx.WritePlain(" ")
|
|
}
|
|
if n.Quick {
|
|
ctx.WriteKeyWord("QUICK ")
|
|
}
|
|
if n.IgnoreErr {
|
|
ctx.WriteKeyWord("IGNORE ")
|
|
}
|
|
|
|
if n.IsMultiTable { // Multiple-Table Syntax
|
|
if n.BeforeFrom {
|
|
if err := n.Tables.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.Tables")
|
|
}
|
|
|
|
ctx.WriteKeyWord(" FROM ")
|
|
if err := n.TableRefs.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.TableRefs")
|
|
}
|
|
} else {
|
|
ctx.WriteKeyWord("FROM ")
|
|
if err := n.Tables.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.Tables")
|
|
}
|
|
|
|
ctx.WriteKeyWord(" USING ")
|
|
if err := n.TableRefs.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.TableRefs")
|
|
}
|
|
}
|
|
} else { // Single-Table Syntax
|
|
ctx.WriteKeyWord("FROM ")
|
|
|
|
if err := n.TableRefs.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.TableRefs")
|
|
}
|
|
}
|
|
|
|
if n.Where != nil {
|
|
ctx.WriteKeyWord(" WHERE ")
|
|
if err := n.Where.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.Where")
|
|
}
|
|
}
|
|
|
|
if n.Order != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Order.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.Order")
|
|
}
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Limit.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore DeleteStmt.Limit")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *DeleteStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
|
|
n = newNode.(*DeleteStmt)
|
|
node, ok := n.TableRefs.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.TableRefs = node.(*TableRefsClause)
|
|
|
|
if n.Tables != nil {
|
|
node, ok = n.Tables.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Tables = node.(*DeleteTableList)
|
|
}
|
|
|
|
if n.Where != nil {
|
|
node, ok = n.Where.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Where = node.(ExprNode)
|
|
}
|
|
if n.Order != nil {
|
|
node, ok = n.Order.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Order = node.(*OrderByClause)
|
|
}
|
|
if n.Limit != nil {
|
|
node, ok = n.Limit.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Limit = node.(*Limit)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// UpdateStmt is a statement to update columns of existing rows in tables with new values.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/update.html
|
|
type UpdateStmt struct {
|
|
dmlNode
|
|
|
|
TableRefs *TableRefsClause
|
|
List []*Assignment
|
|
Where ExprNode
|
|
Order *OrderByClause
|
|
Limit *Limit
|
|
Priority mysql.PriorityEnum
|
|
IgnoreErr bool
|
|
MultipleTable bool
|
|
TableHints []*TableOptimizerHint
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *UpdateStmt) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("UPDATE ")
|
|
|
|
if n.TableHints != nil && len(n.TableHints) != 0 {
|
|
ctx.WritePlain("/*+ ")
|
|
for i, tableHint := range n.TableHints {
|
|
if err := tableHint.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore UpdateStmt.TableHints[%d]", i)
|
|
}
|
|
}
|
|
ctx.WritePlain("*/ ")
|
|
}
|
|
|
|
if err := n.Priority.Restore(ctx); err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
if n.Priority != mysql.NoPriority {
|
|
ctx.WritePlain(" ")
|
|
}
|
|
if n.IgnoreErr {
|
|
ctx.WriteKeyWord("IGNORE ")
|
|
}
|
|
|
|
if err := n.TableRefs.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occur while restore UpdateStmt.TableRefs")
|
|
}
|
|
|
|
ctx.WriteKeyWord(" SET ")
|
|
for i, assignment := range n.List {
|
|
if i != 0 {
|
|
ctx.WritePlain(", ")
|
|
}
|
|
|
|
if err := assignment.Column.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occur while restore UpdateStmt.List[%d].Column", i)
|
|
}
|
|
|
|
ctx.WritePlain("=")
|
|
|
|
if err := assignment.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occur while restore UpdateStmt.List[%d].Expr", i)
|
|
}
|
|
}
|
|
|
|
if n.Where != nil {
|
|
ctx.WriteKeyWord(" WHERE ")
|
|
if err := n.Where.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occur while restore UpdateStmt.Where")
|
|
}
|
|
}
|
|
|
|
if n.Order != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Order.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occur while restore UpdateStmt.Order")
|
|
}
|
|
}
|
|
|
|
if n.Limit != nil {
|
|
ctx.WritePlain(" ")
|
|
if err := n.Limit.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occur while restore UpdateStmt.Limit")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *UpdateStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*UpdateStmt)
|
|
node, ok := n.TableRefs.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.TableRefs = node.(*TableRefsClause)
|
|
for i, val := range n.List {
|
|
node, ok = val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.List[i] = node.(*Assignment)
|
|
}
|
|
if n.Where != nil {
|
|
node, ok = n.Where.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Where = node.(ExprNode)
|
|
}
|
|
if n.Order != nil {
|
|
node, ok = n.Order.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Order = node.(*OrderByClause)
|
|
}
|
|
if n.Limit != nil {
|
|
node, ok = n.Limit.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Limit = node.(*Limit)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// Limit is the limit clause.
|
|
type Limit struct {
|
|
node
|
|
|
|
Count ExprNode
|
|
Offset ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *Limit) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("LIMIT ")
|
|
if n.Offset != nil {
|
|
if err := n.Offset.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Limit.Offset")
|
|
}
|
|
ctx.WritePlain(",")
|
|
}
|
|
if err := n.Count.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore Limit.Count")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *Limit) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
if n.Count != nil {
|
|
node, ok := n.Count.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Count = node.(ExprNode)
|
|
}
|
|
if n.Offset != nil {
|
|
node, ok := n.Offset.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Offset = node.(ExprNode)
|
|
}
|
|
|
|
n = newNode.(*Limit)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// ShowStmtType is the type for SHOW statement.
|
|
type ShowStmtType int
|
|
|
|
// Show statement types.
|
|
const (
|
|
ShowNone = iota
|
|
ShowEngines
|
|
ShowDatabases
|
|
ShowTables
|
|
ShowTableStatus
|
|
ShowColumns
|
|
ShowWarnings
|
|
ShowCharset
|
|
ShowVariables
|
|
ShowStatus
|
|
ShowCollation
|
|
ShowCreateTable
|
|
ShowCreateView
|
|
ShowCreateUser
|
|
ShowGrants
|
|
ShowTriggers
|
|
ShowProcedureStatus
|
|
ShowIndex
|
|
ShowProcessList
|
|
ShowCreateDatabase
|
|
ShowEvents
|
|
ShowStatsMeta
|
|
ShowStatsHistograms
|
|
ShowStatsBuckets
|
|
ShowStatsHealthy
|
|
ShowPlugins
|
|
ShowProfiles
|
|
ShowMasterStatus
|
|
ShowPrivileges
|
|
ShowErrors
|
|
ShowBindings
|
|
ShowPumpStatus
|
|
ShowDrainerStatus
|
|
)
|
|
|
|
// ShowStmt is a statement to provide information about databases, tables, columns and so on.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/show.html
|
|
type ShowStmt struct {
|
|
dmlNode
|
|
resultSetNode
|
|
|
|
Tp ShowStmtType // Databases/Tables/Columns/....
|
|
DBName string
|
|
Table *TableName // Used for showing columns.
|
|
Column *ColumnName // Used for `desc table column`.
|
|
Flag int // Some flag parsed from sql, such as FULL.
|
|
Full bool
|
|
User *auth.UserIdentity // Used for show grants/create user.
|
|
IfNotExists bool // Used for `show create database if not exists`
|
|
|
|
// GlobalScope is used by `show variables` and `show bindings`
|
|
GlobalScope bool
|
|
Pattern *PatternLikeExpr
|
|
Where ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *ShowStmt) Restore(ctx *RestoreCtx) error {
|
|
restoreOptFull := func() {
|
|
if n.Full {
|
|
ctx.WriteKeyWord("FULL ")
|
|
}
|
|
}
|
|
restoreShowDatabaseNameOpt := func() {
|
|
if n.DBName != "" {
|
|
// FROM OR IN
|
|
ctx.WriteKeyWord(" IN ")
|
|
ctx.WriteName(n.DBName)
|
|
}
|
|
}
|
|
restoreGlobalScope := func() {
|
|
if n.GlobalScope {
|
|
ctx.WriteKeyWord("GLOBAL ")
|
|
} else {
|
|
ctx.WriteKeyWord("SESSION ")
|
|
}
|
|
}
|
|
restoreShowLikeOrWhereOpt := func() error {
|
|
if n.Pattern != nil && n.Pattern.Pattern != nil {
|
|
ctx.WriteKeyWord(" LIKE ")
|
|
if err := n.Pattern.Pattern.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.Pattern")
|
|
}
|
|
} else if n.Where != nil {
|
|
ctx.WriteKeyWord(" WHERE ")
|
|
if err := n.Where.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.Where")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
ctx.WriteKeyWord("SHOW ")
|
|
switch n.Tp {
|
|
case ShowCreateTable:
|
|
ctx.WriteKeyWord("CREATE TABLE ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.Table")
|
|
}
|
|
case ShowCreateView:
|
|
ctx.WriteKeyWord("CREATE VIEW ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.VIEW")
|
|
}
|
|
case ShowCreateDatabase:
|
|
ctx.WriteKeyWord("CREATE DATABASE ")
|
|
if n.IfNotExists {
|
|
ctx.WriteKeyWord("IF NOT EXISTS ")
|
|
}
|
|
ctx.WriteName(n.DBName)
|
|
case ShowCreateUser:
|
|
ctx.WriteKeyWord("CREATE USER ")
|
|
if err := n.User.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.User")
|
|
}
|
|
case ShowGrants:
|
|
ctx.WriteKeyWord("GRANTS")
|
|
if n.User != nil {
|
|
ctx.WriteKeyWord(" FOR ")
|
|
if err := n.User.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore ShowStmt.User")
|
|
}
|
|
}
|
|
case ShowMasterStatus:
|
|
ctx.WriteKeyWord("MASTER STATUS")
|
|
case ShowProcessList:
|
|
restoreOptFull()
|
|
ctx.WriteKeyWord("PROCESSLIST")
|
|
case ShowStatsMeta:
|
|
ctx.WriteKeyWord("STATS_META")
|
|
if err := restoreShowLikeOrWhereOpt(); err != nil {
|
|
return err
|
|
}
|
|
case ShowStatsHistograms:
|
|
ctx.WriteKeyWord("STATS_HISTOGRAMS")
|
|
if err := restoreShowLikeOrWhereOpt(); err != nil {
|
|
return err
|
|
}
|
|
case ShowStatsBuckets:
|
|
ctx.WriteKeyWord("STATS_BUCKETS")
|
|
if err := restoreShowLikeOrWhereOpt(); err != nil {
|
|
return err
|
|
}
|
|
case ShowStatsHealthy:
|
|
ctx.WriteKeyWord("STATS_HEALTHY")
|
|
if err := restoreShowLikeOrWhereOpt(); err != nil {
|
|
return err
|
|
}
|
|
case ShowProfiles:
|
|
ctx.WriteKeyWord("PROFILES")
|
|
case ShowPrivileges:
|
|
ctx.WriteKeyWord("PRIVILEGES")
|
|
// ShowTargetFilterable
|
|
default:
|
|
switch n.Tp {
|
|
case ShowEngines:
|
|
ctx.WriteKeyWord("ENGINES")
|
|
case ShowDatabases:
|
|
ctx.WriteKeyWord("DATABASES")
|
|
case ShowCharset:
|
|
ctx.WriteKeyWord("CHARSET")
|
|
case ShowTables:
|
|
restoreOptFull()
|
|
ctx.WriteKeyWord("TABLES")
|
|
restoreShowDatabaseNameOpt()
|
|
case ShowTableStatus:
|
|
ctx.WriteKeyWord("TABLE STATUS")
|
|
restoreShowDatabaseNameOpt()
|
|
case ShowIndex:
|
|
// here can be INDEX INDEXES KEYS
|
|
// FROM or IN
|
|
ctx.WriteKeyWord("INDEX IN ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while resotre ShowStmt.Table")
|
|
} // TODO: remember to check this case
|
|
case ShowColumns: // equivalent to SHOW FIELDS
|
|
restoreOptFull()
|
|
ctx.WriteKeyWord("COLUMNS")
|
|
if n.Table != nil {
|
|
// FROM or IN
|
|
ctx.WriteKeyWord(" IN ")
|
|
if err := n.Table.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while resotre ShowStmt.Table")
|
|
}
|
|
}
|
|
restoreShowDatabaseNameOpt()
|
|
case ShowWarnings:
|
|
ctx.WriteKeyWord("WARNINGS")
|
|
case ShowErrors:
|
|
ctx.WriteKeyWord("ERRORS")
|
|
case ShowVariables:
|
|
restoreGlobalScope()
|
|
ctx.WriteKeyWord("VARIABLES")
|
|
case ShowStatus:
|
|
restoreGlobalScope()
|
|
ctx.WriteKeyWord("STATUS")
|
|
case ShowCollation:
|
|
ctx.WriteKeyWord("COLLATION")
|
|
case ShowTriggers:
|
|
ctx.WriteKeyWord("TRIGGERS")
|
|
restoreShowDatabaseNameOpt()
|
|
case ShowProcedureStatus:
|
|
ctx.WriteKeyWord("PROCEDURE STATUS")
|
|
case ShowEvents:
|
|
ctx.WriteKeyWord("EVENTS")
|
|
restoreShowDatabaseNameOpt()
|
|
case ShowPlugins:
|
|
ctx.WriteKeyWord("PLUGINS")
|
|
case ShowBindings:
|
|
if n.GlobalScope {
|
|
ctx.WriteKeyWord("GLOBAL ")
|
|
} else {
|
|
ctx.WriteKeyWord("SESSION ")
|
|
}
|
|
ctx.WriteKeyWord("BINDINGS")
|
|
case ShowPumpStatus:
|
|
ctx.WriteKeyWord("PUMP STATUS")
|
|
case ShowDrainerStatus:
|
|
ctx.WriteKeyWord("DRAINER STATUS")
|
|
default:
|
|
return errors.New("Unknown ShowStmt type")
|
|
}
|
|
restoreShowLikeOrWhereOpt()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *ShowStmt) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*ShowStmt)
|
|
if n.Table != nil {
|
|
node, ok := n.Table.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Table = node.(*TableName)
|
|
}
|
|
if n.Column != nil {
|
|
node, ok := n.Column.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Column = node.(*ColumnName)
|
|
}
|
|
if n.Pattern != nil {
|
|
node, ok := n.Pattern.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Pattern = node.(*PatternLikeExpr)
|
|
}
|
|
|
|
switch n.Tp {
|
|
case ShowTriggers, ShowProcedureStatus, ShowProcessList, ShowEvents:
|
|
// We don't have any data to return for those types,
|
|
// but visiting Where may cause resolving error, so return here to avoid error.
|
|
return v.Leave(n)
|
|
}
|
|
|
|
if n.Where != nil {
|
|
node, ok := n.Where.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Where = node.(ExprNode)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// WindowSpec is the specification of a window.
|
|
type WindowSpec struct {
|
|
node
|
|
|
|
Name model.CIStr
|
|
// Ref is the reference window of this specification. For example, in `w2 as (w1 order by a)`,
|
|
// the definition of `w2` references `w1`.
|
|
Ref model.CIStr
|
|
|
|
PartitionBy *PartitionByClause
|
|
OrderBy *OrderByClause
|
|
Frame *FrameClause
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *WindowSpec) Restore(ctx *RestoreCtx) error {
|
|
if name := n.Name.String(); name != "" {
|
|
ctx.WriteName(name)
|
|
ctx.WriteKeyWord(" AS ")
|
|
}
|
|
ctx.WritePlain("(")
|
|
sep := ""
|
|
if refName := n.Ref.String(); refName != "" {
|
|
ctx.WriteName(refName)
|
|
sep = " "
|
|
}
|
|
if n.PartitionBy != nil {
|
|
ctx.WritePlain(sep)
|
|
if err := n.PartitionBy.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore WindowSpec.PartitionBy")
|
|
}
|
|
sep = " "
|
|
}
|
|
if n.OrderBy != nil {
|
|
ctx.WritePlain(sep)
|
|
if err := n.OrderBy.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore WindowSpec.OrderBy")
|
|
}
|
|
sep = " "
|
|
}
|
|
if n.Frame != nil {
|
|
ctx.WritePlain(sep)
|
|
if err := n.Frame.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore WindowSpec.Frame")
|
|
}
|
|
}
|
|
ctx.WritePlain(")")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *WindowSpec) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*WindowSpec)
|
|
if n.PartitionBy != nil {
|
|
node, ok := n.PartitionBy.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.PartitionBy = node.(*PartitionByClause)
|
|
}
|
|
if n.OrderBy != nil {
|
|
node, ok := n.OrderBy.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.OrderBy = node.(*OrderByClause)
|
|
}
|
|
if n.Frame != nil {
|
|
node, ok := n.Frame.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Frame = node.(*FrameClause)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// PartitionByClause represents partition by clause.
|
|
type PartitionByClause struct {
|
|
node
|
|
|
|
Items []*ByItem
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *PartitionByClause) Restore(ctx *RestoreCtx) error {
|
|
ctx.WriteKeyWord("PARTITION BY ")
|
|
for i, v := range n.Items {
|
|
if i != 0 {
|
|
ctx.WritePlain(", ")
|
|
}
|
|
if err := v.Restore(ctx); err != nil {
|
|
return errors.Annotatef(err, "An error occurred while restore PartitionByClause.Items[%d]", i)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *PartitionByClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*PartitionByClause)
|
|
for i, val := range n.Items {
|
|
node, ok := val.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Items[i] = node.(*ByItem)
|
|
}
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// FrameType is the type of window function frame.
|
|
type FrameType int
|
|
|
|
// Window function frame types.
|
|
// MySQL only supports `ROWS` and `RANGES`.
|
|
const (
|
|
Rows = iota
|
|
Ranges
|
|
Groups
|
|
)
|
|
|
|
// FrameClause represents frame clause.
|
|
type FrameClause struct {
|
|
node
|
|
|
|
Type FrameType
|
|
Extent FrameExtent
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *FrameClause) Restore(ctx *RestoreCtx) error {
|
|
switch n.Type {
|
|
case Rows:
|
|
ctx.WriteKeyWord("ROWS")
|
|
case Ranges:
|
|
ctx.WriteKeyWord("RANGE")
|
|
default:
|
|
return errors.New("Unsupported window function frame type")
|
|
}
|
|
ctx.WriteKeyWord(" BETWEEN ")
|
|
if err := n.Extent.Start.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore FrameClause.Extent.Start")
|
|
}
|
|
ctx.WriteKeyWord(" AND ")
|
|
if err := n.Extent.End.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore FrameClause.Extent.End")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *FrameClause) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*FrameClause)
|
|
node, ok := n.Extent.Start.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Extent.Start = *node.(*FrameBound)
|
|
node, ok = n.Extent.End.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Extent.End = *node.(*FrameBound)
|
|
return v.Leave(n)
|
|
}
|
|
|
|
// FrameExtent represents frame extent.
|
|
type FrameExtent struct {
|
|
Start FrameBound
|
|
End FrameBound
|
|
}
|
|
|
|
// FrameType is the type of window function frame bound.
|
|
type BoundType int
|
|
|
|
// Frame bound types.
|
|
const (
|
|
Following = iota
|
|
Preceding
|
|
CurrentRow
|
|
)
|
|
|
|
// FrameBound represents frame bound.
|
|
type FrameBound struct {
|
|
node
|
|
|
|
Type BoundType
|
|
UnBounded bool
|
|
Expr ExprNode
|
|
// `Unit` is used to indicate the units in which the `Expr` should be interpreted.
|
|
// For example: '2:30' MINUTE_SECOND.
|
|
Unit ExprNode
|
|
}
|
|
|
|
// Restore implements Node interface.
|
|
func (n *FrameBound) Restore(ctx *RestoreCtx) error {
|
|
if n.UnBounded {
|
|
ctx.WriteKeyWord("UNBOUNDED")
|
|
}
|
|
switch n.Type {
|
|
case CurrentRow:
|
|
ctx.WriteKeyWord("CURRENT ROW")
|
|
case Preceding, Following:
|
|
if n.Unit != nil {
|
|
ctx.WriteKeyWord("INTERVAL ")
|
|
}
|
|
if n.Expr != nil {
|
|
if err := n.Expr.Restore(ctx); err != nil {
|
|
return errors.Annotate(err, "An error occurred while restore FrameBound.Expr")
|
|
}
|
|
}
|
|
if n.Unit != nil {
|
|
// Here the Unit string should not be quoted.
|
|
// TODO: This is a temporary workaround that should be changed once something like "Keyword Expression" is implemented.
|
|
var sb strings.Builder
|
|
n.Unit.Restore(NewRestoreCtx(0, &sb))
|
|
ctx.WritePlain(" ")
|
|
ctx.WriteKeyWord(sb.String())
|
|
}
|
|
if n.Type == Preceding {
|
|
ctx.WriteKeyWord(" PRECEDING")
|
|
} else {
|
|
ctx.WriteKeyWord(" FOLLOWING")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Accept implements Node Accept interface.
|
|
func (n *FrameBound) Accept(v Visitor) (Node, bool) {
|
|
newNode, skipChildren := v.Enter(n)
|
|
if skipChildren {
|
|
return v.Leave(newNode)
|
|
}
|
|
n = newNode.(*FrameBound)
|
|
if n.Expr != nil {
|
|
node, ok := n.Expr.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Expr = node.(ExprNode)
|
|
}
|
|
if n.Unit != nil {
|
|
node, ok := n.Unit.Accept(v)
|
|
if !ok {
|
|
return n, false
|
|
}
|
|
n.Unit = node.(ExprNode)
|
|
}
|
|
return v.Leave(n)
|
|
}
|