// 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/tidb/pkg/parser/auth"
	"github.com/pingcap/tidb/pkg/parser/format"
	"github.com/pingcap/tidb/pkg/parser/mysql"
	"github.com/pingcap/tidb/pkg/parser/terror"
	"github.com/pingcap/tidb/pkg/parser/tidb"
	"github.com/pingcap/tidb/pkg/parser/types"
)

var (
	_ DDLNode = &AlterTableStmt{}
	_ DDLNode = &AlterSequenceStmt{}
	_ DDLNode = &AlterPlacementPolicyStmt{}
	_ DDLNode = &AlterResourceGroupStmt{}
	_ DDLNode = &CreateDatabaseStmt{}
	_ DDLNode = &CreateIndexStmt{}
	_ DDLNode = &CreateTableStmt{}
	_ DDLNode = &CreateViewStmt{}
	_ DDLNode = &CreateSequenceStmt{}
	_ DDLNode = &CreatePlacementPolicyStmt{}
	_ DDLNode = &CreateMaskingPolicyStmt{}
	_ DDLNode = &CreateResourceGroupStmt{}
	_ DDLNode = &DropDatabaseStmt{}
	_ DDLNode = &FlashBackDatabaseStmt{}
	_ DDLNode = &DropIndexStmt{}
	_ DDLNode = &DropTableStmt{}
	_ DDLNode = &DropSequenceStmt{}
	_ DDLNode = &DropPlacementPolicyStmt{}
	_ DDLNode = &DropResourceGroupStmt{}
	_ DDLNode = &OptimizeTableStmt{}
	_ DDLNode = &RenameTableStmt{}
	_ DDLNode = &TruncateTableStmt{}
	_ DDLNode = &RepairTableStmt{}

	_ Node = &AlterTableSpec{}
	_ Node = &ColumnDef{}
	_ Node = &ColumnOption{}
	_ Node = &ColumnPosition{}
	_ Node = &Constraint{}
	_ Node = &IndexPartSpecification{}
	_ Node = &ReferenceDef{}
)

// CharsetOpt is used for parsing charset option from SQL.
type CharsetOpt struct {
	Chs string
	Col string
}

// NullString represents a string that may be nil.
type NullString struct {
	String string
	Empty  bool // Empty is true if String is empty backtick.
}

// DatabaseOptionType is the type for database options.
type DatabaseOptionType int

// Database option types.
const (
	DatabaseOptionNone DatabaseOptionType = iota
	DatabaseOptionCharset
	DatabaseOptionCollate
	DatabaseOptionEncryption
	DatabaseSetTiFlashReplica
	DatabaseOptionPlacementPolicy = DatabaseOptionType(PlacementOptionPolicy)
)

// DatabaseOption represents database option.
type DatabaseOption struct {
	Tp             DatabaseOptionType
	Value          string
	UintValue      uint64
	TiFlashReplica *TiFlashReplicaSpec
}

// Restore implements Node interface.
func (n *DatabaseOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case DatabaseOptionCharset:
		ctx.WriteKeyWord("CHARACTER SET")
		ctx.WritePlain(" = ")
		ctx.WritePlain(n.Value)
	case DatabaseOptionCollate:
		ctx.WriteKeyWord("COLLATE")
		ctx.WritePlain(" = ")
		ctx.WritePlain(n.Value)
	case DatabaseOptionEncryption:
		ctx.WriteKeyWord("ENCRYPTION")
		ctx.WritePlain(" = ")
		ctx.WriteString(n.Value)
	case DatabaseOptionPlacementPolicy:
		placementOpt := PlacementOption{
			Tp:        PlacementOptionPolicy,
			UintValue: n.UintValue,
			StrValue:  n.Value,
		}
		return placementOpt.Restore(ctx)
	case DatabaseSetTiFlashReplica:
		ctx.WriteKeyWord("SET TIFLASH REPLICA ")
		ctx.WritePlainf("%d", n.TiFlashReplica.Count)
		if len(n.TiFlashReplica.Labels) == 0 {
			break
		}
		ctx.WriteKeyWord(" LOCATION LABELS ")
		for i, v := range n.TiFlashReplica.Labels {
			if i > 0 {
				ctx.WritePlain(", ")
			}
			ctx.WriteString(v)
		}
	default:
		return errors.Errorf("invalid DatabaseOptionType: %d", n.Tp)
	}
	return nil
}

// CreateDatabaseStmt is a statement to create a database.
// See https://dev.mysql.com/doc/refman/5.7/en/create-database.html
type CreateDatabaseStmt struct {
	ddlNode

	IfNotExists bool
	Name        CIStr
	Options     []*DatabaseOption
}

// Restore implements Node interface.
func (n *CreateDatabaseStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE DATABASE ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	ctx.WriteName(n.Name.O)
	for i, option := range n.Options {
		ctx.WritePlain(" ")
		err := option.Restore(ctx)
		if err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreateDatabaseStmt DatabaseOption: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreateDatabaseStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateDatabaseStmt)
	return v.Leave(n)
}

// AlterDatabaseStmt is a statement to change the structure of a database.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-database.html
type AlterDatabaseStmt struct {
	ddlNode

	Name                 CIStr
	AlterDefaultDatabase bool
	Options              []*DatabaseOption
}

// Restore implements Node interface.
func (n *AlterDatabaseStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.isAllPlacementOptions() {
		return nil
	}
	// If all options placement options and RestoreTiDBSpecialComment flag is on,
	// we should restore the whole node in special comment. For example, the restore result should be:
	// /*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */
	// instead of
	// ALTER DATABASE `db1` /*T![placement] PLACEMENT POLICY = `p1` */
	// because altering a database without any options is not a legal syntax in mysql
	if n.isAllPlacementOptions() && ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restorePlacementStmtInSpecialComment(ctx, n)
	}

	ctx.WriteKeyWord("ALTER DATABASE")
	if !n.AlterDefaultDatabase {
		ctx.WritePlain(" ")
		ctx.WriteName(n.Name.O)
	}
	for i, option := range n.Options {
		ctx.WritePlain(" ")
		err := option.Restore(ctx)
		if err != nil {
			return errors.Annotatef(err, "An error occurred while splicing AlterDatabaseStmt DatabaseOption: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *AlterDatabaseStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterDatabaseStmt)
	return v.Leave(n)
}

func (n *AlterDatabaseStmt) isAllPlacementOptions() bool {
	for _, n := range n.Options {
		switch n.Tp {
		case DatabaseOptionPlacementPolicy:
		default:
			return false
		}
	}
	return true
}

// DropDatabaseStmt is a statement to drop a database and all tables in the database.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-database.html
type DropDatabaseStmt struct {
	ddlNode

	IfExists bool
	Name     CIStr
}

// Restore implements Node interface.
func (n *DropDatabaseStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DROP DATABASE ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	ctx.WriteName(n.Name.O)
	return nil
}

// Accept implements Node Accept interface.
func (n *DropDatabaseStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropDatabaseStmt)
	return v.Leave(n)
}

// FlashBackDatabaseStmt is a statement to restore a database and all tables in the database.
type FlashBackDatabaseStmt struct {
	ddlNode

	DBName  CIStr
	NewName string
}

// Restore implements Node interface.
func (n *FlashBackDatabaseStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("FLASHBACK DATABASE ")
	ctx.WriteName(n.DBName.O)
	if len(n.NewName) > 0 {
		ctx.WriteKeyWord(" TO ")
		ctx.WriteName(n.NewName)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *FlashBackDatabaseStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*FlashBackDatabaseStmt)
	return v.Leave(n)
}

// IndexPartSpecifications is used for parsing index column name or index expression from SQL.
type IndexPartSpecification struct {
	node

	Column *ColumnName
	Length int
	// Order is parsed but should be ignored because MySQL v5.7 doesn't support it.
	Desc bool
	Expr ExprNode
}

// Restore implements Node interface.
func (n *IndexPartSpecification) Restore(ctx *format.RestoreCtx) error {
	if n.Expr != nil {
		ctx.WritePlain("(")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications")
		}
		ctx.WritePlain(")")
		if n.Desc {
			ctx.WritePlain(" DESC")
		}
		return nil
	}
	if err := n.Column.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications")
	}
	if n.Length > 0 {
		ctx.WritePlainf("(%d)", n.Length)
	}
	if n.Desc {
		ctx.WritePlain(" DESC")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *IndexPartSpecification) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*IndexPartSpecification)
	if n.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
		return v.Leave(n)
	}
	node, ok := n.Column.Accept(v)
	if !ok {
		return n, false
	}
	n.Column = node.(*ColumnName)
	return v.Leave(n)
}

// MatchType is the type for reference match type.
type MatchType int

// match type
const (
	MatchNone MatchType = iota
	MatchFull
	MatchPartial
	MatchSimple
)

// ReferenceDef is used for parsing foreign key reference option from SQL.
// See http://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html
type ReferenceDef struct {
	node

	Table                   *TableName
	IndexPartSpecifications []*IndexPartSpecification
	OnDelete                *OnDeleteOpt
	OnUpdate                *OnUpdateOpt
	Match                   MatchType
}

// Restore implements Node interface.
func (n *ReferenceDef) Restore(ctx *format.RestoreCtx) error {
	if n.Table != nil {
		ctx.WriteKeyWord("REFERENCES ")
		if err := n.Table.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ReferenceDef")
		}
	}

	if n.IndexPartSpecifications != nil {
		ctx.WritePlain("(")
		for i, indexColNames := range n.IndexPartSpecifications {
			if i > 0 {
				ctx.WritePlain(", ")
			}
			if err := indexColNames.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while splicing IndexPartSpecifications: [%v]", i)
			}
		}
		ctx.WritePlain(")")
	}

	if n.Match != MatchNone {
		ctx.WriteKeyWord(" MATCH ")
		switch n.Match {
		case MatchFull:
			ctx.WriteKeyWord("FULL")
		case MatchPartial:
			ctx.WriteKeyWord("PARTIAL")
		case MatchSimple:
			ctx.WriteKeyWord("SIMPLE")
		}
	}
	if n.OnDelete.ReferOpt != ReferOptionNoOption {
		ctx.WritePlain(" ")
		if err := n.OnDelete.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing OnDelete")
		}
	}
	if n.OnUpdate.ReferOpt != ReferOptionNoOption {
		ctx.WritePlain(" ")
		if err := n.OnUpdate.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing OnUpdate")
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *ReferenceDef) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*ReferenceDef)
	if n.Table != nil {
		node, ok := n.Table.Accept(v)
		if !ok {
			return n, false
		}
		n.Table = node.(*TableName)
	}
	for i, val := range n.IndexPartSpecifications {
		node, ok := val.Accept(v)
		if !ok {
			return n, false
		}
		n.IndexPartSpecifications[i] = node.(*IndexPartSpecification)
	}
	onDelete, ok := n.OnDelete.Accept(v)
	if !ok {
		return n, false
	}
	n.OnDelete = onDelete.(*OnDeleteOpt)
	onUpdate, ok := n.OnUpdate.Accept(v)
	if !ok {
		return n, false
	}
	n.OnUpdate = onUpdate.(*OnUpdateOpt)
	return v.Leave(n)
}

// OnDeleteOpt is used for optional on delete clause.
type OnDeleteOpt struct {
	node
	ReferOpt ReferOptionType
}

// Restore implements Node interface.
func (n *OnDeleteOpt) Restore(ctx *format.RestoreCtx) error {
	if n.ReferOpt != ReferOptionNoOption {
		ctx.WriteKeyWord("ON DELETE ")
		ctx.WriteKeyWord(n.ReferOpt.String())
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *OnDeleteOpt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*OnDeleteOpt)
	return v.Leave(n)
}

// OnUpdateOpt is used for optional on update clause.
type OnUpdateOpt struct {
	node
	ReferOpt ReferOptionType
}

// Restore implements Node interface.
func (n *OnUpdateOpt) Restore(ctx *format.RestoreCtx) error {
	if n.ReferOpt != ReferOptionNoOption {
		ctx.WriteKeyWord("ON UPDATE ")
		ctx.WriteKeyWord(n.ReferOpt.String())
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *OnUpdateOpt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*OnUpdateOpt)
	return v.Leave(n)
}

// ColumnOptionType is the type for ColumnOption.
type ColumnOptionType int

// ColumnOption types.
const (
	ColumnOptionNoOption ColumnOptionType = iota
	ColumnOptionPrimaryKey
	ColumnOptionNotNull
	ColumnOptionAutoIncrement
	ColumnOptionDefaultValue
	ColumnOptionUniqKey
	ColumnOptionNull
	ColumnOptionOnUpdate // For Timestamp and Datetime only.
	ColumnOptionFulltext
	ColumnOptionComment
	ColumnOptionGenerated
	ColumnOptionReference
	ColumnOptionCollate
	ColumnOptionCheck
	ColumnOptionColumnFormat
	ColumnOptionStorage
	ColumnOptionAutoRandom
	ColumnOptionSecondaryEngineAttribute
)

var (
	invalidOptionForGeneratedColumn = map[ColumnOptionType]string{
		ColumnOptionAutoIncrement: "AUTO_INCREMENT",
		ColumnOptionOnUpdate:      "ON UPDATE",
		ColumnOptionDefaultValue:  "DEFAULT",
	}
)

// ColumnOptionList stores column options.
type ColumnOptionList struct {
	HasCollateOption bool
	Options          []*ColumnOption
}

// ColumnOption is used for parsing column constraint info from SQL.
type ColumnOption struct {
	node

	Tp ColumnOptionType
	// Expr is used for ColumnOptionDefaultValue/ColumnOptionOnUpdateColumnOptionGenerated.
	// For ColumnOptionDefaultValue or ColumnOptionOnUpdate, it's the target value.
	// For ColumnOptionGenerated, it's the target expression.
	Expr ExprNode
	// Stored is only for ColumnOptionGenerated, default is false.
	Stored bool
	// Refer is used for foreign key.
	Refer       *ReferenceDef
	StrValue    string
	AutoRandOpt AutoRandomOption
	// Enforced is only for Check, default is true.
	Enforced bool
	// Name is only used for Check Constraint name.
	ConstraintName      string
	PrimaryKeyTp        PrimaryKeyType
	SecondaryEngineAttr string
}

// Restore implements Node interface.
func (n *ColumnOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case ColumnOptionNoOption:
		return nil
	case ColumnOptionPrimaryKey:
		ctx.WriteKeyWord("PRIMARY KEY")
		pkTp := n.PrimaryKeyTp.String()
		if len(pkTp) != 0 {
			ctx.WritePlain(" ")
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDClusteredIndex, pkTp)
		}
		if n.StrValue == "Global" {
			ctx.WriteKeyWord(" GLOBAL")
		}
	case ColumnOptionNotNull:
		ctx.WriteKeyWord("NOT NULL")
	case ColumnOptionAutoIncrement:
		ctx.WriteKeyWord("AUTO_INCREMENT")
	case ColumnOptionDefaultValue:
		ctx.WriteKeyWord("DEFAULT ")
		printOuterParentheses := false
		if funcCallExpr, ok := n.Expr.(*FuncCallExpr); ok {
			if name := funcCallExpr.FnName.L; name != CurrentTimestamp {
				printOuterParentheses = true
			}
		}
		if _, ok := n.Expr.(*ColumnNameExpr); ok {
			printOuterParentheses = true
		}
		if printOuterParentheses {
			ctx.WritePlain("(")
		}
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnOption DefaultValue Expr")
		}
		if printOuterParentheses {
			ctx.WritePlain(")")
		}
	case ColumnOptionUniqKey:
		ctx.WriteKeyWord("UNIQUE KEY")
		if n.StrValue == "Global" {
			ctx.WriteKeyWord(" GLOBAL")
		}
	case ColumnOptionNull:
		ctx.WriteKeyWord("NULL")
	case ColumnOptionOnUpdate:
		ctx.WriteKeyWord("ON UPDATE ")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnOption ON UPDATE Expr")
		}
	case ColumnOptionFulltext:
		return errors.New("TiDB Parser ignore the `ColumnOptionFulltext` type now")
	case ColumnOptionComment:
		ctx.WriteKeyWord("COMMENT ")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnOption COMMENT Expr")
		}
	case ColumnOptionGenerated:
		ctx.WriteKeyWord("GENERATED ALWAYS AS")
		ctx.WritePlain("(")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnOption GENERATED ALWAYS Expr")
		}
		ctx.WritePlain(")")
		if n.Stored {
			ctx.WriteKeyWord(" STORED")
		} else {
			ctx.WriteKeyWord(" VIRTUAL")
		}
	case ColumnOptionReference:
		if err := n.Refer.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnOption ReferenceDef")
		}
	case ColumnOptionCollate:
		if n.StrValue == "" {
			return errors.New("Empty ColumnOption COLLATE")
		}
		ctx.WriteKeyWord("COLLATE ")
		ctx.WritePlain(n.StrValue)
	case ColumnOptionCheck:
		if n.ConstraintName != "" {
			ctx.WriteKeyWord("CONSTRAINT ")
			ctx.WriteName(n.ConstraintName)
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("CHECK")
		ctx.WritePlain("(")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Trace(err)
		}
		ctx.WritePlain(")")
		if n.Enforced {
			ctx.WriteKeyWord(" ENFORCED")
		} else {
			ctx.WriteKeyWord(" NOT ENFORCED")
		}
	case ColumnOptionColumnFormat:
		ctx.WriteKeyWord("COLUMN_FORMAT ")
		ctx.WriteKeyWord(n.StrValue)
	case ColumnOptionStorage:
		ctx.WriteKeyWord("STORAGE ")
		ctx.WriteKeyWord(n.StrValue)
	case ColumnOptionAutoRandom:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandom, func() error {
			ctx.WriteKeyWord("AUTO_RANDOM")
			opt := n.AutoRandOpt
			if opt.ShardBits != types.UnspecifiedLength {
				if opt.RangeBits != types.UnspecifiedLength {
					ctx.WritePlainf("(%d, %d)", opt.ShardBits, opt.RangeBits)
				} else {
					ctx.WritePlainf("(%d)", opt.ShardBits)
				}
			}
			return nil
		})
	case ColumnOptionSecondaryEngineAttribute:
		ctx.WriteKeyWord("SECONDARY_ENGINE_ATTRIBUTE")
		ctx.WritePlain(" = ")
		ctx.WriteString(n.StrValue)
	default:
		return errors.New("An error occurred while splicing ColumnOption")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *ColumnOption) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*ColumnOption)
	if n.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
	}
	return v.Leave(n)
}

// AutoRandomOption contains the length of shard bits and range bits.
type AutoRandomOption struct {
	// ShardBits is the number of bits used to store the shard.
	ShardBits int
	// RangeBits is the number of int primary key bits that will be used by TiDB.
	RangeBits int
}

// IndexVisibility is the option for index visibility.
type IndexVisibility int

// IndexVisibility options.
const (
	IndexVisibilityDefault IndexVisibility = iota
	IndexVisibilityVisible
	IndexVisibilityInvisible
)

// IndexOption is the index options.
//
//	  KEY_BLOCK_SIZE [=] value
//	| index_type
//	| WITH PARSER parser_name
//	| COMMENT 'string'
//	| GLOBAL
//
// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html
// with the addition of Global Index
type IndexOption struct {
	node

	KeyBlockSize               uint64
	Tp                         IndexType
	Comment                    string
	ParserName                 CIStr
	Visibility                 IndexVisibility
	PrimaryKeyTp               PrimaryKeyType
	Global                     bool
	SplitOpt                   *SplitOption `json:"-"` // SplitOption contains expr nodes, which cannot marshal for DDL job arguments.
	SecondaryEngineAttr        string
	AddColumnarReplicaOnDemand int
	Condition                  ExprNode `json:"-"` // Condition contains expr nodes, which cannot marshal for DDL job arguments. It's used for partial index.
}

// IsEmpty is true if only default options are given
// and it should not be added to the output
func (n *IndexOption) IsEmpty() bool {
	if n.PrimaryKeyTp != PrimaryKeyTypeDefault ||
		n.KeyBlockSize > 0 ||
		n.Tp != IndexTypeInvalid ||
		len(n.ParserName.O) > 0 ||
		n.Comment != "" ||
		n.Global ||
		n.Visibility != IndexVisibilityDefault ||
		n.SplitOpt != nil ||
		len(n.SecondaryEngineAttr) > 0 ||
		n.Condition != nil {
		return false
	}
	return true
}

// Restore implements Node interface.
func (n *IndexOption) Restore(ctx *format.RestoreCtx) error {
	hasPrevOption := false

	if n.AddColumnarReplicaOnDemand > 0 {
		ctx.WriteKeyWord("ADD_COLUMNAR_REPLICA_ON_DEMAND")
		hasPrevOption = true
	}

	if n.PrimaryKeyTp != PrimaryKeyTypeDefault {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDClusteredIndex, n.PrimaryKeyTp.String())
		hasPrevOption = true
	}
	if n.KeyBlockSize > 0 {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("KEY_BLOCK_SIZE")
		ctx.WritePlainf("=%d", n.KeyBlockSize)
		hasPrevOption = true
	}

	if n.Tp != IndexTypeInvalid {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("USING ")
		ctx.WritePlain(n.Tp.String())
		hasPrevOption = true
	}

	if len(n.ParserName.O) > 0 {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("WITH PARSER ")
		ctx.WriteName(n.ParserName.O)
		hasPrevOption = true
	}

	if n.Comment != "" {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("COMMENT ")
		ctx.WriteString(n.Comment)
		hasPrevOption = true
	}

	if n.Global {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDGlobalIndex, func() error {
			ctx.WriteKeyWord("GLOBAL")
			return nil
		})
		hasPrevOption = true
	}

	if n.Visibility != IndexVisibilityDefault {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		switch n.Visibility {
		case IndexVisibilityVisible:
			ctx.WriteKeyWord("VISIBLE")
		case IndexVisibilityInvisible:
			ctx.WriteKeyWord("INVISIBLE")
		}
		hasPrevOption = true
	}

	if n.SplitOpt != nil {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		err := ctx.WriteWithSpecialComments(tidb.FeatureIDPresplit, func() error {
			ctx.WriteKeyWord("PRE_SPLIT_REGIONS")
			ctx.WritePlain(" = ")
			if n.SplitOpt.Num != 0 && len(n.SplitOpt.Lower) == 0 {
				ctx.WritePlainf("%d", n.SplitOpt.Num)
			} else {
				ctx.WritePlain("(")
				if err := n.SplitOpt.Restore(ctx); err != nil {
					return errors.Annotate(err, "An error occurred while splicing IndexOption SplitOpt")
				}
				ctx.WritePlain(")")
			}
			return nil
		})
		if err != nil {
			return err
		}
		hasPrevOption = true
	}

	if n.SecondaryEngineAttr != "" {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("SECONDARY_ENGINE_ATTRIBUTE")
		ctx.WritePlain(" = ")
		ctx.WriteString(n.SecondaryEngineAttr)
		// If a new option is added after, please also uncomment:
		//hasPrevOption = true
	}

	if n.Condition != nil {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("WHERE ")
		if err := n.Condition.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing IndexOption Condition")
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *IndexOption) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*IndexOption)
	if n.SplitOpt != nil {
		node, ok := n.SplitOpt.Accept(v)
		if !ok {
			return n, false
		}
		n.SplitOpt = node.(*SplitOption)
	}
	return v.Leave(n)
}

// ConstraintType is the type for Constraint.
type ConstraintType int

// ConstraintTypes
const (
	ConstraintNoConstraint ConstraintType = iota
	ConstraintPrimaryKey
	ConstraintKey
	ConstraintIndex
	ConstraintUniq
	ConstraintUniqKey
	ConstraintUniqIndex
	ConstraintForeignKey
	// ConstraintFulltext is only used in AST.
	// It will be rewritten into ConstraintIndex after preprocessor phase.
	ConstraintFulltext
	ConstraintCheck
	// ConstraintVector is only used in AST.
	// It will be rewritten into ConstraintColumnar after preprocessor phase.
	ConstraintVector
	ConstraintColumnar
)

// Constraint is constraint for table definition.
type Constraint struct {
	node

	// only supported by MariaDB 10.0.2+ (ADD {INDEX|KEY}, ADD FOREIGN KEY),
	// see https://mariadb.com/kb/en/library/alter-table/
	IfNotExists bool

	Tp   ConstraintType
	Name string

	Keys []*IndexPartSpecification // Used for PRIMARY KEY, UNIQUE, ......

	Refer *ReferenceDef // Used for foreign key.

	Option *IndexOption // Index Options

	Expr ExprNode // Used for Check

	Enforced bool // Used for Check

	InColumn bool // Used for Check

	InColumnName string // Used for Check
	IsEmptyIndex bool   // Used for Check
}

// Restore implements Node interface.
func (n *Constraint) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case ConstraintNoConstraint:
		return nil
	case ConstraintPrimaryKey:
		ctx.WriteKeyWord("PRIMARY KEY")
	case ConstraintKey:
		ctx.WriteKeyWord("KEY")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, " IF NOT EXISTS")
		}
	case ConstraintIndex:
		ctx.WriteKeyWord("INDEX")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, " IF NOT EXISTS")
		}
	case ConstraintUniq:
		ctx.WriteKeyWord("UNIQUE")
	case ConstraintUniqKey:
		ctx.WriteKeyWord("UNIQUE KEY")
	case ConstraintUniqIndex:
		ctx.WriteKeyWord("UNIQUE INDEX")
	case ConstraintFulltext:
		ctx.WriteKeyWord("FULLTEXT")
	case ConstraintCheck:
		if n.Name != "" {
			ctx.WriteKeyWord("CONSTRAINT ")
			ctx.WriteName(n.Name)
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("CHECK")
		ctx.WritePlain("(")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Trace(err)
		}
		ctx.WritePlain(") ")
		if n.Enforced {
			ctx.WriteKeyWord("ENFORCED")
		} else {
			ctx.WriteKeyWord("NOT ENFORCED")
		}
		return nil
	case ConstraintVector:
		ctx.WriteKeyWord("VECTOR INDEX")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, " IF NOT EXISTS")
		}
	case ConstraintColumnar:
		ctx.WriteKeyWord("COLUMNAR INDEX")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, " IF NOT EXISTS")
		}
	}

	if n.Tp == ConstraintForeignKey {
		ctx.WriteKeyWord("CONSTRAINT ")
		if n.Name != "" {
			ctx.WriteName(n.Name)
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("FOREIGN KEY ")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF NOT EXISTS ")
		}
	} else if n.Name != "" || n.IsEmptyIndex {
		ctx.WritePlain(" ")
		ctx.WriteName(n.Name)
	}

	ctx.WritePlain("(")
	for i, keys := range n.Keys {
		if i > 0 {
			ctx.WritePlain(", ")
		}
		if err := keys.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing Constraint Keys: [%v]", i)
		}
	}
	ctx.WritePlain(")")

	if n.Refer != nil {
		ctx.WritePlain(" ")
		if err := n.Refer.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing Constraint Refer")
		}
	}

	if n.Option != nil && !n.Option.IsEmpty() {
		ctx.WritePlain(" ")
		if err := n.Option.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing Constraint Option")
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *Constraint) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*Constraint)
	for i, val := range n.Keys {
		node, ok := val.Accept(v)
		if !ok {
			return n, false
		}
		n.Keys[i] = node.(*IndexPartSpecification)
	}
	if n.Refer != nil {
		node, ok := n.Refer.Accept(v)
		if !ok {
			return n, false
		}
		n.Refer = node.(*ReferenceDef)
	}
	if n.Option != nil {
		node, ok := n.Option.Accept(v)
		if !ok {
			return n, false
		}
		n.Option = node.(*IndexOption)
	}
	if n.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
	}
	return v.Leave(n)
}

// ColumnDef is used for parsing column definition from SQL.
type ColumnDef struct {
	node

	Name    *ColumnName
	Tp      *types.FieldType
	Options []*ColumnOption
}

// Restore implements Node interface.
func (n *ColumnDef) Restore(ctx *format.RestoreCtx) error {
	if err := n.Name.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while splicing ColumnDef Name")
	}
	if n.Tp != nil {
		ctx.WritePlain(" ")
		if err := n.Tp.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing ColumnDef Type")
		}
	}
	for i, options := range n.Options {
		ctx.WritePlain(" ")
		if err := options.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing ColumnDef ColumnOption: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *ColumnDef) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*ColumnDef)
	node, ok := n.Name.Accept(v)
	if !ok {
		return n, false
	}
	n.Name = node.(*ColumnName)
	for i, val := range n.Options {
		node, ok := val.Accept(v)
		if !ok {
			return n, false
		}
		n.Options[i] = node.(*ColumnOption)
	}
	return v.Leave(n)
}

// Validate checks if a column definition is legal.
// For example, generated column definitions that contain such
// column options as `ON UPDATE`, `AUTO_INCREMENT`, `DEFAULT`
// are illegal.
func (n *ColumnDef) Validate() error {
	generatedCol := false
	var illegalOpt4gc string
	for _, opt := range n.Options {
		if opt.Tp == ColumnOptionGenerated {
			generatedCol = true
		}
		msg, found := invalidOptionForGeneratedColumn[opt.Tp]
		if found {
			illegalOpt4gc = msg
		}
	}
	if generatedCol && illegalOpt4gc != "" {
		return ErrWrongUsage.GenWithStackByArgs(illegalOpt4gc, "generated column")
	}
	return nil
}

type TemporaryKeyword int

const (
	TemporaryNone TemporaryKeyword = iota
	TemporaryGlobal
	TemporaryLocal
)

// CreateTableStmt is a statement to create a table.
// See https://dev.mysql.com/doc/refman/5.7/en/create-table.html
type CreateTableStmt struct {
	ddlNode

	IfNotExists bool
	TemporaryKeyword
	// Meanless when TemporaryKeyword is not TemporaryGlobal.
	// ON COMMIT DELETE ROWS => true
	// ON COMMIT PRESERVE ROW => false
	OnCommitDelete bool
	Table          *TableName
	ReferTable     *TableName
	Cols           []*ColumnDef
	Constraints    []*Constraint
	SplitIndex     []*SplitIndexOption
	Options        []*TableOption
	Partition      *PartitionOptions
	OnDuplicate    OnDuplicateKeyHandlingType
	Select         ResultSetNode
}

// Restore implements Node interface.
func (n *CreateTableStmt) Restore(ctx *format.RestoreCtx) error {
	switch n.TemporaryKeyword {
	case TemporaryNone:
		ctx.WriteKeyWord("CREATE TABLE ")
	case TemporaryGlobal:
		ctx.WriteKeyWord("CREATE GLOBAL TEMPORARY TABLE ")
	case TemporaryLocal:
		ctx.WriteKeyWord("CREATE TEMPORARY TABLE ")
	}
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}

	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Table")
	}

	if n.ReferTable != nil {
		ctx.WriteKeyWord(" LIKE ")
		if err := n.ReferTable.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing CreateTableStmt ReferTable")
		}
	}
	lenCols := len(n.Cols)
	lenConstraints := len(n.Constraints)
	if lenCols+lenConstraints > 0 {
		ctx.WritePlain(" (")
		for i, col := range n.Cols {
			if i > 0 {
				ctx.WritePlain(",")
			}
			if err := col.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt ColumnDef: [%v]", i)
			}
		}
		for i, constraint := range n.Constraints {
			if i > 0 || lenCols >= 1 {
				ctx.WritePlain(",")
			}
			if err := constraint.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt Constraints: [%v]", i)
			}
		}
		ctx.WritePlain(")")
	}

	options := tableOptionsWithRestoreTTLFlag(ctx.Flags, n.Options)
	for i, option := range options {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt TableOption: [%v]", i)
		}
	}

	if n.Partition != nil {
		ctx.WritePlain(" ")
		if err := n.Partition.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Partition")
		}
	}

	for _, opt := range n.SplitIndex {
		ctx.WritePlain(" ")
		if err := opt.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing CreateTableStmt SplitIndex")
		}
	}

	if n.Select != nil {
		switch n.OnDuplicate {
		case OnDuplicateKeyHandlingError:
			ctx.WriteKeyWord(" AS ")
		case OnDuplicateKeyHandlingIgnore:
			ctx.WriteKeyWord(" IGNORE AS ")
		case OnDuplicateKeyHandlingReplace:
			ctx.WriteKeyWord(" REPLACE AS ")
		}

		if err := n.Select.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Select")
		}
	}

	if n.TemporaryKeyword == TemporaryGlobal {
		if n.OnCommitDelete {
			ctx.WriteKeyWord(" ON COMMIT DELETE ROWS")
		} else {
			ctx.WriteKeyWord(" ON COMMIT PRESERVE ROWS")
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *CreateTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateTableStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	if n.ReferTable != nil {
		node, ok = n.ReferTable.Accept(v)
		if !ok {
			return n, false
		}
		n.ReferTable = node.(*TableName)
	}
	for i, val := range n.Cols {
		node, ok = val.Accept(v)
		if !ok {
			return n, false
		}
		n.Cols[i] = node.(*ColumnDef)
	}
	for i, val := range n.Constraints {
		node, ok = val.Accept(v)
		if !ok {
			return n, false
		}
		n.Constraints[i] = node.(*Constraint)
	}
	for i, val := range n.SplitIndex {
		node, ok = val.Accept(v)
		if !ok {
			return n, false
		}
		n.SplitIndex[i] = node.(*SplitIndexOption)
	}
	if n.Select != nil {
		node, ok := n.Select.Accept(v)
		if !ok {
			return n, false
		}
		n.Select = node.(ResultSetNode)
	}
	if n.Partition != nil {
		node, ok := n.Partition.Accept(v)
		if !ok {
			return n, false
		}
		n.Partition = node.(*PartitionOptions)
	}
	for i, option := range n.Options {
		node, ok = option.Accept(v)
		if !ok {
			return n, false
		}
		n.Options[i] = node.(*TableOption)
	}

	return v.Leave(n)
}

// DropTableStmt is a statement to drop one or more tables.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-table.html
type DropTableStmt struct {
	ddlNode

	IfExists         bool
	Tables           []*TableName
	IsView           bool
	TemporaryKeyword // make sense ONLY if/when IsView == false
}

// Restore implements Node interface.
func (n *DropTableStmt) Restore(ctx *format.RestoreCtx) error {
	if n.IsView {
		ctx.WriteKeyWord("DROP VIEW ")
	} else {
		switch n.TemporaryKeyword {
		case TemporaryNone:
			ctx.WriteKeyWord("DROP TABLE ")
		case TemporaryGlobal:
			ctx.WriteKeyWord("DROP GLOBAL TEMPORARY TABLE ")
		case TemporaryLocal:
			ctx.WriteKeyWord("DROP TEMPORARY TABLE ")
		}
	}
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}

	for index, table := range n.Tables {
		if index != 0 {
			ctx.WritePlain(", ")
		}
		if err := table.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index)
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *DropTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropTableStmt)
	for i, val := range n.Tables {
		node, ok := val.Accept(v)
		if !ok {
			return n, false
		}
		n.Tables[i] = node.(*TableName)
	}
	return v.Leave(n)
}

// DropPlacementPolicyStmt is a statement to drop a Policy.
type DropPlacementPolicyStmt struct {
	ddlNode

	IfExists   bool
	PolicyName CIStr
}

// Restore implements Restore interface.
func (n *DropPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restorePlacementStmtInSpecialComment(ctx, n)
	}

	ctx.WriteKeyWord("DROP PLACEMENT POLICY ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	ctx.WriteName(n.PolicyName.O)
	return nil
}

func (n *DropPlacementPolicyStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropPlacementPolicyStmt)
	return v.Leave(n)
}

type DropResourceGroupStmt struct {
	ddlNode

	IfExists          bool
	ResourceGroupName CIStr
}

// Restore implements Restore interface.
func (n *DropResourceGroupStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup)
	}

	ctx.WriteKeyWord("DROP RESOURCE GROUP ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	ctx.WriteName(n.ResourceGroupName.O)
	return nil
}

func (n *DropResourceGroupStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropResourceGroupStmt)
	return v.Leave(n)
}

type OptimizeTableStmt struct {
	ddlNode

	NoWriteToBinLog bool
	Tables          []*TableName
}

func (n *OptimizeTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("OPTIMIZE ")
	if n.NoWriteToBinLog {
		ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ")
	}
	ctx.WriteKeyWord("TABLE ")

	for index, table := range n.Tables {
		if index != 0 {
			ctx.WritePlain(", ")
		}
		if err := table.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore OptimizeTableStmt.Tables[%d]", index)
		}
	}
	return nil
}

func (n *OptimizeTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*OptimizeTableStmt)
	return v.Leave(n)
}

// DropSequenceStmt is a statement to drop a Sequence.
type DropSequenceStmt struct {
	ddlNode

	IfExists  bool
	Sequences []*TableName
}

// Restore implements Node interface.
func (n *DropSequenceStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DROP SEQUENCE ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	for i, sequence := range n.Sequences {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if err := sequence.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore DropSequenceStmt.Sequences[%d]", i)
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *DropSequenceStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropSequenceStmt)
	for i, val := range n.Sequences {
		node, ok := val.Accept(v)
		if !ok {
			return n, false
		}
		n.Sequences[i] = node.(*TableName)
	}
	return v.Leave(n)
}

// RenameTableStmt is a statement to rename a table.
// See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html
type RenameTableStmt struct {
	ddlNode

	TableToTables []*TableToTable
}

// Restore implements Node interface.
func (n *RenameTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("RENAME TABLE ")
	for index, table2table := range n.TableToTables {
		if index != 0 {
			ctx.WritePlain(", ")
		}
		if err := table2table.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore RenameTableStmt.TableToTables")
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *RenameTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*RenameTableStmt)

	for i, t := range n.TableToTables {
		node, ok := t.Accept(v)
		if !ok {
			return n, false
		}
		n.TableToTables[i] = node.(*TableToTable)
	}

	return v.Leave(n)
}

// TableToTable represents renaming old table to new table used in RenameTableStmt.
type TableToTable struct {
	node
	OldTable *TableName
	NewTable *TableName
}

// Restore implements Node interface.
func (n *TableToTable) Restore(ctx *format.RestoreCtx) error {
	if err := n.OldTable.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore TableToTable.OldTable")
	}
	ctx.WriteKeyWord(" TO ")
	if err := n.NewTable.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore TableToTable.NewTable")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *TableToTable) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*TableToTable)
	node, ok := n.OldTable.Accept(v)
	if !ok {
		return n, false
	}
	n.OldTable = node.(*TableName)
	node, ok = n.NewTable.Accept(v)
	if !ok {
		return n, false
	}
	n.NewTable = node.(*TableName)
	return v.Leave(n)
}

// CreateViewStmt is a statement to create a View.
// See https://dev.mysql.com/doc/refman/5.7/en/create-view.html
type CreateViewStmt struct {
	ddlNode

	OrReplace   bool
	ViewName    *TableName
	Cols        []CIStr
	Select      StmtNode
	SchemaCols  []CIStr
	Algorithm   ViewAlgorithm
	Definer     *auth.UserIdentity
	Security    ViewSecurity
	CheckOption ViewCheckOption
}

// Restore implements Node interface.
func (n *CreateViewStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE ")
	if n.OrReplace {
		ctx.WriteKeyWord("OR REPLACE ")
	}
	ctx.WriteKeyWord("ALGORITHM")
	ctx.WritePlain(" = ")
	ctx.WriteKeyWord(n.Algorithm.String())
	ctx.WriteKeyWord(" DEFINER")
	ctx.WritePlain(" = ")

	// todo Use n.Definer.Restore(ctx) to replace this part
	if n.Definer.CurrentUser {
		ctx.WriteKeyWord("current_user")
	} else {
		ctx.WriteName(n.Definer.Username)
		if n.Definer.Hostname != "" {
			ctx.WritePlain("@")
			ctx.WriteName(n.Definer.Hostname)
		}
	}

	ctx.WriteKeyWord(" SQL SECURITY ")
	ctx.WriteKeyWord(n.Security.String())
	ctx.WriteKeyWord(" VIEW ")

	if err := n.ViewName.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while create CreateViewStmt.ViewName")
	}

	for i, col := range n.Cols {
		if i == 0 {
			ctx.WritePlain(" (")
		} else {
			ctx.WritePlain(",")
		}
		ctx.WriteName(col.O)
		if i == len(n.Cols)-1 {
			ctx.WritePlain(")")
		}
	}

	ctx.WriteKeyWord(" AS ")

	if err := n.Select.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while create CreateViewStmt.Select")
	}

	if n.CheckOption != CheckOptionCascaded {
		ctx.WriteKeyWord(" WITH ")
		ctx.WriteKeyWord(n.CheckOption.String())
		ctx.WriteKeyWord(" CHECK OPTION")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreateViewStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateViewStmt)
	node, ok := n.ViewName.Accept(v)
	if !ok {
		return n, false
	}
	n.ViewName = node.(*TableName)
	selnode, ok := n.Select.Accept(v)
	if !ok {
		return n, false
	}
	n.Select = selnode.(StmtNode)
	return v.Leave(n)
}

// CreatePlacementPolicyStmt is a statement to create a policy.
type CreatePlacementPolicyStmt struct {
	ddlNode

	OrReplace        bool
	IfNotExists      bool
	PolicyName       CIStr
	PlacementOptions []*PlacementOption
}

// Restore implements Node interface.
func (n *CreatePlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restorePlacementStmtInSpecialComment(ctx, n)
	}

	ctx.WriteKeyWord("CREATE ")
	if n.OrReplace {
		ctx.WriteKeyWord("OR REPLACE ")
	}
	ctx.WriteKeyWord("PLACEMENT POLICY ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	ctx.WriteName(n.PolicyName.O)
	for i, option := range n.PlacementOptions {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreatePlacementPolicy TableOption: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreatePlacementPolicyStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreatePlacementPolicyStmt)
	return v.Leave(n)
}

// MaskingPolicyState represents the optional ENABLE/DISABLE state of a masking policy.
type MaskingPolicyState struct {
	Enabled  bool
	Explicit bool
}

// MaskingPolicyRestrictOps is a bitmask of operations restricted by a masking policy.
type MaskingPolicyRestrictOps uint64

// Masking policy restricted operation values.
const (
	MaskingPolicyRestrictOpNone MaskingPolicyRestrictOps = 0

	MaskingPolicyRestrictOpInsertIntoSelect MaskingPolicyRestrictOps = 1 << iota
	MaskingPolicyRestrictOpUpdateSelect
	MaskingPolicyRestrictOpDeleteSelect
	MaskingPolicyRestrictOpCTAS
)

// Masking policy restricted operation names.
const (
	MaskingPolicyRestrictNameInsertIntoSelect = "INSERT_INTO_SELECT"
	MaskingPolicyRestrictNameUpdateSelect     = "UPDATE_SELECT"
	MaskingPolicyRestrictNameDeleteSelect     = "DELETE_SELECT"
	MaskingPolicyRestrictNameCTAS             = "CTAS"
)

func (ops MaskingPolicyRestrictOps) names() []string {
	if ops == MaskingPolicyRestrictOpNone {
		return nil
	}

	names := make([]string, 0, 4)
	if ops&MaskingPolicyRestrictOpInsertIntoSelect != 0 {
		names = append(names, MaskingPolicyRestrictNameInsertIntoSelect)
	}
	if ops&MaskingPolicyRestrictOpUpdateSelect != 0 {
		names = append(names, MaskingPolicyRestrictNameUpdateSelect)
	}
	if ops&MaskingPolicyRestrictOpDeleteSelect != 0 {
		names = append(names, MaskingPolicyRestrictNameDeleteSelect)
	}
	if ops&MaskingPolicyRestrictOpCTAS != 0 {
		names = append(names, MaskingPolicyRestrictNameCTAS)
	}
	return names
}

func restoreMaskingPolicyRestrictOn(ctx *format.RestoreCtx, ops MaskingPolicyRestrictOps, writeNone bool) {
	if ops == MaskingPolicyRestrictOpNone {
		if !writeNone {
			return
		}
		ctx.WriteKeyWord("RESTRICT ON NONE")
		return
	}

	ctx.WriteKeyWord("RESTRICT ON (")
	for i, name := range ops.names() {
		if i > 0 {
			ctx.WritePlain(", ")
		}
		ctx.WriteKeyWord(name)
	}
	ctx.WritePlain(")")
}

// CreateMaskingPolicyStmt is a statement to create a masking policy.
type CreateMaskingPolicyStmt struct {
	ddlNode

	OrReplace          bool
	IfNotExists        bool
	PolicyName         CIStr
	Table              *TableName
	Column             *ColumnName
	Expr               ExprNode
	RestrictOps        MaskingPolicyRestrictOps
	MaskingPolicyState MaskingPolicyState
}

// Restore implements Node interface.
func (n *CreateMaskingPolicyStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE ")
	if n.OrReplace {
		ctx.WriteKeyWord("OR REPLACE ")
	}
	ctx.WriteKeyWord("MASKING POLICY ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	ctx.WriteName(n.PolicyName.O)
	ctx.WriteKeyWord(" ON ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore CreateMaskingPolicyStmt.Table")
	}
	ctx.WritePlain(" (")
	if err := n.Column.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore CreateMaskingPolicyStmt.Column")
	}
	ctx.WritePlain(") ")
	ctx.WriteKeyWord("AS ")
	if err := n.Expr.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore CreateMaskingPolicyStmt.Expr")
	}
	if n.RestrictOps != MaskingPolicyRestrictOpNone {
		ctx.WritePlain(" ")
		restoreMaskingPolicyRestrictOn(ctx, n.RestrictOps, false)
	}
	if n.MaskingPolicyState.Explicit {
		ctx.WritePlain(" ")
		if n.MaskingPolicyState.Enabled {
			ctx.WriteKeyWord("ENABLE")
		} else {
			ctx.WriteKeyWord("DISABLE")
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreateMaskingPolicyStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateMaskingPolicyStmt)
	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.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
	}
	return v.Leave(n)
}

// CreateResourceGroupStmt is a statement to create a policy.
type CreateResourceGroupStmt struct {
	ddlNode

	IfNotExists             bool
	ResourceGroupName       CIStr
	ResourceGroupOptionList []*ResourceGroupOption
}

// Restore implements Node interface.
func (n *CreateResourceGroupStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup)
	}

	ctx.WriteKeyWord("CREATE ")

	ctx.WriteKeyWord("RESOURCE GROUP ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	ctx.WriteName(n.ResourceGroupName.O)
	for i, option := range n.ResourceGroupOptionList {
		if i > 0 {
			ctx.WritePlain(",")
		}
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreateResourceGroupStmt Option: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreateResourceGroupStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateResourceGroupStmt)
	return v.Leave(n)
}

// CreateSequenceStmt is a statement to create a Sequence.
type CreateSequenceStmt struct {
	ddlNode

	// TODO : support or replace if need : care for it will conflict on temporaryOpt.
	IfNotExists bool
	Name        *TableName
	SeqOptions  []*SequenceOption
	TblOptions  []*TableOption
}

// Restore implements Node interface.
func (n *CreateSequenceStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE ")
	ctx.WriteKeyWord("SEQUENCE ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	if err := n.Name.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while create CreateSequenceStmt.Name")
	}
	for i, option := range n.SeqOptions {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt SequenceOption: [%v]", i)
		}
	}
	for i, option := range n.TblOptions {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt TableOption: [%v]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *CreateSequenceStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateSequenceStmt)
	node, ok := n.Name.Accept(v)
	if !ok {
		return n, false
	}
	n.Name = node.(*TableName)
	return v.Leave(n)
}

// IndexLockAndAlgorithm stores the algorithm option and the lock option.
type IndexLockAndAlgorithm struct {
	node

	LockTp      LockType
	AlgorithmTp AlgorithmType
}

// Restore implements Node interface.
func (n *IndexLockAndAlgorithm) Restore(ctx *format.RestoreCtx) error {
	hasPrevOption := false
	if n.AlgorithmTp != AlgorithmTypeDefault {
		ctx.WriteKeyWord("ALGORITHM")
		ctx.WritePlain(" = ")
		ctx.WriteKeyWord(n.AlgorithmTp.String())
		hasPrevOption = true
	}

	if n.LockTp != LockTypeDefault {
		if hasPrevOption {
			ctx.WritePlain(" ")
		}
		ctx.WriteKeyWord("LOCK")
		ctx.WritePlain(" = ")
		ctx.WriteKeyWord(n.LockTp.String())
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *IndexLockAndAlgorithm) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*IndexLockAndAlgorithm)
	return v.Leave(n)
}

// IndexKeyType is the type for index key.
type IndexKeyType int

// Index key types.
const (
	IndexKeyTypeNone IndexKeyType = iota
	IndexKeyTypeUnique
	IndexKeyTypeSpatial
	// IndexKeyTypeFulltext is only used in AST.
	// It will be rewritten into IndexKeyTypeFulltext after preprocessor phase.
	IndexKeyTypeFulltext
	// IndexKeyTypeVector is only used in AST.
	// It will be rewritten into IndexKeyTypeColumnar after preprocessor phase.
	IndexKeyTypeVector
	IndexKeyTypeColumnar
)

// CreateIndexStmt is a statement to create an index.
// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html
type CreateIndexStmt struct {
	ddlNode

	// only supported by MariaDB 10.0.2+,
	// see https://mariadb.com/kb/en/library/create-index/
	IfNotExists bool

	IndexName               string
	Table                   *TableName
	IndexPartSpecifications []*IndexPartSpecification
	IndexOption             *IndexOption
	KeyType                 IndexKeyType
	LockAlg                 *IndexLockAndAlgorithm
}

// Restore implements Node interface.
func (n *CreateIndexStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE ")
	switch n.KeyType {
	case IndexKeyTypeUnique:
		ctx.WriteKeyWord("UNIQUE ")
	case IndexKeyTypeSpatial:
		ctx.WriteKeyWord("SPATIAL ")
	case IndexKeyTypeFulltext:
		ctx.WriteKeyWord("FULLTEXT ")
	case IndexKeyTypeVector:
		ctx.WriteKeyWord("VECTOR ")
	case IndexKeyTypeColumnar:
		ctx.WriteKeyWord("COLUMNAR ")
	}
	ctx.WriteKeyWord("INDEX ")
	if n.IfNotExists {
		ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF NOT EXISTS ")
	}
	ctx.WriteName(n.IndexName)
	ctx.WriteKeyWord(" ON ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.Table")
	}

	ctx.WritePlain(" (")
	for i, indexColName := range n.IndexPartSpecifications {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if err := indexColName.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore CreateIndexStmt.IndexPartSpecifications: [%v]", i)
		}
	}
	ctx.WritePlain(")")

	if n.IndexOption != nil && !n.IndexOption.IsEmpty() {
		ctx.WritePlain(" ")
		if err := n.IndexOption.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.IndexOption")
		}
	}

	if n.LockAlg != nil {
		ctx.WritePlain(" ")
		if err := n.LockAlg.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg")
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *CreateIndexStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CreateIndexStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	for i, val := range n.IndexPartSpecifications {
		node, ok = val.Accept(v)
		if !ok {
			return n, false
		}
		n.IndexPartSpecifications[i] = node.(*IndexPartSpecification)
	}
	if n.IndexOption != nil {
		node, ok := n.IndexOption.Accept(v)
		if !ok {
			return n, false
		}
		n.IndexOption = node.(*IndexOption)
	}
	if n.LockAlg != nil {
		node, ok := n.LockAlg.Accept(v)
		if !ok {
			return n, false
		}
		n.LockAlg = node.(*IndexLockAndAlgorithm)
	}
	return v.Leave(n)
}

// DropIndexStmt is a statement to drop the index.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-index.html
type DropIndexStmt struct {
	ddlNode

	IfExists  bool
	IndexName string
	Table     *TableName
	LockAlg   *IndexLockAndAlgorithm
	IsHypo    bool // whether this operation is for a hypothetical index.
}

// Restore implements Node interface.
func (n *DropIndexStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DROP INDEX ")
	if n.IfExists {
		ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
	}
	ctx.WriteName(n.IndexName)
	ctx.WriteKeyWord(" ON ")

	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while add index")
	}

	if n.LockAlg != nil {
		ctx.WritePlain(" ")
		if err := n.LockAlg.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg")
		}
	}

	return nil
}

// Accept implements Node Accept interface.
func (n *DropIndexStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*DropIndexStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	if n.LockAlg != nil {
		node, ok := n.LockAlg.Accept(v)
		if !ok {
			return n, false
		}
		n.LockAlg = node.(*IndexLockAndAlgorithm)
	}
	return v.Leave(n)
}

// LockTablesStmt is a statement to lock tables.
type LockTablesStmt struct {
	ddlNode

	TableLocks []TableLock
}

// TableLock contains the table name and lock type.
type TableLock struct {
	Table *TableName
	Type  TableLockType
}

// Accept implements Node Accept interface.
func (n *LockTablesStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*LockTablesStmt)
	for i := range n.TableLocks {
		node, ok := n.TableLocks[i].Table.Accept(v)
		if !ok {
			return n, false
		}
		n.TableLocks[i].Table = node.(*TableName)
	}
	return v.Leave(n)
}

// Restore implements Node interface.
func (n *LockTablesStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("LOCK TABLES ")
	for i, tl := range n.TableLocks {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if err := tl.Table.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while add index")
		}
		ctx.WriteKeyWord(" " + tl.Type.String())
	}
	return nil
}

// UnlockTablesStmt is a statement to unlock tables.
type UnlockTablesStmt struct {
	ddlNode
}

// Accept implements Node Accept interface.
func (n *UnlockTablesStmt) Accept(v Visitor) (Node, bool) {
	_, _ = v.Enter(n)
	return v.Leave(n)
}

// Restore implements Node interface.
func (n *UnlockTablesStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("UNLOCK TABLES")
	return nil
}

// CleanupTableLockStmt is a statement to cleanup table lock.
type CleanupTableLockStmt struct {
	ddlNode

	Tables []*TableName
}

// Accept implements Node Accept interface.
func (n *CleanupTableLockStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*CleanupTableLockStmt)
	for i := range n.Tables {
		node, ok := n.Tables[i].Accept(v)
		if !ok {
			return n, false
		}
		n.Tables[i] = node.(*TableName)
	}
	return v.Leave(n)
}

// Restore implements Node interface.
func (n *CleanupTableLockStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ADMIN CLEANUP TABLE LOCK ")
	for i, v := range n.Tables {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if err := v.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore CleanupTableLockStmt.Tables[%d]", i)
		}
	}
	return nil
}

// RepairTableStmt is a statement to repair tableInfo.
type RepairTableStmt struct {
	ddlNode
	Table      *TableName
	CreateStmt *CreateTableStmt
}

// Accept implements Node Accept interface.
func (n *RepairTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*RepairTableStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	node, ok = n.CreateStmt.Accept(v)
	if !ok {
		return n, false
	}
	n.CreateStmt = node.(*CreateTableStmt)
	return v.Leave(n)
}

// Restore implements Node interface.
func (n *RepairTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ADMIN REPAIR TABLE ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.table : [%v]", n.Table)
	}
	ctx.WritePlain(" ")
	if err := n.CreateStmt.Restore(ctx); err != nil {
		return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.createStmt : [%v]", n.CreateStmt)
	}
	return nil
}

// PlacementOptionType is the type for PlacementOption
type PlacementOptionType int

// PlacementOption types.
const (
	PlacementOptionPrimaryRegion PlacementOptionType = 0x3000 + iota
	PlacementOptionRegions
	PlacementOptionFollowerCount
	PlacementOptionVoterCount
	PlacementOptionLearnerCount
	PlacementOptionSchedule
	PlacementOptionConstraints
	PlacementOptionLeaderConstraints
	PlacementOptionLearnerConstraints
	PlacementOptionFollowerConstraints
	PlacementOptionVoterConstraints
	PlacementOptionSurvivalPreferences
	PlacementOptionPolicy
)

// PlacementOption is used for parsing placement option.
type PlacementOption struct {
	Tp        PlacementOptionType
	StrValue  string
	UintValue uint64
}

func (n *PlacementOption) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() {
		return nil
	}
	fn := func() error {
		switch n.Tp {
		case PlacementOptionPrimaryRegion:
			ctx.WriteKeyWord("PRIMARY_REGION ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionRegions:
			ctx.WriteKeyWord("REGIONS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionFollowerCount:
			ctx.WriteKeyWord("FOLLOWERS ")
			ctx.WritePlain("= ")
			ctx.WritePlainf("%d", n.UintValue)
		case PlacementOptionVoterCount:
			ctx.WriteKeyWord("VOTERS ")
			ctx.WritePlain("= ")
			ctx.WritePlainf("%d", n.UintValue)
		case PlacementOptionLearnerCount:
			ctx.WriteKeyWord("LEARNERS ")
			ctx.WritePlain("= ")
			ctx.WritePlainf("%d", n.UintValue)
		case PlacementOptionSchedule:
			ctx.WriteKeyWord("SCHEDULE ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionConstraints:
			ctx.WriteKeyWord("CONSTRAINTS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionLeaderConstraints:
			ctx.WriteKeyWord("LEADER_CONSTRAINTS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionFollowerConstraints:
			ctx.WriteKeyWord("FOLLOWER_CONSTRAINTS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionVoterConstraints:
			ctx.WriteKeyWord("VOTER_CONSTRAINTS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionLearnerConstraints:
			ctx.WriteKeyWord("LEARNER_CONSTRAINTS ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		case PlacementOptionPolicy:
			ctx.WriteKeyWord("PLACEMENT POLICY ")
			ctx.WritePlain("= ")
			ctx.WriteName(n.StrValue)
		case PlacementOptionSurvivalPreferences:
			ctx.WriteKeyWord("SURVIVAL_PREFERENCES ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
		default:
			return errors.Errorf("invalid PlacementOption: %d", n.Tp)
		}
		return nil
	}
	// WriteSpecialComment
	return ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, fn)
}

// ResourceGroupOption is used for parsing resource group option.
type ResourceGroupOption struct {
	Tp                ResourceUnitType
	StrValue          string
	UintValue         uint64
	Burstable         BurstableType
	RunawayOptionList []*ResourceGroupRunawayOption
	BackgroundOptions []*ResourceGroupBackgroundOption
}

type ResourceUnitType int

const (
	// RU mode
	ResourceRURate ResourceUnitType = iota
	ResourcePriority
	ResourceBurstable
	// Raw mode
	ResourceUnitCPU
	ResourceUnitIOReadBandwidth
	ResourceUnitIOWriteBandwidth

	// Options
	ResourceBurstableOpiton
	ResourceUnlimitedOption
	ResourceGroupRunaway
	ResourceGroupBackground
)

type BurstableType int

const (
	BurstableDisable BurstableType = iota
	BurstableModerated
	BurstableUnlimited
)

func (n *ResourceGroupOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case ResourceRURate:
		ctx.WriteKeyWord("RU_PER_SEC ")
		ctx.WritePlain("= ")
		if n.Burstable == BurstableUnlimited {
			ctx.WriteKeyWord("UNLIMITED")
		} else {
			ctx.WritePlainf("%d", n.UintValue)
		}
	case ResourcePriority:
		ctx.WriteKeyWord("PRIORITY ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord(PriorityValueToName(n.UintValue))
	case ResourceUnitCPU:
		ctx.WriteKeyWord("CPU ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case ResourceUnitIOReadBandwidth:
		ctx.WriteKeyWord("IO_READ_BANDWIDTH ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case ResourceUnitIOWriteBandwidth:
		ctx.WriteKeyWord("IO_WRITE_BANDWIDTH ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case ResourceBurstable:
		ctx.WriteKeyWord("BURSTABLE ")
		ctx.WritePlain("= ")
		switch n.Burstable {
		case BurstableDisable:
			ctx.WritePlain("OFF")
		case BurstableModerated:
			ctx.WritePlain("MODERATED")
		case BurstableUnlimited:
			ctx.WritePlain("UNLIMITED")
		}
	case ResourceGroupRunaway:
		ctx.WritePlain("QUERY_LIMIT ")
		ctx.WritePlain("= ")
		if len(n.RunawayOptionList) > 0 {
			ctx.WritePlain("(")
			for i, option := range n.RunawayOptionList {
				if i > 0 {
					ctx.WritePlain(" ")
				}
				if err := option.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while splicing ResourceGroupRunaway Option: [%v]", option)
				}
			}
			ctx.WritePlain(")")
		} else {
			ctx.WritePlain("NULL")
		}
	case ResourceGroupBackground:
		ctx.WritePlain("BACKGROUND ")
		ctx.WritePlain("= ")
		if len(n.BackgroundOptions) > 0 {
			ctx.WritePlain("(")
			for i, option := range n.BackgroundOptions {
				if i > 0 {
					ctx.WritePlain(", ")
				}
				if err := option.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while splicing ResourceGroup Background Option: [%v]", option)
				}
			}
			ctx.WritePlain(")")
		} else {
			ctx.WritePlain("NULL")
		}
	default:
		return errors.Errorf("invalid ResourceGroupOption: %d", n.Tp)
	}
	return nil
}

// ResourceGroupRunawayOption is used for parsing resource group runaway rule option.
type ResourceGroupRunawayOption struct {
	Tp           RunawayOptionType
	RuleOption   *ResourceGroupRunawayRuleOption
	ActionOption *ResourceGroupRunawayActionOption
	WatchOption  *ResourceGroupRunawayWatchOption
}

func (n *ResourceGroupRunawayOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case RunawayRule:
		n.RuleOption.restore(ctx)
	case RunawayAction:
		n.ActionOption.Restore(ctx)
	case RunawayWatch:
		n.WatchOption.restore(ctx)
	default:
		return errors.Errorf("invalid ResourceGroupRunawayOption: %d", n.Tp)
	}
	return nil
}

// ResourceGroupRunawayRuleOption is used for parsing the resource group/query watch runaway rule.
type ResourceGroupRunawayRuleOption struct {
	Tp            RunawayRuleOptionType
	ExecElapsed   string
	ProcessedKeys int64
	RequestUnit   int64
}

type RunawayRuleOptionType int

const (
	RunawayRuleExecElapsed RunawayRuleOptionType = iota
	RunawayRuleProcessedKeys
	RunawayRuleRequestUnit
)

func (n *ResourceGroupRunawayRuleOption) restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case RunawayRuleExecElapsed:
		ctx.WriteKeyWord("EXEC_ELAPSED ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.ExecElapsed)
	case RunawayRuleProcessedKeys:
		ctx.WriteKeyWord("PROCESSED_KEYS ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.ProcessedKeys)
	case RunawayRuleRequestUnit:
		ctx.WriteKeyWord("RU ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.RequestUnit)
	}
	return nil
}

// ResourceGroupRunawayActionOption is used for parsing the resource group runaway action.
type ResourceGroupRunawayActionOption struct {
	node
	Type            RunawayActionType
	SwitchGroupName CIStr
}

// Restore implements Node interface.
func (n *ResourceGroupRunawayActionOption) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ACTION ")
	ctx.WritePlain("= ")
	switch n.Type {
	case RunawayActionNone, RunawayActionDryRun, RunawayActionCooldown, RunawayActionKill:
		ctx.WriteKeyWord(n.Type.String())
	case RunawayActionSwitchGroup:
		switchGroup := n.SwitchGroupName.String()
		if len(switchGroup) == 0 {
			return errors.New("SWITCH_GROUP runaway watch action requires a non-empty group name")
		}
		ctx.WriteKeyWord("SWITCH_GROUP")
		ctx.WritePlain("(")
		ctx.WriteName(switchGroup)
		ctx.WritePlain(")")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *ResourceGroupRunawayActionOption) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	return v.Leave(n)
}

// ResourceGroupRunawayWatchOption is used for parsing the resource group runaway watch.
type ResourceGroupRunawayWatchOption struct {
	Type     RunawayWatchType
	Duration string
}

func (n *ResourceGroupRunawayWatchOption) restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("WATCH ")
	ctx.WritePlain("= ")
	ctx.WriteKeyWord(n.Type.String())
	ctx.WritePlain(" ")
	ctx.WriteKeyWord("DURATION ")
	ctx.WritePlain("= ")
	if len(n.Duration) > 0 {
		ctx.WriteString(n.Duration)
	} else {
		ctx.WriteKeyWord("UNLIMITED")
	}
	return nil
}

type BackgroundOptionType int

const (
	BackgroundOptionNone BackgroundOptionType = iota
	BackgroundOptionTaskNames
	BackgroundUtilizationLimit
)

// ResourceGroupBackgroundOption is used to config background job settings.
type ResourceGroupBackgroundOption struct {
	Type      BackgroundOptionType
	StrValue  string
	UintValue uint64
}

func (n *ResourceGroupBackgroundOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Type {
	case BackgroundOptionTaskNames:
		ctx.WriteKeyWord("TASK_TYPES")
		ctx.WritePlain(" = ")
		ctx.WriteString(n.StrValue)
	case BackgroundUtilizationLimit:
		ctx.WriteKeyWord("UTILIZATION_LIMIT")
		ctx.WritePlain(" = ")
		ctx.WritePlainf("%d", n.UintValue)
	default:
		return errors.Errorf("unknown ResourceGroupBackgroundOption: %d", n.Type)
	}

	return nil
}

type StatsOptionType int

const (
	StatsOptionBuckets StatsOptionType = 0x5000 + iota
	StatsOptionTopN
	StatsOptionColsChoice
	StatsOptionColList
	StatsOptionSampleRate
)

// TableOptionType is the type for TableOption
type TableOptionType int

// TableOption types.
const (
	TableOptionNone TableOptionType = iota
	TableOptionEngine
	TableOptionCharset
	TableOptionCollate
	TableOptionAutoIdCache //nolint:revive
	TableOptionAutoIncrement
	TableOptionAutoRandomBase
	TableOptionComment
	TableOptionAvgRowLength
	TableOptionCheckSum
	TableOptionCompression
	TableOptionConnection
	TableOptionPassword
	TableOptionKeyBlockSize
	TableOptionMaxRows
	TableOptionMinRows
	TableOptionDelayKeyWrite
	TableOptionRowFormat
	TableOptionStatsPersistent
	TableOptionStatsAutoRecalc
	TableOptionShardRowID
	TableOptionPreSplitRegion
	TableOptionPackKeys
	TableOptionTablespace
	TableOptionNodegroup
	TableOptionDataDirectory
	TableOptionIndexDirectory
	TableOptionStorageMedia
	TableOptionStatsSamplePages
	TableOptionSecondaryEngine
	TableOptionSecondaryEngineNull
	TableOptionInsertMethod
	TableOptionTableCheckSum
	TableOptionUnion
	TableOptionEncryption
	TableOptionTTL
	TableOptionTTLEnable
	TableOptionTTLJobInterval
	TableOptionEngineAttribute
	TableOptionSecondaryEngineAttribute
	TableOptionAutoextendSize
	TableOptionPageChecksum
	TableOptionPageCompressed
	TableOptionPageCompressionLevel
	TableOptionTransactional
	TableOptionIetfQuotes
	TableOptionSequence
	TableOptionAffinity
	TableOptionPlacementPolicy = TableOptionType(PlacementOptionPolicy)
	TableOptionStatsBuckets    = TableOptionType(StatsOptionBuckets)
	TableOptionStatsTopN       = TableOptionType(StatsOptionTopN)
	TableOptionStatsColsChoice = TableOptionType(StatsOptionColsChoice)
	TableOptionStatsColList    = TableOptionType(StatsOptionColList)
	TableOptionStatsSampleRate = TableOptionType(StatsOptionSampleRate)
)

// RowFormat types
const (
	RowFormatDefault uint64 = iota + 1
	RowFormatDynamic
	RowFormatFixed
	RowFormatCompressed
	RowFormatRedundant
	RowFormatCompact
	TokuDBRowFormatDefault
	TokuDBRowFormatFast
	TokuDBRowFormatSmall
	TokuDBRowFormatZlib
	TokuDBRowFormatQuickLZ
	TokuDBRowFormatLzma
	TokuDBRowFormatSnappy
	TokuDBRowFormatUncompressed
	TokuDBRowFormatZstd
)

// OnDuplicateKeyHandlingType is the option that handle unique key values in 'CREATE TABLE ... SELECT' or `LOAD DATA`.
// See https://dev.mysql.com/doc/refman/5.7/en/create-table-select.html
// See https://dev.mysql.com/doc/refman/5.7/en/load-data.html
type OnDuplicateKeyHandlingType int

// OnDuplicateKeyHandling types
const (
	OnDuplicateKeyHandlingError OnDuplicateKeyHandlingType = iota
	OnDuplicateKeyHandlingIgnore
	OnDuplicateKeyHandlingReplace
)

const (
	TableOptionCharsetWithoutConvertTo uint64 = 0
	TableOptionCharsetWithConvertTo    uint64 = 1
)

const (
	// TableAffinityLevelNone means no affinity.
	TableAffinityLevelNone = "none"
	// TableAffinityLevelTable means table-level affinity.
	TableAffinityLevelTable = "table"
	// TableAffinityLevelPartition means partition-level affinity.
	TableAffinityLevelPartition = "partition"
)

// NormalizeTableAffinityLevel normalizes the affinity level to lower case and checks if it's valid.
func NormalizeTableAffinityLevel(s string) (string, bool) {
	lower := strings.ToLower(s)
	switch lower {
	case TableAffinityLevelNone, TableAffinityLevelTable, TableAffinityLevelPartition:
		return lower, true
	case "":
		return TableAffinityLevelNone, true
	default:
		return s, false
	}
}

// TableOption is used for parsing table option from SQL.
type TableOption struct {
	node
	Tp            TableOptionType
	Default       bool
	StrValue      string
	UintValue     uint64
	BoolValue     bool
	TimeUnitValue *TimeUnitExpr
	Value         ValueExpr
	TableNames    []*TableName
	ColumnName    *ColumnName
}

func (n *TableOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case TableOptionEngine:
		ctx.WriteKeyWord("ENGINE ")
		ctx.WritePlain("= ")
		if n.StrValue != "" {
			ctx.WritePlain(n.StrValue)
		} else {
			ctx.WritePlain("''")
		}
	case TableOptionCharset:
		if n.UintValue == TableOptionCharsetWithConvertTo {
			ctx.WriteKeyWord("CONVERT TO ")
		} else {
			ctx.WriteKeyWord("DEFAULT ")
		}
		ctx.WriteKeyWord("CHARACTER SET ")
		if n.UintValue == TableOptionCharsetWithoutConvertTo {
			ctx.WriteKeyWord("= ")
		}
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WriteKeyWord(n.StrValue)
		}
	case TableOptionCollate:
		ctx.WriteKeyWord("DEFAULT COLLATE ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord(n.StrValue)
	case TableOptionAutoIncrement:
		if n.BoolValue {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDForceAutoInc, "FORCE ")
		}
		ctx.WriteKeyWord("AUTO_INCREMENT ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionAutoIdCache:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoIDCache, func() error {
			ctx.WriteKeyWord("AUTO_ID_CACHE ")
			ctx.WritePlain("= ")
			ctx.WritePlainf("%d", n.UintValue)
			return nil
		})
	case TableOptionAutoRandomBase:
		if n.BoolValue {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDForceAutoInc, "FORCE ")
		}
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandomBase, func() error {
			ctx.WriteKeyWord("AUTO_RANDOM_BASE ")
			ctx.WritePlain("= ")
			ctx.WritePlainf("%d", n.UintValue)
			return nil
		})
	case TableOptionComment:
		ctx.WriteKeyWord("COMMENT ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionAvgRowLength:
		ctx.WriteKeyWord("AVG_ROW_LENGTH ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionCheckSum:
		ctx.WriteKeyWord("CHECKSUM ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionCompression:
		ctx.WriteKeyWord("COMPRESSION ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionConnection:
		ctx.WriteKeyWord("CONNECTION ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionPassword:
		ctx.WriteKeyWord("PASSWORD ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionKeyBlockSize:
		ctx.WriteKeyWord("KEY_BLOCK_SIZE ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionMaxRows:
		ctx.WriteKeyWord("MAX_ROWS ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionMinRows:
		ctx.WriteKeyWord("MIN_ROWS ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionDelayKeyWrite:
		ctx.WriteKeyWord("DELAY_KEY_WRITE ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionRowFormat:
		ctx.WriteKeyWord("ROW_FORMAT ")
		ctx.WritePlain("= ")
		switch n.UintValue {
		case RowFormatDefault:
			ctx.WriteKeyWord("DEFAULT")
		case RowFormatDynamic:
			ctx.WriteKeyWord("DYNAMIC")
		case RowFormatFixed:
			ctx.WriteKeyWord("FIXED")
		case RowFormatCompressed:
			ctx.WriteKeyWord("COMPRESSED")
		case RowFormatRedundant:
			ctx.WriteKeyWord("REDUNDANT")
		case RowFormatCompact:
			ctx.WriteKeyWord("COMPACT")
		case TokuDBRowFormatDefault:
			ctx.WriteKeyWord("TOKUDB_DEFAULT")
		case TokuDBRowFormatFast:
			ctx.WriteKeyWord("TOKUDB_FAST")
		case TokuDBRowFormatSmall:
			ctx.WriteKeyWord("TOKUDB_SMALL")
		case TokuDBRowFormatZlib:
			ctx.WriteKeyWord("TOKUDB_ZLIB")
		case TokuDBRowFormatQuickLZ:
			ctx.WriteKeyWord("TOKUDB_QUICKLZ")
		case TokuDBRowFormatLzma:
			ctx.WriteKeyWord("TOKUDB_LZMA")
		case TokuDBRowFormatSnappy:
			ctx.WriteKeyWord("TOKUDB_SNAPPY")
		case TokuDBRowFormatZstd:
			ctx.WriteKeyWord("TOKUDB_ZSTD")
		case TokuDBRowFormatUncompressed:
			ctx.WriteKeyWord("TOKUDB_UNCOMPRESSED")
		default:
			return errors.Errorf("invalid TableOption: TableOptionRowFormat: %d", n.UintValue)
		}
	case TableOptionStatsPersistent:
		// TODO: not support
		ctx.WriteKeyWord("STATS_PERSISTENT ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord("DEFAULT")
		ctx.WritePlain(" /* TableOptionStatsPersistent is not supported */ ")
	case TableOptionStatsAutoRecalc:
		ctx.WriteKeyWord("STATS_AUTO_RECALC ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WritePlainf("%d", n.UintValue)
		}
	case TableOptionShardRowID:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error {
			ctx.WriteKeyWord("SHARD_ROW_ID_BITS ")
			ctx.WritePlainf("= %d", n.UintValue)
			return nil
		})
	case TableOptionPreSplitRegion:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error {
			ctx.WriteKeyWord("PRE_SPLIT_REGIONS ")
			ctx.WritePlainf("= %d", n.UintValue)
			return nil
		})
	case TableOptionPackKeys:
		// TODO: not support
		ctx.WriteKeyWord("PACK_KEYS ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord("DEFAULT")
		ctx.WritePlain(" /* TableOptionPackKeys is not supported */ ")
	case TableOptionTablespace:
		ctx.WriteKeyWord("TABLESPACE ")
		ctx.WritePlain("= ")
		ctx.WriteName(n.StrValue)
	case TableOptionNodegroup:
		ctx.WriteKeyWord("NODEGROUP ")
		ctx.WritePlainf("= %d", n.UintValue)
	case TableOptionDataDirectory:
		ctx.WriteKeyWord("DATA DIRECTORY ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionIndexDirectory:
		ctx.WriteKeyWord("INDEX DIRECTORY ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionStorageMedia:
		ctx.WriteKeyWord("STORAGE ")
		ctx.WriteKeyWord(n.StrValue)
	case TableOptionStatsSamplePages:
		ctx.WriteKeyWord("STATS_SAMPLE_PAGES ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WritePlainf("%d", n.UintValue)
		}
	case TableOptionSecondaryEngine:
		ctx.WriteKeyWord("SECONDARY_ENGINE ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionSecondaryEngineNull:
		ctx.WriteKeyWord("SECONDARY_ENGINE ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord("NULL")
	case TableOptionSecondaryEngineAttribute:
		ctx.WriteKeyWord("SECONDARY_ENGINE_ATTRIBUTE ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionInsertMethod:
		ctx.WriteKeyWord("INSERT_METHOD ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord(n.StrValue)
	case TableOptionTableCheckSum:
		ctx.WriteKeyWord("TABLE_CHECKSUM ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
	case TableOptionUnion:
		ctx.WriteKeyWord("UNION ")
		ctx.WritePlain("= (")
		for i, tableName := range n.TableNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			tableName.Restore(ctx)
		}
		ctx.WritePlain(")")
	case TableOptionEncryption:
		ctx.WriteKeyWord("ENCRYPTION ")
		ctx.WritePlain("= ")
		ctx.WriteString(n.StrValue)
	case TableOptionPlacementPolicy:
		if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() {
			return nil
		}
		placementOpt := PlacementOption{
			Tp:        PlacementOptionPolicy,
			UintValue: n.UintValue,
			StrValue:  n.StrValue,
		}
		return placementOpt.Restore(ctx)
	case TableOptionStatsBuckets:
		ctx.WriteKeyWord("STATS_BUCKETS ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WritePlainf("%d", n.UintValue)
		}
	case TableOptionStatsTopN:
		ctx.WriteKeyWord("STATS_TOPN ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WritePlainf("%d", n.UintValue)
		}
	case TableOptionStatsSampleRate:
		ctx.WriteKeyWord("STATS_SAMPLE_RATE ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WritePlainf("%v", n.Value.GetValue())
		}
	case TableOptionStatsColsChoice:
		ctx.WriteKeyWord("STATS_COL_CHOICE ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WriteString(n.StrValue)
		}
	case TableOptionStatsColList:
		ctx.WriteKeyWord("STATS_COL_LIST ")
		ctx.WritePlain("= ")
		if n.Default {
			ctx.WriteKeyWord("DEFAULT")
		} else {
			ctx.WriteString(n.StrValue)
		}
	case TableOptionTTL:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error {
			ctx.WriteKeyWord("TTL ")
			ctx.WritePlain("= ")
			ctx.WriteName(n.ColumnName.Name.String())
			ctx.WritePlain(" + INTERVAL ")
			err := n.Value.Restore(ctx)
			ctx.WritePlain(" ")
			if err != nil {
				return err
			}
			return n.TimeUnitValue.Restore(ctx)
		})
	case TableOptionTTLEnable:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error {
			ctx.WriteKeyWord("TTL_ENABLE ")
			ctx.WritePlain("= ")
			if n.BoolValue {
				ctx.WriteString("ON")
			} else {
				ctx.WriteString("OFF")
			}
			return nil
		})
	case TableOptionTTLJobInterval:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error {
			ctx.WriteKeyWord("TTL_JOB_INTERVAL ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
			return nil
		})
	case TableOptionAutoextendSize:
		ctx.WriteKeyWord("AUTOEXTEND_SIZE ")
		ctx.WritePlain("= ")
		ctx.WritePlain(n.StrValue) // e.g. '4M'

	// MariaDB specific options
	case TableOptionPageChecksum:
		ctx.WriteKeyWord("PAGE_CHECKSUM ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
		return nil
	case TableOptionPageCompressed:
		ctx.WriteKeyWord("PAGE_COMPRESSED ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
		return nil
	case TableOptionPageCompressionLevel:
		ctx.WriteKeyWord("PAGE_COMPRESSION_LEVEL ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
		return nil
	case TableOptionTransactional:
		ctx.WriteKeyWord("TRANSACTIONAL ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
		return nil
	case TableOptionIetfQuotes:
		ctx.WriteKeyWord("IETF_QUOTES ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%s", n.StrValue)
		return nil
	case TableOptionSequence:
		ctx.WriteKeyWord("SEQUENCE ")
		ctx.WritePlain("= ")
		ctx.WritePlainf("%d", n.UintValue)
		return nil
	case TableOptionAffinity:
		_ = ctx.WriteWithSpecialComments(tidb.FeatureIDAffinity, func() error {
			ctx.WriteKeyWord("AFFINITY ")
			ctx.WritePlain("= ")
			ctx.WriteString(n.StrValue)
			return nil
		})
	default:
		return errors.Errorf("invalid TableOption: %d", n.Tp)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *TableOption) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*TableOption)
	if n.Value != nil {
		node, ok := n.Value.Accept(v)
		if !ok {
			return n, false
		}
		n.Value = node.(ValueExpr)
	}
	if n.TimeUnitValue != nil {
		node, ok := n.TimeUnitValue.Accept(v)
		if !ok {
			return n, false
		}
		n.TimeUnitValue = node.(*TimeUnitExpr)
	}
	return v.Leave(n)
}

// SequenceOptionType is the type for SequenceOption
type SequenceOptionType int

// SequenceOption types.
const (
	SequenceOptionNone SequenceOptionType = iota
	SequenceOptionIncrementBy
	SequenceStartWith
	SequenceNoMinValue
	SequenceMinValue
	SequenceNoMaxValue
	SequenceMaxValue
	SequenceNoCache
	SequenceCache
	SequenceNoCycle
	SequenceCycle
	// SequenceRestart is only used in alter sequence statement.
	SequenceRestart
	SequenceRestartWith
)

// SequenceOption is used for parsing sequence option from SQL.
type SequenceOption struct {
	Tp       SequenceOptionType
	IntValue int64
}

func (n *SequenceOption) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case SequenceOptionIncrementBy:
		ctx.WriteKeyWord("INCREMENT BY ")
		ctx.WritePlainf("%d", n.IntValue)
	case SequenceStartWith:
		ctx.WriteKeyWord("START WITH ")
		ctx.WritePlainf("%d", n.IntValue)
	case SequenceNoMinValue:
		ctx.WriteKeyWord("NO MINVALUE")
	case SequenceMinValue:
		ctx.WriteKeyWord("MINVALUE ")
		ctx.WritePlainf("%d", n.IntValue)
	case SequenceNoMaxValue:
		ctx.WriteKeyWord("NO MAXVALUE")
	case SequenceMaxValue:
		ctx.WriteKeyWord("MAXVALUE ")
		ctx.WritePlainf("%d", n.IntValue)
	case SequenceNoCache:
		ctx.WriteKeyWord("NOCACHE")
	case SequenceCache:
		ctx.WriteKeyWord("CACHE ")
		ctx.WritePlainf("%d", n.IntValue)
	case SequenceNoCycle:
		ctx.WriteKeyWord("NOCYCLE")
	case SequenceCycle:
		ctx.WriteKeyWord("CYCLE")
	case SequenceRestart:
		ctx.WriteKeyWord("RESTART")
	case SequenceRestartWith:
		ctx.WriteKeyWord("RESTART WITH ")
		ctx.WritePlainf("%d", n.IntValue)
	default:
		return errors.Errorf("invalid SequenceOption: %d", n.Tp)
	}
	return nil
}

// ColumnPositionType is the type for ColumnPosition.
type ColumnPositionType int

// ColumnPosition Types
const (
	ColumnPositionNone ColumnPositionType = iota
	ColumnPositionFirst
	ColumnPositionAfter
)

// ColumnPosition represent the position of the newly added column
type ColumnPosition struct {
	node
	// Tp is either ColumnPositionNone, ColumnPositionFirst or ColumnPositionAfter.
	Tp ColumnPositionType
	// RelativeColumn is the column the newly added column after if type is ColumnPositionAfter
	RelativeColumn *ColumnName
}

// Restore implements Node interface.
func (n *ColumnPosition) Restore(ctx *format.RestoreCtx) error {
	switch n.Tp {
	case ColumnPositionNone:
		// do nothing
	case ColumnPositionFirst:
		ctx.WriteKeyWord("FIRST")
	case ColumnPositionAfter:
		ctx.WriteKeyWord("AFTER ")
		if err := n.RelativeColumn.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore ColumnPosition.RelativeColumn")
		}
	default:
		return errors.Errorf("invalid ColumnPositionType: %d", n.Tp)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *ColumnPosition) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*ColumnPosition)
	if n.RelativeColumn != nil {
		node, ok := n.RelativeColumn.Accept(v)
		if !ok {
			return n, false
		}
		n.RelativeColumn = node.(*ColumnName)
	}
	return v.Leave(n)
}

// AlterTableType is the type for AlterTableSpec.
type AlterTableType int

// AlterTable types.
const (
	AlterTableOption AlterTableType = iota + 1
	AlterTableAddColumns
	AlterTableAddConstraint
	AlterTableDropColumn
	AlterTableDropPrimaryKey
	AlterTableDropIndex
	AlterTableDropForeignKey
	AlterTableModifyColumn
	AlterTableChangeColumn
	AlterTableRenameColumn
	AlterTableRenameTable
	AlterTableAlterColumn
	AlterTableLock
	AlterTableWriteable
	AlterTableAlgorithm
	AlterTableRenameIndex
	AlterTableForce
	AlterTableAddPartitions
	// A tombstone for `AlterTableAlterPartition`. It will never be used anymore.
	// Just left a tombstone here to keep the enum number unchanged.
	__DEPRECATED_AlterTableAlterPartition //nolint:revive
	AlterTablePartitionAttributes
	AlterTablePartitionOptions
	AlterTableCoalescePartitions
	AlterTableDropPartition
	AlterTableTruncatePartition
	AlterTablePartition
	AlterTableEnableKeys
	AlterTableDisableKeys
	AlterTableRemovePartitioning
	AlterTableWithValidation
	AlterTableWithoutValidation
	AlterTableSecondaryLoad
	AlterTableSecondaryUnload
	AlterTableRebuildPartition
	AlterTableReorganizePartition
	AlterTableCheckPartitions
	AlterTableExchangePartition
	AlterTableOptimizePartition
	AlterTableRepairPartition
	AlterTableImportPartitionTablespace
	AlterTableDiscardPartitionTablespace
	AlterTableAlterCheck
	AlterTableDropCheck
	AlterTableImportTablespace
	AlterTableDiscardTablespace
	AlterTableIndexInvisible
	// TODO: Add more actions
	AlterTableOrderByColumns
	// AlterTableSetTiFlashReplica uses to set the table TiFlash replica.
	AlterTableSetTiFlashReplica
	// A tombstone for `AlterTablePlacement`. It will never be used anymore.
	// Just left a tombstone here to keep the enum number unchanged.
	__DEPRECATED_AlterTablePlacement //nolint:revive
	AlterTableAddStatistics
	AlterTableDropStatistics
	AlterTableAttributes
	AlterTableCache
	AlterTableNoCache
	AlterTableStatsOptions
	AlterTableDropFirstPartition
	AlterTableAddLastPartition
	AlterTableReorganizeLastPartition
	AlterTableReorganizeFirstPartition
	AlterTableRemoveTTL
	AlterTableSplitIndex
	AlterTableAddMaskingPolicy
	AlterTableEnableMaskingPolicy
	AlterTableDisableMaskingPolicy
	AlterTableDropMaskingPolicy
	AlterTableModifyMaskingPolicyExpression
	AlterTableModifyMaskingPolicyRestrictOn
)

// LockType is the type for AlterTableSpec.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html#alter-table-concurrency
type LockType byte

func (n LockType) String() string {
	switch n {
	case LockTypeNone:
		return "NONE"
	case LockTypeDefault:
		return "DEFAULT"
	case LockTypeShared:
		return "SHARED"
	case LockTypeExclusive:
		return "EXCLUSIVE"
	}
	return ""
}

// Lock Types.
const (
	LockTypeNone LockType = iota + 1
	LockTypeDefault
	LockTypeShared
	LockTypeExclusive
)

// AlgorithmType is the algorithm of the DDL operations.
// See https://dev.mysql.com/doc/refman/8.0/en/alter-table.html#alter-table-performance.
type AlgorithmType byte

// DDL algorithms.
// For now, TiDB only supported inplace and instance algorithms. If the user specify `copy`,
// will get an error.
const (
	AlgorithmTypeDefault AlgorithmType = iota
	AlgorithmTypeCopy
	AlgorithmTypeInplace
	AlgorithmTypeInstant
)

func (a AlgorithmType) String() string {
	switch a {
	case AlgorithmTypeDefault:
		return "DEFAULT"
	case AlgorithmTypeCopy:
		return "COPY"
	case AlgorithmTypeInplace:
		return "INPLACE"
	case AlgorithmTypeInstant:
		return "INSTANT"
	default:
		return "DEFAULT"
	}
}

// AlterTableSpec represents alter table specification.
type AlterTableSpec struct {
	node

	// only supported by MariaDB 10.0.2+ (DROP COLUMN, CHANGE COLUMN, MODIFY COLUMN, DROP INDEX, DROP FOREIGN KEY, DROP PARTITION)
	// see https://mariadb.com/kb/en/library/alter-table/
	IfExists bool

	// only supported by MariaDB 10.0.2+ (ADD COLUMN, ADD PARTITION)
	// see https://mariadb.com/kb/en/library/alter-table/
	IfNotExists bool

	NoWriteToBinlog bool
	OnAllPartitions bool

	Tp                       AlterTableType
	Name                     string
	IndexName                CIStr
	Constraint               *Constraint
	SplitIndex               *SplitIndexOption
	Options                  []*TableOption
	OrderByList              []*AlterOrderItem
	NewTable                 *TableName
	NewColumns               []*ColumnDef
	NewConstraints           []*Constraint
	OldColumnName            *ColumnName
	NewColumnName            *ColumnName
	Position                 *ColumnPosition
	LockType                 LockType
	Algorithm                AlgorithmType
	Comment                  string
	FromKey                  CIStr
	ToKey                    CIStr
	Partition                *PartitionOptions
	PartitionNames           []CIStr
	PartDefinitions          []*PartitionDefinition
	WithValidation           bool
	Num                      uint64
	Visibility               IndexVisibility
	TiFlashReplica           *TiFlashReplicaSpec
	Writeable                bool
	Statistics               *StatisticsSpec
	MaskingPolicyName        CIStr
	MaskingPolicyColumn      *ColumnName
	MaskingPolicyExpr        ExprNode
	MaskingPolicyRestrictOps MaskingPolicyRestrictOps
	MaskingPolicyState       MaskingPolicyState
	AttributesSpec           *AttributesSpec
	StatsOptionsSpec         *StatsOptionsSpec
}

type TiFlashReplicaSpec struct {
	Count  uint64
	Labels []string
	Hypo   bool // hypothetical replica is used by index advisor
}

// AlterOrderItem represents an item in order by at alter table stmt.
type AlterOrderItem struct {
	node
	Column *ColumnName
	Desc   bool
}

// Restore implements Node interface.
func (n *AlterOrderItem) Restore(ctx *format.RestoreCtx) error {
	if err := n.Column.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore AlterOrderItem.Column")
	}
	if n.Desc {
		ctx.WriteKeyWord(" DESC")
	}
	return nil
}

func (n *AlterTableSpec) IsAllPlacementRule() bool {
	switch n.Tp {
	case AlterTablePartitionAttributes, AlterTablePartitionOptions, AlterTableOption, AlterTableAttributes:
		for _, o := range n.Options {
			if o.Tp != TableOptionPlacementPolicy {
				return false
			}
		}
		return true
	default:
		return false
	}
}

// Restore implements Node interface.
func (n *AlterTableSpec) Restore(ctx *format.RestoreCtx) error {
	if n.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() {
		return nil
	}
	switch n.Tp {
	case AlterTableSetTiFlashReplica:
		ctx.WriteKeyWord("SET TIFLASH REPLICA ")
		ctx.WritePlainf("%d", n.TiFlashReplica.Count)
		if len(n.TiFlashReplica.Labels) == 0 {
			break
		}
		ctx.WriteKeyWord(" LOCATION LABELS ")
		for i, v := range n.TiFlashReplica.Labels {
			if i > 0 {
				ctx.WritePlain(", ")
			}
			ctx.WriteString(v)
		}
	case AlterTableAddStatistics:
		ctx.WriteKeyWord("ADD STATS_EXTENDED ")
		if n.IfNotExists {
			ctx.WriteKeyWord("IF NOT EXISTS ")
		}
		ctx.WriteName(n.Statistics.StatsName)
		switch n.Statistics.StatsType {
		case StatsTypeCardinality:
			ctx.WriteKeyWord(" CARDINALITY(")
		case StatsTypeDependency:
			ctx.WriteKeyWord(" DEPENDENCY(")
		case StatsTypeCorrelation:
			ctx.WriteKeyWord(" CORRELATION(")
		}
		for i, col := range n.Statistics.Columns {
			if i != 0 {
				ctx.WritePlain(", ")
			}
			if err := col.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore AddStatisticsSpec.Columns: [%v]", i)
			}
		}
		ctx.WritePlain(")")
	case AlterTableDropStatistics:
		ctx.WriteKeyWord("DROP STATS_EXTENDED ")
		if n.IfExists {
			ctx.WriteKeyWord("IF EXISTS ")
		}
		ctx.WriteName(n.Statistics.StatsName)
	case AlterTableAddMaskingPolicy:
		ctx.WriteKeyWord("ADD MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
		ctx.WritePlain(" ON (")
		if err := n.MaskingPolicyColumn.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.MaskingPolicyColumn")
		}
		ctx.WritePlain(") ")
		ctx.WriteKeyWord("AS ")
		if err := n.MaskingPolicyExpr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.MaskingPolicyExpr")
		}
		if n.MaskingPolicyRestrictOps != MaskingPolicyRestrictOpNone {
			ctx.WritePlain(" ")
			restoreMaskingPolicyRestrictOn(ctx, n.MaskingPolicyRestrictOps, false)
		}
		if n.MaskingPolicyState.Explicit {
			ctx.WritePlain(" ")
			if n.MaskingPolicyState.Enabled {
				ctx.WriteKeyWord("ENABLE")
			} else {
				ctx.WriteKeyWord("DISABLE")
			}
		}
	case AlterTableEnableMaskingPolicy:
		ctx.WriteKeyWord("ENABLE MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
	case AlterTableDisableMaskingPolicy:
		ctx.WriteKeyWord("DISABLE MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
	case AlterTableDropMaskingPolicy:
		ctx.WriteKeyWord("DROP MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
	case AlterTableModifyMaskingPolicyExpression:
		ctx.WriteKeyWord("MODIFY MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
		ctx.WriteKeyWord(" SET EXPRESSION = ")
		if err := n.MaskingPolicyExpr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.MaskingPolicyExpr")
		}
	case AlterTableModifyMaskingPolicyRestrictOn:
		ctx.WriteKeyWord("MODIFY MASKING POLICY ")
		ctx.WriteName(n.MaskingPolicyName.O)
		ctx.WriteKeyWord(" SET ")
		restoreMaskingPolicyRestrictOn(ctx, n.MaskingPolicyRestrictOps, true)
	case AlterTableOption:
		switch {
		case len(n.Options) == 2 && n.Options[0].Tp == TableOptionCharset && n.Options[1].Tp == TableOptionCollate:
			if n.Options[0].UintValue == TableOptionCharsetWithConvertTo {
				ctx.WriteKeyWord("CONVERT TO ")
			}
			ctx.WriteKeyWord("CHARACTER SET ")
			if n.Options[0].Default {
				ctx.WriteKeyWord("DEFAULT")
			} else {
				ctx.WriteKeyWord(n.Options[0].StrValue)
			}
			ctx.WriteKeyWord(" COLLATE ")
			ctx.WriteKeyWord(n.Options[1].StrValue)
		case n.Options[0].Tp == TableOptionCharset && n.Options[0].Default:
			if n.Options[0].UintValue == TableOptionCharsetWithConvertTo {
				ctx.WriteKeyWord("CONVERT TO ")
			}
			ctx.WriteKeyWord("CHARACTER SET DEFAULT")
		default:
			for i, opt := range n.Options {
				if i != 0 {
					ctx.WritePlain(" ")
				}
				if err := opt.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d]", i)
				}
			}
		}
	case AlterTableAddColumns:
		ctx.WriteKeyWord("ADD COLUMN ")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF NOT EXISTS ")
		}
		if n.Position != nil && len(n.NewColumns) == 1 {
			if err := n.NewColumns[0].Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", 0)
			}
			if n.Position.Tp != ColumnPositionNone {
				ctx.WritePlain(" ")
			}
			if err := n.Position.Restore(ctx); err != nil {
				return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
			}
		} else {
			lenCols := len(n.NewColumns)
			ctx.WritePlain("(")
			for i, col := range n.NewColumns {
				if i != 0 {
					ctx.WritePlain(", ")
				}
				if err := col.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", i)
				}
			}
			for i, constraint := range n.NewConstraints {
				if i != 0 || lenCols >= 1 {
					ctx.WritePlain(", ")
				}
				if err := constraint.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewConstraints[%d]", i)
				}
			}
			ctx.WritePlain(")")
		}
	case AlterTableAddConstraint:
		ctx.WriteKeyWord("ADD ")
		if err := n.Constraint.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Constraint")
		}
	case AlterTableDropColumn:
		ctx.WriteKeyWord("DROP COLUMN ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		if err := n.OldColumnName.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName")
		}
	// TODO: RestrictOrCascadeOpt not support
	case AlterTableDropPrimaryKey:
		ctx.WriteKeyWord("DROP PRIMARY KEY")
	case AlterTableDropIndex:
		ctx.WriteKeyWord("DROP INDEX ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		ctx.WriteName(n.Name)
	case AlterTableDropForeignKey:
		ctx.WriteKeyWord("DROP FOREIGN KEY ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		ctx.WriteName(n.Name)
	case AlterTableModifyColumn:
		ctx.WriteKeyWord("MODIFY COLUMN ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		if err := n.NewColumns[0].Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
		}
		if n.Position.Tp != ColumnPositionNone {
			ctx.WritePlain(" ")
		}
		if err := n.Position.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
		}
	case AlterTableChangeColumn:
		ctx.WriteKeyWord("CHANGE COLUMN ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		if err := n.OldColumnName.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName")
		}
		ctx.WritePlain(" ")
		if err := n.NewColumns[0].Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
		}
		if n.Position.Tp != ColumnPositionNone {
			ctx.WritePlain(" ")
		}
		if err := n.Position.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
		}
	case AlterTableRenameColumn:
		ctx.WriteKeyWord("RENAME COLUMN ")
		if err := n.OldColumnName.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName")
		}
		ctx.WriteKeyWord(" TO ")
		if err := n.NewColumnName.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumnName")
		}
	case AlterTableRenameTable:
		ctx.WriteKeyWord("RENAME AS ")
		if err := n.NewTable.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewTable")
		}
	case AlterTableAlterColumn:
		ctx.WriteKeyWord("ALTER COLUMN ")
		if err := n.NewColumns[0].Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
		}
		if len(n.NewColumns[0].Options) == 1 {
			ctx.WriteKeyWord("SET DEFAULT ")
			expr := n.NewColumns[0].Options[0].Expr
			if valueExpr, ok := expr.(ValueExpr); ok {
				if err := valueExpr.Restore(ctx); err != nil {
					return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr")
				}
			} else {
				ctx.WritePlain("(")
				if err := expr.Restore(ctx); err != nil {
					return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr")
				}
				ctx.WritePlain(")")
			}
		} else {
			ctx.WriteKeyWord(" DROP DEFAULT")
		}
	case AlterTableLock:
		ctx.WriteKeyWord("LOCK ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord(n.LockType.String())
	case AlterTableWriteable:
		ctx.WriteKeyWord("READ ")
		if n.Writeable {
			ctx.WriteKeyWord("WRITE")
		} else {
			ctx.WriteKeyWord("ONLY")
		}
	case AlterTableOrderByColumns:
		ctx.WriteKeyWord("ORDER BY ")
		for i, alterOrderItem := range n.OrderByList {
			if i != 0 {
				ctx.WritePlain(",")
			}
			if err := alterOrderItem.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.OrderByList[%d]", i)
			}
		}
	case AlterTableAlgorithm:
		ctx.WriteKeyWord("ALGORITHM ")
		ctx.WritePlain("= ")
		ctx.WriteKeyWord(n.Algorithm.String())
	case AlterTableRenameIndex:
		ctx.WriteKeyWord("RENAME INDEX ")
		ctx.WriteName(n.FromKey.O)
		ctx.WriteKeyWord(" TO ")
		ctx.WriteName(n.ToKey.O)
	case AlterTableForce:
		// TODO: not support
		ctx.WriteKeyWord("FORCE")
		ctx.WritePlain(" /* AlterTableForce is not supported */ ")
	case AlterTableAddPartitions:
		ctx.WriteKeyWord("ADD PARTITION")
		if n.IfNotExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, " IF NOT EXISTS")
		}
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG")
		}
		if n.PartDefinitions != nil {
			ctx.WritePlain(" (")
			for i, def := range n.PartDefinitions {
				if i != 0 {
					ctx.WritePlain(", ")
				}
				if err := def.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i)
				}
			}
			ctx.WritePlain(")")
		} else if n.Num != 0 {
			ctx.WriteKeyWord(" PARTITIONS ")
			ctx.WritePlainf("%d", n.Num)
		}
	case AlterTableDropFirstPartition:
		ctx.WriteKeyWord("FIRST PARTITION LESS THAN (")
		if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableDropFirstPartition Exprs")
		}
		ctx.WriteKeyWord(")")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG")
		}
	case AlterTableAddLastPartition:
		ctx.WriteKeyWord("LAST PARTITION LESS THAN (")
		if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableAddLastPartition Exprs")
		}
		ctx.WriteKeyWord(")")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG")
		}
	case AlterTablePartitionOptions:
		restoreWithoutSpecialComment := func() error {
			origFlags := ctx.Flags
			defer func() {
				ctx.Flags = origFlags
			}()
			ctx.Flags &= ^format.RestoreTiDBSpecialComment
			ctx.WriteKeyWord("PARTITION ")
			ctx.WriteName(n.PartitionNames[0].O)
			ctx.WritePlain(" ")

			for i, opt := range n.Options {
				if i != 0 {
					ctx.WritePlain(" ")
				}
				if err := opt.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d] for PARTITION `%s`", i, n.PartitionNames[0].O)
				}
			}
			return nil
		}

		var err error
		if ctx.Flags.HasTiDBSpecialCommentFlag() {
			// AlterTablePartitionOptions now only supports placement options, so add put all options to special comment
			err = ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, restoreWithoutSpecialComment)
		} else {
			err = restoreWithoutSpecialComment()
		}

		if err != nil {
			return err
		}
	case AlterTablePartitionAttributes:
		ctx.WriteKeyWord("PARTITION ")
		ctx.WriteName(n.PartitionNames[0].O)
		ctx.WritePlain(" ")

		spec := n.AttributesSpec
		if err := spec.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec")
		}
	case AlterTableCoalescePartitions:
		ctx.WriteKeyWord("COALESCE PARTITION ")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ")
		}
		ctx.WritePlainf("%d", n.Num)
	case AlterTableDropPartition:
		ctx.WriteKeyWord("DROP PARTITION ")
		if n.IfExists {
			ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTiDB, "IF EXISTS ")
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableTruncatePartition:
		ctx.WriteKeyWord("TRUNCATE PARTITION ")
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableCheckPartitions:
		ctx.WriteKeyWord("CHECK PARTITION ")
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableOptimizePartition:
		ctx.WriteKeyWord("OPTIMIZE PARTITION ")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ")
		}
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableRepairPartition:
		ctx.WriteKeyWord("REPAIR PARTITION ")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ")
		}
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableImportPartitionTablespace:
		ctx.WriteKeyWord("IMPORT PARTITION ")
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
		} else {
			for i, name := range n.PartitionNames {
				if i != 0 {
					ctx.WritePlain(",")
				}
				ctx.WriteName(name.O)
			}
		}
		ctx.WriteKeyWord(" TABLESPACE")
	case AlterTableDiscardPartitionTablespace:
		ctx.WriteKeyWord("DISCARD PARTITION ")
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
		} else {
			for i, name := range n.PartitionNames {
				if i != 0 {
					ctx.WritePlain(",")
				}
				ctx.WriteName(name.O)
			}
		}
		ctx.WriteKeyWord(" TABLESPACE")
	case AlterTablePartition:
		if err := n.Partition.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Partition")
		}
	case AlterTableEnableKeys:
		ctx.WriteKeyWord("ENABLE KEYS")
	case AlterTableDisableKeys:
		ctx.WriteKeyWord("DISABLE KEYS")
	case AlterTableRemovePartitioning:
		ctx.WriteKeyWord("REMOVE PARTITIONING")
	case AlterTableWithValidation:
		ctx.WriteKeyWord("WITH VALIDATION")
	case AlterTableWithoutValidation:
		ctx.WriteKeyWord("WITHOUT VALIDATION")
	case AlterTableRebuildPartition:
		ctx.WriteKeyWord("REBUILD PARTITION ")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ")
		}
		if n.OnAllPartitions {
			ctx.WriteKeyWord("ALL")
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(name.O)
		}
	case AlterTableReorganizeLastPartition:
		ctx.WriteKeyWord("SPLIT MAXVALUE PARTITION LESS THAN (")
		if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs")
		}
		ctx.WriteKeyWord(")")
	case AlterTableReorganizeFirstPartition:
		ctx.WriteKeyWord("MERGE FIRST PARTITION LESS THAN (")
		if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs")
		}
		ctx.WriteKeyWord(")")
	case AlterTableReorganizePartition:
		ctx.WriteKeyWord("REORGANIZE PARTITION")
		if n.NoWriteToBinlog {
			ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG")
		}
		if n.OnAllPartitions {
			return nil
		}
		for i, name := range n.PartitionNames {
			if i != 0 {
				ctx.WritePlain(",")
			} else {
				ctx.WritePlain(" ")
			}
			ctx.WriteName(name.O)
		}
		ctx.WriteKeyWord(" INTO ")
		if n.PartDefinitions != nil {
			ctx.WritePlain("(")
			for i, def := range n.PartDefinitions {
				if i != 0 {
					ctx.WritePlain(", ")
				}
				if err := def.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i)
				}
			}
			ctx.WritePlain(")")
		}
	case AlterTableExchangePartition:
		ctx.WriteKeyWord("EXCHANGE PARTITION ")
		ctx.WriteName(n.PartitionNames[0].O)
		ctx.WriteKeyWord(" WITH TABLE ")
		n.NewTable.Restore(ctx)
		if !n.WithValidation {
			ctx.WriteKeyWord(" WITHOUT VALIDATION")
		}
	case AlterTableSecondaryLoad:
		ctx.WriteKeyWord("SECONDARY_LOAD")
	case AlterTableSecondaryUnload:
		ctx.WriteKeyWord("SECONDARY_UNLOAD")
	case AlterTableAlterCheck:
		ctx.WriteKeyWord("ALTER CHECK ")
		ctx.WriteName(n.Constraint.Name)
		if !n.Constraint.Enforced {
			ctx.WriteKeyWord(" NOT")
		}
		ctx.WriteKeyWord(" ENFORCED")
	case AlterTableDropCheck:
		ctx.WriteKeyWord("DROP CHECK ")
		ctx.WriteName(n.Constraint.Name)
	case AlterTableImportTablespace:
		ctx.WriteKeyWord("IMPORT TABLESPACE")
	case AlterTableDiscardTablespace:
		ctx.WriteKeyWord("DISCARD TABLESPACE")
	case AlterTableIndexInvisible:
		ctx.WriteKeyWord("ALTER INDEX ")
		ctx.WriteName(n.IndexName.O)
		switch n.Visibility {
		case IndexVisibilityVisible:
			ctx.WriteKeyWord(" VISIBLE")
		case IndexVisibilityInvisible:
			ctx.WriteKeyWord(" INVISIBLE")
		}
	case AlterTableAttributes:
		spec := n.AttributesSpec
		if err := spec.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec")
		}
	case AlterTableCache:
		ctx.WriteKeyWord("CACHE")
	case AlterTableNoCache:
		ctx.WriteKeyWord("NOCACHE")
	case AlterTableStatsOptions:
		spec := n.StatsOptionsSpec
		if err := spec.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.StatsOptionsSpec")
		}
	case AlterTableRemoveTTL:
		ctx.WriteKeyWordWithSpecialComments(tidb.FeatureIDTTL, "REMOVE TTL")
	case AlterTableSplitIndex:
		spec := n.SplitIndex
		if err := spec.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.SplitIndex")
		}
	default:
		// TODO: not support
		ctx.WritePlainf(" /* AlterTableType(%d) is not supported */ ", n.Tp)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *AlterTableSpec) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterTableSpec)
	if n.Constraint != nil {
		node, ok := n.Constraint.Accept(v)
		if !ok {
			return n, false
		}
		n.Constraint = node.(*Constraint)
	}
	if n.NewTable != nil {
		node, ok := n.NewTable.Accept(v)
		if !ok {
			return n, false
		}
		n.NewTable = node.(*TableName)
	}
	if n.SplitIndex != nil {
		node, ok := n.SplitIndex.Accept(v)
		if !ok {
			return n, false
		}
		n.SplitIndex = node.(*SplitIndexOption)
	}
	for i, col := range n.NewColumns {
		node, ok := col.Accept(v)
		if !ok {
			return n, false
		}
		n.NewColumns[i] = node.(*ColumnDef)
	}
	for i, constraint := range n.NewConstraints {
		node, ok := constraint.Accept(v)
		if !ok {
			return n, false
		}
		n.NewConstraints[i] = node.(*Constraint)
	}
	if n.OldColumnName != nil {
		node, ok := n.OldColumnName.Accept(v)
		if !ok {
			return n, false
		}
		n.OldColumnName = node.(*ColumnName)
	}
	if n.Position != nil {
		node, ok := n.Position.Accept(v)
		if !ok {
			return n, false
		}
		n.Position = node.(*ColumnPosition)
	}
	if n.MaskingPolicyColumn != nil {
		node, ok := n.MaskingPolicyColumn.Accept(v)
		if !ok {
			return n, false
		}
		n.MaskingPolicyColumn = node.(*ColumnName)
	}
	if n.MaskingPolicyExpr != nil {
		node, ok := n.MaskingPolicyExpr.Accept(v)
		if !ok {
			return n, false
		}
		n.MaskingPolicyExpr = node.(ExprNode)
	}
	if n.Partition != nil {
		node, ok := n.Partition.Accept(v)
		if !ok {
			return n, false
		}
		n.Partition = node.(*PartitionOptions)
	}
	for i, option := range n.Options {
		node, ok := option.Accept(v)
		if !ok {
			return n, false
		}
		n.Options[i] = node.(*TableOption)
	}
	for _, def := range n.PartDefinitions {
		if !def.acceptInPlace(v) {
			return n, false
		}
	}
	return v.Leave(n)
}

// AlterTableStmt is a statement to change the structure of a table.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html
type AlterTableStmt struct {
	ddlNode

	Table *TableName
	Specs []*AlterTableSpec
}

func (n *AlterTableStmt) HaveOnlyPlacementOptions() bool {
	for _, n := range n.Specs {
		if n.Tp != AlterTablePartitionOptions {
			return false
		}
		if !n.IsAllPlacementRule() {
			return false
		}
	}
	return true
}

// Restore implements Node interface.
func (n *AlterTableStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.HaveOnlyPlacementOptions() {
		return nil
	}
	ctx.WriteKeyWord("ALTER TABLE ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore AlterTableStmt.Table")
	}
	specs := make([]*AlterTableSpec, 0, len(n.Specs))
	for _, spec := range n.Specs {
		if spec.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() {
			continue
		}
		if spec.Tp == AlterTableOption {
			newOptions := tableOptionsWithRestoreTTLFlag(ctx.Flags, spec.Options)
			if len(newOptions) == 0 {
				continue
			}
			newSpec := *spec
			newSpec.Options = newOptions
			spec = &newSpec
		}
		specs = append(specs, spec)
	}
	for i, spec := range specs {
		if i == 0 || spec.Tp == AlterTablePartition || spec.Tp == AlterTableRemovePartitioning || spec.Tp == AlterTableImportTablespace || spec.Tp == AlterTableDiscardTablespace {
			ctx.WritePlain(" ")
		} else {
			ctx.WritePlain(", ")
		}
		if err := spec.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore AlterTableStmt.Specs[%d]", i)
		}
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *AlterTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterTableStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	for i, val := range n.Specs {
		node, ok = val.Accept(v)
		if !ok {
			return n, false
		}
		n.Specs[i] = node.(*AlterTableSpec)
	}
	return v.Leave(n)
}

// TruncateTableStmt is a statement to empty a table completely.
// See https://dev.mysql.com/doc/refman/5.7/en/truncate-table.html
type TruncateTableStmt struct {
	ddlNode

	Table *TableName
}

// Restore implements Node interface.
func (n *TruncateTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("TRUNCATE TABLE ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore TruncateTableStmt.Table")
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *TruncateTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*TruncateTableStmt)
	node, ok := n.Table.Accept(v)
	if !ok {
		return n, false
	}
	n.Table = node.(*TableName)
	return v.Leave(n)
}

var (
	ErrNoParts                              = terror.ClassDDL.NewStd(mysql.ErrNoParts)
	ErrPartitionColumnList                  = terror.ClassDDL.NewStd(mysql.ErrPartitionColumnList)
	ErrPartitionRequiresValues              = terror.ClassDDL.NewStd(mysql.ErrPartitionRequiresValues)
	ErrPartitionsMustBeDefined              = terror.ClassDDL.NewStd(mysql.ErrPartitionsMustBeDefined)
	ErrPartitionWrongNoPart                 = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoPart)
	ErrPartitionWrongNoSubpart              = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoSubpart)
	ErrPartitionWrongValues                 = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongValues)
	ErrRowSinglePartitionField              = terror.ClassDDL.NewStd(mysql.ErrRowSinglePartitionField)
	ErrSubpartition                         = terror.ClassDDL.NewStd(mysql.ErrSubpartition)
	ErrSystemVersioningWrongPartitions      = terror.ClassDDL.NewStd(mysql.ErrSystemVersioningWrongPartitions)
	ErrTooManyValues                        = terror.ClassDDL.NewStd(mysql.ErrTooManyValues)
	ErrWrongPartitionTypeExpectedSystemTime = terror.ClassDDL.NewStd(mysql.ErrWrongPartitionTypeExpectedSystemTime)
	ErrUnknownCharacterSet                  = terror.ClassDDL.NewStd(mysql.ErrUnknownCharacterSet)
	ErrCoalescePartitionNoPartition         = terror.ClassDDL.NewStd(mysql.ErrCoalescePartitionNoPartition)
	ErrWrongUsage                           = terror.ClassDDL.NewStd(mysql.ErrWrongUsage)
)

type SubPartitionDefinition struct {
	Name    CIStr
	Options []*TableOption
}

func (spd *SubPartitionDefinition) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("SUBPARTITION ")
	ctx.WriteName(spd.Name.O)
	for i, opt := range spd.Options {
		ctx.WritePlain(" ")
		if err := opt.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore SubPartitionDefinition.Options[%d]", i)
		}
	}
	return nil
}

type PartitionDefinitionClause interface {
	restore(ctx *format.RestoreCtx) error
	acceptInPlace(v Visitor) bool
	// Validate checks if the clause is consistent with the given options.
	// `pt` can be 0 and `columns` can be -1 to skip checking the clause against
	// the partition type or number of columns in the expression list.
	Validate(pt PartitionType, columns int) error
}

type PartitionDefinitionClauseNone struct{}

func (*PartitionDefinitionClauseNone) restore(_ *format.RestoreCtx) error {
	return nil
}

func (*PartitionDefinitionClauseNone) acceptInPlace(_ Visitor) bool {
	return true
}

func (*PartitionDefinitionClauseNone) Validate(pt PartitionType, _ int) error {
	switch pt {
	case 0:
	case PartitionTypeRange:
		return ErrPartitionRequiresValues.GenWithStackByArgs("RANGE", "LESS THAN")
	case PartitionTypeList:
		return ErrPartitionRequiresValues.GenWithStackByArgs("LIST", "IN")
	case PartitionTypeSystemTime:
		return ErrSystemVersioningWrongPartitions
	}
	return nil
}

type PartitionDefinitionClauseLessThan struct {
	Exprs []ExprNode
}

func (n *PartitionDefinitionClauseLessThan) restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord(" VALUES LESS THAN ")
	ctx.WritePlain("(")
	for i, expr := range n.Exprs {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if err := expr.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseLessThan.Exprs[%d]", i)
		}
	}
	ctx.WritePlain(")")
	return nil
}

func (n *PartitionDefinitionClauseLessThan) acceptInPlace(v Visitor) bool {
	for i, expr := range n.Exprs {
		newExpr, ok := expr.Accept(v)
		if !ok {
			return false
		}
		n.Exprs[i] = newExpr.(ExprNode)
	}
	return true
}

func (n *PartitionDefinitionClauseLessThan) Validate(pt PartitionType, columns int) error {
	switch pt {
	case PartitionTypeRange, 0:
	default:
		return ErrPartitionWrongValues.GenWithStackByArgs("RANGE", "LESS THAN")
	}

	switch {
	case columns == 0 && len(n.Exprs) != 1:
		return ErrTooManyValues.GenWithStackByArgs("RANGE")
	case columns > 0 && len(n.Exprs) != columns:
		return ErrPartitionColumnList
	}
	return nil
}

type PartitionDefinitionClauseIn struct {
	Values [][]ExprNode
}

func (n *PartitionDefinitionClauseIn) restore(ctx *format.RestoreCtx) error {
	// we special-case an empty list of values to mean MariaDB's "DEFAULT" clause.
	if len(n.Values) == 0 {
		ctx.WriteKeyWord(" DEFAULT")
		return nil
	}
	if len(n.Values) == 1 && len(n.Values[0]) == 1 {
		if _, ok := n.Values[0][0].(*DefaultExpr); ok {
			ctx.WriteKeyWord(" DEFAULT")
			return nil
		}
	}

	ctx.WriteKeyWord(" VALUES IN ")
	ctx.WritePlain("(")
	for i, valList := range n.Values {
		if i != 0 {
			ctx.WritePlain(", ")
		}
		if len(valList) == 1 {
			if err := valList[0].Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][0]", i)
			}
		} else {
			ctx.WritePlain("(")
			for j, val := range valList {
				if j != 0 {
					ctx.WritePlain(", ")
				}
				if err := val.Restore(ctx); err != nil {
					return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][%d]", i, j)
				}
			}
			ctx.WritePlain(")")
		}
	}
	ctx.WritePlain(")")
	return nil
}

func (n *PartitionDefinitionClauseIn) acceptInPlace(v Visitor) bool {
	for _, valList := range n.Values {
		for j, val := range valList {
			newVal, ok := val.Accept(v)
			if !ok {
				return false
			}
			valList[j] = newVal.(ExprNode)
		}
	}
	return true
}

func (n *PartitionDefinitionClauseIn) Validate(pt PartitionType, columns int) error {
	switch pt {
	case PartitionTypeList, 0:
	default:
		return ErrPartitionWrongValues.GenWithStackByArgs("LIST", "IN")
	}

	if len(n.Values) == 0 {
		return nil
	}

	nextIdx := 1
	expectedColCount := len(n.Values[0])
	// OK if one of the n.Values is DefaultExpr as only value
	if expectedColCount == 1 {
		if _, ok := n.Values[0][0].(*DefaultExpr); ok {
			// Only DEFAULT in the partition definition, OK
			if len(n.Values) > 1 {
				expectedColCount = len(n.Values[1])
				nextIdx++
			}
		}
	}
	for _, val := range n.Values[nextIdx:] {
		if len(val) != expectedColCount {
			if _, ok := val[0].(*DefaultExpr); ok && len(val) == 1 {
				continue
			}
			return ErrPartitionColumnList
		}
	}

	switch {
	case columns == 0 && expectedColCount != 1:
		return ErrRowSinglePartitionField
	case columns > 0 && expectedColCount != columns:
		if len(n.Values) == 1 && expectedColCount == 1 {
			if _, ok := n.Values[0][0].(*DefaultExpr); ok {
				// Only one value, which is DEFAULT, which is OK
				return nil
			}
		}
		return ErrPartitionColumnList
	}
	return nil
}

type PartitionDefinitionClauseHistory struct {
	Current bool
}

func (n *PartitionDefinitionClauseHistory) restore(ctx *format.RestoreCtx) error {
	if n.Current {
		ctx.WriteKeyWord(" CURRENT")
	} else {
		ctx.WriteKeyWord(" HISTORY")
	}
	return nil
}

func (*PartitionDefinitionClauseHistory) acceptInPlace(_ Visitor) bool {
	return true
}

func (*PartitionDefinitionClauseHistory) Validate(pt PartitionType, _ int) error {
	switch pt {
	case 0, PartitionTypeSystemTime:
	default:
		return ErrWrongPartitionTypeExpectedSystemTime
	}

	return nil
}

// PartitionDefinition defines a single partition.
type PartitionDefinition struct {
	Name    CIStr
	Clause  PartitionDefinitionClause
	Options []*TableOption
	Sub     []*SubPartitionDefinition
}

// Comment returns the comment option given to this definition.
// The second return value indicates if the comment option exists.
func (n *PartitionDefinition) Comment() (string, bool) {
	for _, opt := range n.Options {
		if opt.Tp == TableOptionComment {
			return opt.StrValue, true
		}
	}
	return "", false
}

func (n *PartitionDefinition) acceptInPlace(v Visitor) bool {
	return n.Clause.acceptInPlace(v)
}

// Restore implements Node interface.
func (n *PartitionDefinition) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("PARTITION ")
	ctx.WriteName(n.Name.O)

	if err := n.Clause.restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore PartitionDefinition.Clause")
	}

	for i, opt := range n.Options {
		ctx.WritePlain(" ")
		if err := opt.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Options[%d]", i)
		}
	}

	if len(n.Sub) > 0 {
		ctx.WritePlain(" (")
		for i, spd := range n.Sub {
			if i != 0 {
				ctx.WritePlain(",")
			}
			if err := spd.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Sub[%d]", i)
			}
		}
		ctx.WritePlain(")")
	}

	return nil
}

type PartitionIntervalExpr struct {
	Expr ExprNode
	// TimeUnitInvalid if not Time based INTERVAL!
	TimeUnit TimeUnitType
}

type PartitionInterval struct {
	// To be able to get original text and replace the syntactic sugar with generated
	// partition definitions
	node
	IntervalExpr  PartitionIntervalExpr
	FirstRangeEnd *ExprNode
	LastRangeEnd  *ExprNode
	MaxValPart    bool
	NullPart      bool
}

// PartitionMethod describes how partitions or subpartitions are constructed.
type PartitionMethod struct {
	// To be able to get original text and replace the syntactic sugar with generated
	// partition definitions
	node
	// Tp is the type of the partition function
	Tp PartitionType
	// Linear is a modifier to the HASH and KEY type for choosing a different
	// algorithm
	Linear bool
	// Expr is an expression used as argument of HASH, RANGE AND LIST types
	Expr ExprNode
	// ColumnNames is a list of column names used as argument of KEY,
	// RANGE COLUMNS and LIST COLUMNS types
	ColumnNames []*ColumnName
	// Unit is a time unit used as argument of SYSTEM_TIME type
	Unit TimeUnitType
	// Limit is a row count used as argument of the SYSTEM_TIME type
	Limit uint64

	// Num is the number of (sub)partitions required by the method.
	Num uint64

	// KeyAlgorithm is the optional hash algorithm type for `PARTITION BY [LINEAR] KEY` syntax.
	KeyAlgorithm *PartitionKeyAlgorithm

	Interval *PartitionInterval
}

type PartitionKeyAlgorithm struct {
	Type uint64
}

// Restore implements the Node interface
func (n *PartitionMethod) Restore(ctx *format.RestoreCtx) error {
	if n.Linear {
		ctx.WriteKeyWord("LINEAR ")
	}
	ctx.WriteKeyWord(n.Tp.String())

	if n.KeyAlgorithm != nil {
		ctx.WriteKeyWord(" ALGORITHM")
		ctx.WritePlainf(" = %d", n.KeyAlgorithm.Type)
	}

	switch {
	case n.Tp == PartitionTypeSystemTime:
		if n.Expr != nil && n.Unit != TimeUnitInvalid {
			ctx.WriteKeyWord(" INTERVAL ")
			if err := n.Expr.Restore(ctx); err != nil {
				return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr")
			}
			ctx.WritePlain(" ")
			ctx.WriteKeyWord(n.Unit.String())
		}
		if n.Limit > 0 {
			ctx.WriteKeyWord(" LIMIT ")
			ctx.WritePlainf("%d", n.Limit)
		}

	case n.Expr != nil:
		ctx.WritePlain(" (")
		if err := n.Expr.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr")
		}
		ctx.WritePlain(")")

	default:
		if n.Tp == PartitionTypeRange || n.Tp == PartitionTypeList {
			ctx.WriteKeyWord(" COLUMNS")
		}
		ctx.WritePlain(" (")
		for i, col := range n.ColumnNames {
			if i > 0 {
				ctx.WritePlain(",")
			}
			if err := col.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while splicing PartitionMethod.ColumnName[%d]", i)
			}
		}
		ctx.WritePlain(")")
	}

	if n.Interval != nil {
		ctx.WritePlain(" INTERVAL (")
		n.Interval.IntervalExpr.Expr.Restore(ctx)
		if n.Interval.IntervalExpr.TimeUnit != TimeUnitInvalid {
			ctx.WritePlain(" ")
			ctx.WriteKeyWord(n.Interval.IntervalExpr.TimeUnit.String())
		}
		ctx.WritePlain(")")
		if n.Interval.FirstRangeEnd != nil {
			ctx.WritePlain(" FIRST PARTITION LESS THAN (")
			(*n.Interval.FirstRangeEnd).Restore(ctx)
			ctx.WritePlain(")")
		}
		if n.Interval.LastRangeEnd != nil {
			ctx.WritePlain(" LAST PARTITION LESS THAN (")
			(*n.Interval.LastRangeEnd).Restore(ctx)
			ctx.WritePlain(")")
		}
		if n.Interval.NullPart {
			ctx.WritePlain(" NULL PARTITION")
		}
		if n.Interval.MaxValPart {
			ctx.WritePlain(" MAXVALUE PARTITION")
		}
	}

	return nil
}

// acceptInPlace is like Node.Accept but does not allow replacing the node itself.
func (n *PartitionMethod) acceptInPlace(v Visitor) bool {
	if n.Expr != nil {
		expr, ok := n.Expr.Accept(v)
		if !ok {
			return false
		}
		n.Expr = expr.(ExprNode)
	}
	for i, colName := range n.ColumnNames {
		newColName, ok := colName.Accept(v)
		if !ok {
			return false
		}
		n.ColumnNames[i] = newColName.(*ColumnName)
	}
	return true
}

// PartitionOptions specifies the partition options.
type PartitionOptions struct {
	PartitionMethod
	Sub           *PartitionMethod
	Definitions   []*PartitionDefinition
	UpdateIndexes []*Constraint
}

// Validate checks if the partition is well-formed.
func (n *PartitionOptions) Validate() error {
	// if both a partition list and the partition numbers are specified, their values must match
	if n.Num != 0 && len(n.Definitions) != 0 && n.Num != uint64(len(n.Definitions)) {
		return ErrPartitionWrongNoPart
	}
	// now check the subpartition count
	if len(n.Definitions) > 0 {
		// ensure the subpartition count for every partitions are the same
		// then normalize n.Num and n.Sub.Num so equality comparison works.
		n.Num = uint64(len(n.Definitions))

		subDefCount := len(n.Definitions[0].Sub)
		for _, pd := range n.Definitions[1:] {
			if len(pd.Sub) != subDefCount {
				return ErrPartitionWrongNoSubpart
			}
		}
		if n.Sub != nil {
			if n.Sub.Num != 0 && subDefCount != 0 && n.Sub.Num != uint64(subDefCount) {
				return ErrPartitionWrongNoSubpart
			}
			if subDefCount != 0 {
				n.Sub.Num = uint64(subDefCount)
			}
		} else if subDefCount != 0 {
			return ErrSubpartition
		}
	}

	switch n.Tp {
	case PartitionTypeHash, PartitionTypeKey:
		if n.Num == 0 {
			n.Num = 1
		}
	case PartitionTypeRange, PartitionTypeList:
		if n.Interval == nil && len(n.Definitions) == 0 {
			return ErrPartitionsMustBeDefined.GenWithStackByArgs(n.Tp)
		}
	case PartitionTypeSystemTime:
		if len(n.Definitions) < 2 {
			return ErrSystemVersioningWrongPartitions
		}
	}

	for _, pd := range n.Definitions {
		// ensure the partition definition types match the methods,
		// e.g. RANGE partitions only allows VALUES LESS THAN
		if err := pd.Clause.Validate(n.Tp, len(n.ColumnNames)); err != nil {
			return err
		}
	}

	return nil
}

func (n *PartitionOptions) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("PARTITION BY ")
	if err := n.PartitionMethod.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore PartitionOptions.PartitionMethod")
	}

	if n.Num > 0 && len(n.Definitions) == 0 {
		ctx.WriteKeyWord(" PARTITIONS ")
		ctx.WritePlainf("%d", n.Num)
	}

	if n.Sub != nil {
		ctx.WriteKeyWord(" SUBPARTITION BY ")
		if err := n.Sub.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while restore PartitionOptions.Sub")
		}
		if n.Sub.Num > 0 {
			ctx.WriteKeyWord(" SUBPARTITIONS ")
			ctx.WritePlainf("%d", n.Sub.Num)
		}
	}

	if len(n.Definitions) > 0 {
		ctx.WritePlain(" (")
		for i, def := range n.Definitions {
			if i > 0 {
				ctx.WritePlain(",")
			}
			if err := def.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore PartitionOptions.Definitions[%d]", i)
			}
		}
		ctx.WritePlain(")")
	}

	if len(n.UpdateIndexes) > 0 {
		ctx.WritePlain(" UPDATE INDEXES (")
		for i, update := range n.UpdateIndexes {
			if i > 0 {
				ctx.WritePlain(",")
			}
			ctx.WriteName(update.Name)
			if update.Option != nil && update.Option.Global {
				ctx.WritePlain(" GLOBAL")
			} else {
				ctx.WritePlain(" LOCAL")
			}
		}
		ctx.WritePlain(")")
	}

	return nil
}

func (n *PartitionOptions) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}

	n = newNode.(*PartitionOptions)
	if !n.PartitionMethod.acceptInPlace(v) {
		return n, false
	}
	if n.Sub != nil && !n.Sub.acceptInPlace(v) {
		return n, false
	}
	for _, def := range n.Definitions {
		if !def.acceptInPlace(v) {
			return n, false
		}
	}
	return v.Leave(n)
}

// RecoverTableStmt is a statement to recover dropped table.
type RecoverTableStmt struct {
	ddlNode

	JobID  int64
	Table  *TableName
	JobNum int64
}

// Restore implements Node interface.
func (n *RecoverTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("RECOVER TABLE ")
	if n.Table != nil {
		if err := n.Table.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table")
		}
		if n.JobNum > 0 {
			ctx.WritePlainf(" %d", n.JobNum)
		}
	} else {
		ctx.WriteKeyWord("BY JOB ")
		ctx.WritePlainf("%d", n.JobID)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *RecoverTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}

	n = newNode.(*RecoverTableStmt)
	if n.Table != nil {
		node, ok := n.Table.Accept(v)
		if !ok {
			return n, false
		}
		n.Table = node.(*TableName)
	}
	return v.Leave(n)
}

// FlashBackToTimestampStmt is a statement to restore the cluster to the specified timestamp
type FlashBackToTimestampStmt struct {
	ddlNode

	FlashbackTS  ExprNode
	FlashbackTSO uint64
	Tables       []*TableName
	DBName       CIStr
}

// Restore implements Node interface
func (n *FlashBackToTimestampStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("FLASHBACK ")
	if len(n.Tables) != 0 {
		ctx.WriteKeyWord("TABLE ")
		for index, table := range n.Tables {
			if index != 0 {
				ctx.WritePlain(", ")
			}
			if err := table.Restore(ctx); err != nil {
				return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index)
			}
		}
	} else if n.DBName.O != "" {
		ctx.WriteKeyWord("DATABASE ")
		ctx.WriteName(n.DBName.O)
	} else {
		ctx.WriteKeyWord("CLUSTER")
	}
	if n.FlashbackTSO == 0 {
		ctx.WriteKeyWord(" TO TIMESTAMP ")
		if err := n.FlashbackTS.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occurred while splicing FlashBackToTimestampStmt.FlashbackTS")
		}
	} else {
		ctx.WriteKeyWord(" TO TSO ")
		ctx.WritePlainf("%d", n.FlashbackTSO)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *FlashBackToTimestampStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*FlashBackToTimestampStmt)
	if len(n.Tables) != 0 {
		for i, val := range n.Tables {
			node, ok := val.Accept(v)
			if !ok {
				return n, false
			}
			n.Tables[i] = node.(*TableName)
		}
	}

	if n.FlashbackTSO == 0 {
		node, ok := n.FlashbackTS.Accept(v)
		if !ok {
			return n, false
		}
		n.FlashbackTS = node.(ExprNode)
	}
	return v.Leave(n)
}

// FlashBackTableStmt is a statement to restore a dropped/truncate table.
type FlashBackTableStmt struct {
	ddlNode

	Table   *TableName
	NewName string
}

// Restore implements Node interface.
func (n *FlashBackTableStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("FLASHBACK TABLE ")
	if err := n.Table.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table")
	}
	if len(n.NewName) > 0 {
		ctx.WriteKeyWord(" TO ")
		ctx.WriteName(n.NewName)
	}
	return nil
}

// Accept implements Node Accept interface.
func (n *FlashBackTableStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}

	n = newNode.(*FlashBackTableStmt)
	if n.Table != nil {
		node, ok := n.Table.Accept(v)
		if !ok {
			return n, false
		}
		n.Table = node.(*TableName)
	}
	return v.Leave(n)
}

type AttributesSpec struct {
	node

	Attributes string
	Default    bool
}

func (n *AttributesSpec) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ATTRIBUTES")
	ctx.WritePlain("=")
	if n.Default {
		ctx.WriteKeyWord("DEFAULT")
		return nil
	}
	ctx.WriteString(n.Attributes)
	return nil
}

func (n *AttributesSpec) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AttributesSpec)
	return v.Leave(n)
}

type StatsOptionsSpec struct {
	node

	StatsOptions string
	Default      bool
}

func (n *StatsOptionsSpec) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("STATS_OPTIONS")
	ctx.WritePlain("=")
	if n.Default {
		ctx.WriteKeyWord("DEFAULT")
		return nil
	}
	ctx.WriteString(n.StatsOptions)
	return nil
}

func (n *StatsOptionsSpec) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*StatsOptionsSpec)
	return v.Leave(n)
}

// AlterPlacementPolicyStmt is a statement to alter placement policy option.
type AlterPlacementPolicyStmt struct {
	ddlNode

	PolicyName       CIStr
	IfExists         bool
	PlacementOptions []*PlacementOption
}

func (n *AlterPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() {
		return nil
	}
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restorePlacementStmtInSpecialComment(ctx, n)
	}

	ctx.WriteKeyWord("ALTER PLACEMENT POLICY ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	ctx.WriteName(n.PolicyName.O)
	for i, option := range n.PlacementOptions {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing AlterPlacementPolicyStmt TableOption: [%v]", i)
		}
	}
	return nil
}

func (n *AlterPlacementPolicyStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterPlacementPolicyStmt)
	return v.Leave(n)
}

func CheckAppend(ops []*ResourceGroupOption, newOp *ResourceGroupOption) bool {
	for _, op := range ops {
		if op.Tp == newOp.Tp {
			return false
		}
	}
	return true
}

func CheckRunawayAppend(ops []*ResourceGroupRunawayOption, newOp *ResourceGroupRunawayOption) bool {
	for _, op := range ops {
		if op.Tp == newOp.Tp {
			// support multiple runaway rules.
			if op.Tp == RunawayRule {
				continue
			}
			return false
		}
	}
	return true
}

func CheckBackgroundAppend(ops []*ResourceGroupBackgroundOption, newOp *ResourceGroupBackgroundOption) bool {
	for _, op := range ops {
		if op.Type == newOp.Type {
			return false
		}
	}
	return true
}

// AlterResourceGroupStmt is a statement to alter placement policy option.
type AlterResourceGroupStmt struct {
	ddlNode

	ResourceGroupName       CIStr
	IfExists                bool
	ResourceGroupOptionList []*ResourceGroupOption
}

func (n *AlterResourceGroupStmt) Restore(ctx *format.RestoreCtx) error {
	if ctx.Flags.HasTiDBSpecialCommentFlag() {
		return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup)
	}

	ctx.WriteKeyWord("ALTER RESOURCE GROUP ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	ctx.WriteName(n.ResourceGroupName.O)
	for i, option := range n.ResourceGroupOptionList {
		if i > 0 {
			ctx.WritePlain(",")
		}
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing AlterResourceGroupStmt Options: [%v]", i)
		}
	}
	return nil
}

func (n *AlterResourceGroupStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterResourceGroupStmt)
	return v.Leave(n)
}

// AlterSequenceStmt is a statement to alter sequence option.
type AlterSequenceStmt struct {
	ddlNode

	// sequence name
	Name *TableName

	IfExists   bool
	SeqOptions []*SequenceOption
}

func (n *AlterSequenceStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ALTER SEQUENCE ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	if err := n.Name.Restore(ctx); err != nil {
		return errors.Annotate(err, "An error occurred while restore AlterSequenceStmt.Table")
	}
	for i, option := range n.SeqOptions {
		ctx.WritePlain(" ")
		if err := option.Restore(ctx); err != nil {
			return errors.Annotatef(err, "An error occurred while splicing AlterSequenceStmt SequenceOption: [%v]", i)
		}
	}
	return nil
}

func (n *AlterSequenceStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*AlterSequenceStmt)
	node, ok := n.Name.Accept(v)
	if !ok {
		return n, false
	}
	n.Name = node.(*TableName)
	return v.Leave(n)
}

func restorePlacementStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode) error {
	return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDPlacement)
}

func restoreStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode, feature string) error {
	origFlags := ctx.Flags
	defer func() {
		ctx.Flags = origFlags
	}()

	ctx.Flags |= format.RestoreTiDBSpecialComment
	return ctx.WriteWithSpecialComments(feature, func() error {
		ctx.Flags &= ^format.RestoreTiDBSpecialComment
		return n.Restore(ctx)
	})
}

func tableOptionsWithRestoreTTLFlag(flags format.RestoreFlags, options []*TableOption) []*TableOption {
	if !flags.HasRestoreWithTTLEnableOff() {
		return options
	}

	newOptions := make([]*TableOption, 0, len(options))
	for _, opt := range options {
		if opt.Tp == TableOptionTTLEnable {
			continue
		}

		newOptions = append(newOptions, opt)
		if opt.Tp == TableOptionTTL {
			newOptions = append(newOptions, &TableOption{
				Tp:        TableOptionTTLEnable,
				BoolValue: false,
			})
		}
	}
	return newOptions
}
