// Copyright 2023 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 (
	"strconv"

	"github.com/pingcap/errors"
	"github.com/pingcap/tidb/pkg/parser/format"
	"github.com/pingcap/tidb/pkg/parser/types"
)

var (
	_ Node = &StoreParameter{}
	_ Node = &ProcedureDecl{}

	_ StmtNode = &ProcedureBlock{}
	_ StmtNode = &ProcedureInfo{}
	_ StmtNode = &DropProcedureStmt{}
	_ StmtNode = &ProcedureElseIfBlock{}
	_ StmtNode = &ProcedureElseBlock{}
	_ StmtNode = &ProcedureIfBlock{}
	_ StmtNode = &SimpleWhenThenStmt{}
	_ StmtNode = &ProcedureIfInfo{}
	_ StmtNode = &ProcedureLabelBlock{}
	_ StmtNode = &ProcedureLabelLoop{}
	_ StmtNode = &ProcedureJump{}

	_ DeclNode = &ProcedureErrorControl{}
	_ DeclNode = &ProcedureCursor{}
	_ DeclNode = &ProcedureDecl{}

	_ LabelInfo = &ProcedureLabelBlock{}
	_ LabelInfo = &ProcedureLabelLoop{}

	_ ErrNode = &ProcedureErrorCon{}
	_ ErrNode = &ProcedureErrorVal{}
	_ ErrNode = &ProcedureErrorState{}
)

// procedure param type.
const (
	MODE_IN = iota
	MODE_OUT
	MODE_INOUT
)

// procedure handler operation type.
const (
	PROCEDUR_CONTINUE = iota
	PROCEDUR_EXIT
)

// procedure handler value string.
const (
	PROCEDUR_SQLWARNING = iota
	PROCEDUR_NOT_FOUND
	PROCEDUR_SQLEXCEPTION
	PROCEDUR_END
)

// DeclNode expresses procedure block variable interface(include handler\cursor\sp variable)
type DeclNode interface {
	Node
}

// ErrNode expresses all types of handler condition value.
type ErrNode interface {
	StmtNode
}

// ProcedureDeclInfo is the base node of a procedure variable.
type ProcedureDeclInfo struct {
	node
}

// ProcedureErrorCondition is the base node of a condition value.
type ProcedureErrorCondition struct {
	stmtNode
}

// LabelInfo is the interface of loop and block label.
type LabelInfo interface {
	// GetErrorStatus gets label status, if error, return end label name and true.
	// if normal，The returned string has no meaning and false.
	GetErrorStatus() (string, bool)
	// GetLabelName gets label name.
	GetLabelName() string
	// IsBlock gets type flag, true is block, false is loop.
	IsBlock() bool
	// GetBlock gets block stmtnode
	GetBlock() StmtNode
}

// StoreParameter is the parameter of stored procedure.
type StoreParameter struct {
	node
	Paramstatus int
	ParamType   *types.FieldType
	ParamName   string
}

// Restore implements Node interface.
func (n *StoreParameter) Restore(ctx *format.RestoreCtx) error {
	switch n.Paramstatus {
	case MODE_IN:
		ctx.WriteKeyWord(" IN ")
	case MODE_OUT:
		ctx.WriteKeyWord(" OUT ")
	case MODE_INOUT:
		ctx.WriteKeyWord(" INOUT ")
	}

	ctx.WriteName(n.ParamName)
	ctx.WritePlain(" ")
	ctx.WriteKeyWord(n.ParamType.CompactStr())
	return nil
}

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

// ProcedureDecl represents the internal variables of stored procedure .
type ProcedureDecl struct {
	ProcedureDeclInfo
	DeclNames   []string
	DeclType    *types.FieldType
	DeclDefault ExprNode
}

// Restore implements Node interface.
func (n *ProcedureDecl) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DECLARE ")
	for i, name := range n.DeclNames {
		if i != 0 {
			ctx.WritePlain(",")
		}
		ctx.WriteName(name)
	}
	ctx.WritePlain(" ")
	ctx.WriteKeyWord(n.DeclType.CompactStr())
	if n.DeclDefault != nil {
		ctx.WriteKeyWord(" DEFAULT ")
		if err := n.DeclDefault.Restore(ctx); err != nil {
			return errors.Annotate(err, "An error occur while restore expr")
		}
	}
	return nil
}

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

// ProcedureBlock represents a procedure block.
type ProcedureBlock struct {
	stmtNode
	ProcedureVars      []DeclNode // include handler && cursor && variable
	ProcedureProcStmts []StmtNode // procedure statement
}

// Restore implements Node interface.
func (n *ProcedureBlock) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("BEGIN ")
	for _, ProcedureVar := range n.ProcedureVars {
		err := ProcedureVar.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WritePlain(";")
	}

	for _, ProcedureProcStmt := range n.ProcedureProcStmts {
		err := ProcedureProcStmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WritePlain(";")
	}
	ctx.WriteKeyWord(" END")
	return nil
}

// Accept implements Node interface.
func (n *ProcedureBlock) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*ProcedureBlock)
	for i, ProcedureVar := range n.ProcedureVars {
		node, ok := ProcedureVar.Accept(v)
		if !ok {
			return n, false
		}
		n.ProcedureVars[i] = node.(DeclNode)
	}
	// Store Procedure doesn't check the justifiability for statements, so don't traverse ProcedureProcStmts.
	return v.Leave(n)
}

// ProcedureInfo stores all procedure information.
type ProcedureInfo struct {
	stmtNode
	IfNotExists       bool
	ProcedureName     *TableName
	ProcedureParam    []*StoreParameter //procedure param
	ProcedureBody     StmtNode          //procedure body statement
	ProcedureParamStr string            //procedure parameter string
}

// Restore implements Node interface.
func (n *ProcedureInfo) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CREATE PROCEDURE ")
	if n.IfNotExists {
		ctx.WriteKeyWord("IF NOT EXISTS ")
	}
	err := n.ProcedureName.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WritePlain("(")
	for i, ProcedureParam := range n.ProcedureParam {
		if i > 0 {
			ctx.WritePlain(",")
		}
		err := ProcedureParam.Restore(ctx)
		if err != nil {
			return err
		}
	}
	ctx.WritePlain(") ")
	err = (n.ProcedureBody).Restore(ctx)
	if err != nil {
		return err
	}
	return nil
}

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

// DropProcedureStmt represents the ast of `drop procedure`
type DropProcedureStmt struct {
	stmtNode

	IfExists      bool
	ProcedureName *TableName
}

// Restore implements DropProcedureStmt interface.
func (n *DropProcedureStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DROP PROCEDURE ")
	if n.IfExists {
		ctx.WriteKeyWord("IF EXISTS ")
	}
	err := n.ProcedureName.Restore(ctx)
	if err != nil {
		return err
	}
	return nil
}

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

// ProcedureIfInfo stores the `if statement` of procedure.
type ProcedureIfInfo struct {
	stmtNode
	IfBody *ProcedureIfBlock
}

// Restore implements Node interface.
func (n *ProcedureIfInfo) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("IF ")
	err := n.IfBody.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord("END IF")
	return nil
}

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

// ProcedureElseIfBlock stores the `elseif` statement info of procedure.
type ProcedureElseIfBlock struct {
	stmtNode
	ProcedureIfStmt *ProcedureIfBlock
}

// Restore implements Node interface.
func (n *ProcedureElseIfBlock) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ELSEIF ")
	err := n.ProcedureIfStmt.Restore(ctx)
	if err != nil {
		return err
	}
	return nil
}

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

// ProcedureElseBlock stores procedure `else` statement info.
type ProcedureElseBlock struct {
	stmtNode
	ProcedureIfStmts []StmtNode
}

// Restore implements ProcedureElseBlock interface.
func (n *ProcedureElseBlock) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("ELSE ")
	for _, stmt := range n.ProcedureIfStmts {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	return nil
}

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

// ProcedureIfBlock stores `expr ... else if ... else ...` statement in procedure.
type ProcedureIfBlock struct {
	stmtNode
	IfExpr            ExprNode
	ProcedureIfStmts  []StmtNode
	ProcedureElseStmt StmtNode
}

// Restore implements ProcedureIfBlock interface.
func (n *ProcedureIfBlock) Restore(ctx *format.RestoreCtx) error {
	err := n.IfExpr.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" THEN ")
	for _, stmt := range n.ProcedureIfStmts {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	if n.ProcedureElseStmt != nil {
		err = n.ProcedureElseStmt.Restore(ctx)
		if err != nil {
			return err
		}
	}
	return nil
}

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

	if n.ProcedureElseStmt != nil {
		node, ok := n.ProcedureElseStmt.Accept(v)
		if !ok {
			return n, false
		}
		n.ProcedureElseStmt = node.(StmtNode)
	}
	return v.Leave(n)
}

// SimpleWhenThenStmt stores `case expr then ...` statement.
type SimpleWhenThenStmt struct {
	stmtNode

	Expr           ExprNode
	ProcedureStmts []StmtNode
}

// Restore implements SimpleWhenThenStmt interface.
func (n *SimpleWhenThenStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("WHEN ")
	err := n.Expr.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" THEN ")
	for _, stmt := range n.ProcedureStmts {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	return nil
}

// Accept implements SimpleWhenThenStmt Accept interface.
func (n *SimpleWhenThenStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*SimpleWhenThenStmt)
	if n.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
	}
	// Store Procedure do not check sql justifiability, so don't traverse ProcedureStmts.
	return v.Leave(n)
}

// SimpleCaseStmt store WhenCases SimpleWhenThenStmt `case expr SimpleWhenThenStmt else ...` statement.
type SimpleCaseStmt struct {
	stmtNode

	Condition ExprNode
	WhenCases []*SimpleWhenThenStmt
	ElseCases []StmtNode
}

// Restore implements SimpleCaseStmt interface.
func (n *SimpleCaseStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CASE ")
	err := n.Condition.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" ")
	for _, stmt := range n.WhenCases {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
	}

	if n.ElseCases != nil {
		ctx.WriteKeyWord(" ELSE ")
		for _, stmt := range n.ElseCases {
			err := stmt.Restore(ctx)
			if err != nil {
				return err
			}
			ctx.WriteKeyWord(";")
		}
	}
	ctx.WriteKeyWord(" END CASE")
	return nil
}

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

	for i, stmt := range n.WhenCases {
		node, ok := stmt.Accept(v)
		if !ok {
			return n, false
		}
		n.WhenCases[i] = node.(*SimpleWhenThenStmt)
	}

	if n.ElseCases != nil {
		for i, stmt := range n.ElseCases {
			node, ok := stmt.Accept(v)
			if !ok {
				return n, false
			}
			n.ElseCases[i] = node.(StmtNode)
		}
	}
	return v.Leave(n)
}

// SearchWhenThenStmt stores SearchCaseStmt whencase `case expr then ...` statement.
type SearchWhenThenStmt struct {
	stmtNode

	Expr           ExprNode
	ProcedureStmts []StmtNode
}

// Restore implements SearchWhenThenStmt interface.
func (n *SearchWhenThenStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("WHEN ")
	err := n.Expr.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" THEN ")
	for _, stmt := range n.ProcedureStmts {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	return nil
}

// Accept implements SearchWhenThenStmt Accept interface.
func (n *SearchWhenThenStmt) Accept(v Visitor) (Node, bool) {
	newNode, skipChildren := v.Enter(n)
	if skipChildren {
		return v.Leave(newNode)
	}
	n = newNode.(*SearchWhenThenStmt)
	if n.Expr != nil {
		node, ok := n.Expr.Accept(v)
		if !ok {
			return n, false
		}
		n.Expr = node.(ExprNode)
	}
	// Store Procedure do not check sql justifiability, so don't traverse ProcedureStmts.
	return v.Leave(n)
}

// SearchCaseStmt store `case SimpleWhenThenStmt else ...` statement.
type SearchCaseStmt struct {
	stmtNode

	WhenCases []*SearchWhenThenStmt
	ElseCases []StmtNode
}

// Restore implements SearchCaseStmt interface.
func (n *SearchCaseStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CASE ")

	for _, stmt := range n.WhenCases {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
	}

	if n.ElseCases != nil {
		ctx.WriteKeyWord(" ELSE ")
		for _, stmt := range n.ElseCases {
			err := stmt.Restore(ctx)
			if err != nil {
				return err
			}
			ctx.WriteKeyWord(";")
		}
	}
	ctx.WriteKeyWord(" END CASE")
	return nil
}

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

	for i, stmt := range n.WhenCases {
		node, ok := stmt.Accept(v)
		if !ok {
			return n, false
		}
		n.WhenCases[i] = node.(*SearchWhenThenStmt)
	}
	// Store Procedure do not check sql justifiability, so don't traverse ElseCases.
	return v.Leave(n)
}

// ProcedureRepeatStmt store `repeat ... until expr end repeat` statement.
type ProcedureRepeatStmt struct {
	stmtNode

	Body      []StmtNode
	Condition ExprNode
}

// Restore implements ProcedureRepeatStmt interface.
func (n *ProcedureRepeatStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("REPEAT ")
	for _, stmt := range n.Body {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	ctx.WriteKeyWord("UNTIL ")
	err := n.Condition.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" END REPEAT")
	return nil
}

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

	for i, stmt := range n.Body {
		node, ok := stmt.Accept(v)
		if !ok {
			return n, false
		}
		n.Body[i] = node.(StmtNode)
	}

	node, ok := n.Condition.Accept(v)
	if !ok {
		return n, false
	}
	n.Condition = node.(ExprNode)

	return v.Leave(n)
}

// ProcedureWhileStmt stores `while expr do ... end while` statement.
type ProcedureWhileStmt struct {
	stmtNode

	Condition ExprNode
	Body      []StmtNode
}

// Restore implements ProcedureWhileStmt interface.
func (n *ProcedureWhileStmt) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("WHILE ")
	err := n.Condition.Restore(ctx)
	if err != nil {
		return err
	}
	ctx.WriteKeyWord(" DO ")
	for _, stmt := range n.Body {
		err := stmt.Restore(ctx)
		if err != nil {
			return err
		}
		ctx.WriteKeyWord(";")
	}
	ctx.WriteKeyWord("END WHILE")
	return nil
}

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

	node, ok := n.Condition.Accept(v)
	if !ok {
		return n, false
	}
	n.Condition = node.(ExprNode)

	for i, stmt := range n.Body {
		node, ok := stmt.Accept(v)
		if !ok {
			return n, false
		}
		n.Body[i] = node.(StmtNode)
	}
	return v.Leave(n)
}

// ProcedureCursor stores procedure cursor statement.
type ProcedureCursor struct {
	ProcedureDeclInfo

	CurName      string
	Selectstring StmtNode
}

// Restore implements ProcedureCursor interface.
func (n *ProcedureCursor) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DECLARE ")
	ctx.WriteKeyWord(n.CurName)
	ctx.WriteKeyWord(" CURSOR FOR ")
	err := n.Selectstring.Restore(ctx)
	if err != nil {
		return err
	}
	return nil
}

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

// ProcedureErrorControl stored procedure handler statement.
type ProcedureErrorControl struct {
	ProcedureDeclInfo

	ControlHandle int       // handler operation (exit\continue).
	ErrorCon      []ErrNode //handler condition value.
	Operate       StmtNode  // handler block.
}

// Restore implements ProcedureErrorControl interface.
func (n *ProcedureErrorControl) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("DECLARE ")
	switch n.ControlHandle {
	case PROCEDUR_CONTINUE:
		ctx.WriteKeyWord("CONTINUE ")
	case PROCEDUR_EXIT:
		ctx.WriteKeyWord("EXIT ")
	}
	ctx.WriteKeyWord("HANDLER FOR ")
	for i, errorInfo := range n.ErrorCon {
		err := errorInfo.Restore(ctx)
		if err != nil {
			return err
		}
		if i+1 != len(n.ErrorCon) {
			ctx.WritePlain(", ")
		}
	}
	ctx.WritePlain(" ")
	err := n.Operate.Restore(ctx)
	if err != nil {
		return err
	}
	return nil
}

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

// ProcedureOpenCur store open cursor statement.
type ProcedureOpenCur struct {
	stmtNode

	CurName string
}

// Restore implements ProcedureOpenCur interface.
func (n *ProcedureOpenCur) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("OPEN ")
	ctx.WriteKeyWord(n.CurName)
	return nil
}

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

// ProcedureCloseCur store close cursor statement.
type ProcedureCloseCur struct {
	stmtNode

	CurName string
}

// Restore implements ProcedureCloseCur interface.
func (n *ProcedureCloseCur) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("CLOSE ")
	ctx.WriteKeyWord(n.CurName)
	return nil
}

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

// ProcedureFetchInto store cursor read data command.
type ProcedureFetchInto struct {
	stmtNode

	CurName   string
	Variables []string
}

// Restore implements ProcedureFetchInto interface.
func (n *ProcedureFetchInto) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("FETCH ")
	ctx.WriteKeyWord(n.CurName)
	ctx.WriteKeyWord(" INTO ")
	for i, varName := range n.Variables {
		ctx.WriteKeyWord(varName)
		if i+1 < len(n.Variables) {
			ctx.WriteKeyWord(", ")
		}
	}
	return nil
}

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

// ProcedureErrorVal store procedure handler error code.
type ProcedureErrorVal struct {
	ProcedureErrorCondition

	ErrorNum uint64
}

// Restore implements ProcedureErrorVal interface.
func (n *ProcedureErrorVal) Restore(ctx *format.RestoreCtx) error {
	ctx.WritePlain(strconv.FormatUint(n.ErrorNum, 10))
	return nil
}

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

// ProcedureErrorState store procedure handler SQLSTATE string.
type ProcedureErrorState struct {
	ProcedureErrorCondition

	CodeStatus string
}

// Restore implements ProcedureErrorState interface.
func (n *ProcedureErrorState) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteKeyWord("SQLSTATE ")
	ctx.WriteString(n.CodeStatus)
	return nil
}

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

// ProcedureErrorCon stores procedure handler status info.
type ProcedureErrorCon struct {
	ProcedureErrorCondition

	ErrorCon int
}

// Restore implements ProcedureErrorCon interface.
func (n *ProcedureErrorCon) Restore(ctx *format.RestoreCtx) error {
	switch n.ErrorCon {
	case PROCEDUR_SQLWARNING:
		ctx.WriteKeyWord("SQLWARNING")
	case PROCEDUR_NOT_FOUND:
		ctx.WriteKeyWord("NOT FOUND")
	case PROCEDUR_SQLEXCEPTION:
		ctx.WriteKeyWord("SQLEXCEPTION")
	}
	return nil
}

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

// ProcedureLabelBlock stored procedure block label statement.
type ProcedureLabelBlock struct {
	stmtNode
	LabelName  string
	Block      *ProcedureBlock
	LabelError bool
	LabelEnd   string
}

// Restore implements ProcedureLabelBlock interface.
func (n *ProcedureLabelBlock) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteName(n.LabelName)
	ctx.WriteKeyWord(": ")
	err := n.Block.Restore(ctx)
	if err != nil {
		return err
	}
	if n.LabelError {
		return errors.Errorf("the same label has different names,begin: %s,end: %s", n.LabelName, n.LabelEnd)
	}
	ctx.WriteKeyWord(" ")
	ctx.WriteName(n.LabelName)
	return nil
}

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

	node, ok := n.Block.Accept(v)
	if !ok {
		return n, false
	}
	n.Block = node.(*ProcedureBlock)
	// Store Procedure do not check sql justifiability, so don't traverse 	ProcedureProcStmts.
	return v.Leave(n)
}

// GetErrorStatus gets label error info.
func (n *ProcedureLabelBlock) GetErrorStatus() (string, bool) {
	return n.LabelEnd, n.LabelError
}

// GetLabelName gets label name.
func (n *ProcedureLabelBlock) GetLabelName() string {
	return n.LabelName
}

// IsBlock gets block flag.
func (n *ProcedureLabelBlock) IsBlock() bool {
	return true
}

// GetBlock gets the block stmtnode
func (n *ProcedureLabelBlock) GetBlock() StmtNode {
	return n.Block
}

// ProcedureLabelLoop stores  the labeled loop block info in procedure.
type ProcedureLabelLoop struct {
	stmtNode
	LabelName  string
	Block      StmtNode
	LabelError bool
	LabelEnd   string
}

// Restore implements ProcedureLabelLoop interface.
func (n *ProcedureLabelLoop) Restore(ctx *format.RestoreCtx) error {
	ctx.WriteName(n.LabelName)
	ctx.WriteKeyWord(": ")
	err := n.Block.Restore(ctx)
	if err != nil {
		return err
	}
	if n.LabelError {
		return errors.Errorf("the same label has different names,begin: %s,end: %s", n.LabelName, n.LabelEnd)
	}
	ctx.WriteKeyWord(" ")
	ctx.WriteName(n.LabelName)
	return nil
}

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

	node, ok := n.Block.Accept(v)
	if !ok {
		return n, false
	}
	n.Block = node.(StmtNode)
	// Store Procedure do not check sql justifiability, so don't traverse 	ProcedureProcStmts.
	return v.Leave(n)
}

// GetErrorStatus get label error info.
func (n *ProcedureLabelLoop) GetErrorStatus() (string, bool) {
	return n.LabelEnd, n.LabelError
}

// GetLabelName get label name.
func (n *ProcedureLabelLoop) GetLabelName() string {
	return n.LabelName
}

// IsBlock get block flag.
func (n *ProcedureLabelLoop) IsBlock() bool {
	return false
}

// GetBlock get label stmtnode
func (n *ProcedureLabelLoop) GetBlock() StmtNode {
	return n.Block
}

// ProcedureJump stores the Jump statements(leave and iterate) in procedure.
type ProcedureJump struct {
	stmtNode
	Name    string
	IsLeave bool
}

// Restore implements ProcedureJump interface.
func (n *ProcedureJump) Restore(ctx *format.RestoreCtx) error {
	if n.IsLeave {
		ctx.WriteKeyWord("LEAVE ")
	} else {
		ctx.WriteKeyWord("ITERATE ")
	}

	ctx.WriteString(n.Name)
	return nil
}

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