github.com/dolthub/go-mysql-server@v0.18.0/sql/planbuilder/proc.go (about)

     1  // Copyright 2023 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package planbuilder
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  
    22  	ast "github.com/dolthub/vitess/go/vt/sqlparser"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  	"github.com/dolthub/go-mysql-server/sql/expression"
    26  	"github.com/dolthub/go-mysql-server/sql/plan"
    27  	"github.com/dolthub/go-mysql-server/sql/types"
    28  )
    29  
    30  type declareState uint8
    31  
    32  const (
    33  	dsUnknownDeclareState = iota
    34  	dsVariable
    35  	dsCondition
    36  	dsCursor
    37  	dsHandler
    38  	dsBody // No more declarations should be seen
    39  )
    40  
    41  type procCtx struct {
    42  	s          *scope
    43  	handlers   []*plan.DeclareHandler
    44  	conditions map[string]*plan.DeclareCondition
    45  	vars       map[string]scopeColumn
    46  	cursors    map[string]struct{}
    47  	labels     map[string]bool
    48  	lastState  declareState
    49  }
    50  
    51  func (p *procCtx) NewState(state declareState) {
    52  	switch state {
    53  	case dsCondition:
    54  		if p.lastState > state {
    55  			err := sql.ErrDeclareConditionOrderInvalid.New()
    56  			p.s.b.handleErr(err)
    57  		}
    58  	case dsVariable:
    59  		if p.lastState > state && p.lastState != dsCondition {
    60  			err := sql.ErrDeclareVariableOrderInvalid.New()
    61  			p.s.b.handleErr(err)
    62  		}
    63  	case dsHandler:
    64  		if p.lastState > state {
    65  			err := sql.ErrDeclareHandlerOrderInvalid.New()
    66  			p.s.b.handleErr(err)
    67  		}
    68  	case dsCursor:
    69  		if p.lastState > state {
    70  			err := sql.ErrDeclareCursorOrderInvalid.New()
    71  			p.s.b.handleErr(err)
    72  		}
    73  	}
    74  	p.lastState = state
    75  }
    76  
    77  func (p *procCtx) AddVar(param *expression.ProcedureParam) {
    78  	p.NewState(dsVariable)
    79  	lowerName := strings.ToLower(param.Name())
    80  	if _, ok := p.vars[lowerName]; ok {
    81  		err := sql.ErrDeclareVariableDuplicate.New(lowerName)
    82  		p.s.b.handleErr(err)
    83  	}
    84  	col := scopeColumn{col: lowerName, typ: param.Type(), scalar: param}
    85  	p.vars[lowerName] = col
    86  }
    87  
    88  func (p *procCtx) GetVar(name string) (scopeColumn, bool) {
    89  	param, ok := p.vars[strings.ToLower(name)]
    90  	parent := p.s.parent
    91  	for !ok && parent != nil {
    92  		if parent.procActive() {
    93  			param, ok = parent.proc.GetVar(name)
    94  		}
    95  		parent = parent.parent
    96  	}
    97  	return param, ok
    98  }
    99  
   100  func (p *procCtx) AddCursor(name string) {
   101  	p.NewState(dsCursor)
   102  	lowerName := strings.ToLower(name)
   103  	if _, ok := p.cursors[lowerName]; ok {
   104  		err := sql.ErrDeclareCursorDuplicate.New(name)
   105  		p.s.b.handleErr(err)
   106  	}
   107  	p.cursors[lowerName] = struct{}{}
   108  }
   109  
   110  func (p *procCtx) HasCursor(name string) bool {
   111  	_, ok := p.cursors[strings.ToLower(name)]
   112  	if !ok {
   113  		if p.s.parent != nil && p.s.parent.procActive() {
   114  			return p.s.parent.proc.HasCursor(name)
   115  		}
   116  	}
   117  	return ok
   118  }
   119  
   120  func (p *procCtx) AddHandler(h *plan.DeclareHandler) {
   121  	p.NewState(dsHandler)
   122  	p.handlers = append(p.handlers, h)
   123  }
   124  
   125  func (p *procCtx) HasHandler(name string) bool {
   126  	return p.handlers != nil
   127  }
   128  
   129  func (p *procCtx) AddLabel(label string, isLoop bool) {
   130  	p.NewState(dsVariable)
   131  
   132  	// Empty labels are not added since they cannot be referenced
   133  	if label == "" {
   134  		return
   135  	}
   136  	lowercaseLabel := strings.ToLower(label)
   137  	if _, ok := p.labels[lowercaseLabel]; ok {
   138  		err := sql.ErrLoopRedefinition.New(label)
   139  		p.s.b.handleErr(err)
   140  	}
   141  	p.labels[lowercaseLabel] = isLoop
   142  }
   143  
   144  func (p *procCtx) HasLabel(name string) (bool, bool) {
   145  	isLoop, ok := p.labels[strings.ToLower(name)]
   146  	if !ok {
   147  		if p.s.parent != nil && p.s.parent.procActive() {
   148  			return p.s.parent.proc.HasLabel(name)
   149  		}
   150  	}
   151  	return ok, isLoop
   152  }
   153  
   154  func (p *procCtx) AddCondition(cond *plan.DeclareCondition) {
   155  	p.NewState(dsCondition)
   156  	name := strings.ToLower(cond.Name)
   157  	if _, ok := p.conditions[name]; ok {
   158  		err := sql.ErrDeclareConditionDuplicate.New(name)
   159  		p.s.handleErr(err)
   160  	}
   161  	p.conditions[name] = cond
   162  }
   163  
   164  func (p *procCtx) GetCondition(name string) *plan.DeclareCondition {
   165  	cond, ok := p.conditions[strings.ToLower(name)]
   166  	if !ok {
   167  		if p.s.parent != nil && p.s.parent.procActive() {
   168  			return p.s.parent.proc.GetCondition(name)
   169  		}
   170  	}
   171  	return cond
   172  }
   173  
   174  func (b *Builder) buildBeginEndBlock(inScope *scope, n *ast.BeginEndBlock) (outScope *scope) {
   175  	outScope = inScope.push()
   176  	outScope.initProc()
   177  	outScope.proc.AddLabel(n.Label, false)
   178  	block := b.buildBlock(outScope, n.Statements)
   179  	outScope.node = plan.NewBeginEndBlock(n.Label, block)
   180  	return outScope
   181  }
   182  
   183  func (b *Builder) buildIfBlock(inScope *scope, n *ast.IfStatement) (outScope *scope) {
   184  	outScope = inScope.push()
   185  	ifConditionals := make([]*plan.IfConditional, len(n.Conditions))
   186  	for i, ic := range n.Conditions {
   187  		ifConditionalScope := b.buildIfConditional(inScope, ic)
   188  		ifConditionals[i] = ifConditionalScope.node.(*plan.IfConditional)
   189  	}
   190  	elseBlock := b.buildBlock(inScope, n.Else)
   191  	outScope.node = plan.NewIfElse(ifConditionals, elseBlock)
   192  	return outScope
   193  }
   194  
   195  func (b *Builder) buildCaseStatement(inScope *scope, n *ast.CaseStatement) (outScope *scope) {
   196  	outScope = inScope.push()
   197  	ifConditionals := make([]*plan.IfConditional, len(n.Cases))
   198  	for i, c := range n.Cases {
   199  		ifConditionalScope := b.buildIfConditional(inScope, ast.IfStatementCondition{
   200  			Expr:       c.Case,
   201  			Statements: c.Statements,
   202  		})
   203  		ifConditionals[i] = ifConditionalScope.node.(*plan.IfConditional)
   204  	}
   205  	var elseBlock sql.Node
   206  	if n.Else != nil {
   207  		elseBlock = b.buildBlock(inScope, n.Else)
   208  	}
   209  	if n.Expr == nil {
   210  		outScope.node = plan.NewCaseStatement(nil, ifConditionals, elseBlock)
   211  		return outScope
   212  	} else {
   213  		caseExpr := b.buildScalar(inScope, n.Expr)
   214  		outScope.node = plan.NewCaseStatement(caseExpr, ifConditionals, elseBlock)
   215  		return outScope
   216  	}
   217  }
   218  
   219  func (b *Builder) buildIfConditional(inScope *scope, n ast.IfStatementCondition) (outScope *scope) {
   220  	outScope = inScope.push()
   221  	block := b.buildBlock(inScope, n.Statements)
   222  	condition := b.buildScalar(inScope, n.Expr)
   223  	outScope.node = plan.NewIfConditional(condition, block)
   224  	return outScope
   225  }
   226  
   227  func (b *Builder) buildCall(inScope *scope, c *ast.Call) (outScope *scope) {
   228  	outScope = inScope.push()
   229  	params := make([]sql.Expression, len(c.Params))
   230  	for i, param := range c.Params {
   231  		expr := b.buildScalar(inScope, param)
   232  		params[i] = expr
   233  	}
   234  
   235  	var asOf sql.Expression = nil
   236  	if c.AsOf != nil {
   237  		asOf = b.buildAsOfExpr(inScope, c.AsOf)
   238  	} else if b.ProcCtx().AsOf != nil {
   239  		asOf = expression.NewLiteral(b.ProcCtx().AsOf, types.Text)
   240  	} else if b.ViewCtx().AsOf != nil {
   241  		asOf = expression.NewLiteral(b.ViewCtx().AsOf, types.Text)
   242  	}
   243  
   244  	var db sql.Database = nil
   245  	if b.ProcCtx().DbName != "" {
   246  		db = b.resolveDb(b.ProcCtx().DbName)
   247  	} else if b.ViewCtx().DbName != "" {
   248  		db = b.resolveDb(b.ViewCtx().DbName)
   249  	} else if dbName := c.ProcName.Qualifier.String(); dbName != "" {
   250  		db = b.resolveDb(dbName)
   251  	} else if b.ctx.GetCurrentDatabase() != "" {
   252  		db = b.currentDb()
   253  	}
   254  
   255  	outScope.node = plan.NewCall(
   256  		db,
   257  		c.ProcName.Name.String(),
   258  		params,
   259  		asOf,
   260  		&b.cat)
   261  	return outScope
   262  }
   263  
   264  func (b *Builder) buildDeclare(inScope *scope, d *ast.Declare, query string) (outScope *scope) {
   265  	outScope = inScope.push()
   266  	// TODO check and record most recent declare
   267  	if d.Condition != nil {
   268  		return b.buildDeclareCondition(inScope, d)
   269  	} else if d.Variables != nil {
   270  		return b.buildDeclareVariables(inScope, d)
   271  	} else if d.Cursor != nil {
   272  		return b.buildDeclareCursor(inScope, d)
   273  	} else if d.Handler != nil {
   274  		return b.buildDeclareHandler(inScope, d, query)
   275  	}
   276  	err := sql.ErrUnsupportedSyntax.New(ast.String(d))
   277  	b.handleErr(err)
   278  	return
   279  }
   280  
   281  func (b *Builder) buildDeclareCondition(inScope *scope, d *ast.Declare) (outScope *scope) {
   282  	outScope = inScope.push()
   283  	dc := d.Condition
   284  	if dc.SqlStateValue != "" {
   285  		if len(dc.SqlStateValue) != 5 {
   286  			err := fmt.Errorf("SQLSTATE VALUE must be a string with length 5 consisting of only integers")
   287  			b.handleErr(err)
   288  		}
   289  		if dc.SqlStateValue[0:2] == "00" {
   290  			err := fmt.Errorf("invalid SQLSTATE VALUE: '%s'", dc.SqlStateValue)
   291  			b.handleErr(err)
   292  		}
   293  	} else {
   294  		number, err := strconv.ParseUint(string(dc.MysqlErrorCode.Val), 10, 64)
   295  		if err != nil || number == 0 {
   296  			// We use our own error instead
   297  			err := fmt.Errorf("invalid value '%s' for MySQL error code", string(dc.MysqlErrorCode.Val))
   298  			b.handleErr(err)
   299  		}
   300  		//TODO: implement MySQL error code support
   301  		err = sql.ErrUnsupportedSyntax.New(ast.String(d))
   302  		b.handleErr(err)
   303  	}
   304  
   305  	cond := plan.NewDeclareCondition(strings.ToLower(dc.Name), 0, dc.SqlStateValue)
   306  	inScope.proc.AddCondition(cond)
   307  	outScope.node = cond
   308  	return outScope
   309  }
   310  
   311  func (b *Builder) buildDeclareVariables(inScope *scope, d *ast.Declare) (outScope *scope) {
   312  	outScope = inScope.push()
   313  	dVars := d.Variables
   314  	names := make([]string, len(dVars.Names))
   315  	typ, err := types.ColumnTypeToType(&dVars.VarType)
   316  	if err != nil {
   317  		err := err
   318  		b.handleErr(err)
   319  	}
   320  	for i, variable := range dVars.Names {
   321  		varName := strings.ToLower(variable.String())
   322  		names[i] = varName
   323  		param := expression.NewProcedureParam(varName, typ)
   324  		inScope.proc.AddVar(param)
   325  		inScope.newColumn(scopeColumn{col: varName, typ: typ, scalar: param})
   326  	}
   327  	defaultVal := b.buildDefaultExpression(inScope, dVars.VarType.Default)
   328  
   329  	outScope.node = plan.NewDeclareVariables(names, typ, defaultVal)
   330  	return outScope
   331  }
   332  
   333  func (b *Builder) buildDeclareCursor(inScope *scope, d *ast.Declare) (outScope *scope) {
   334  	outScope = inScope.push()
   335  	dCursor := d.Cursor
   336  	selectScope := b.buildSelectStmt(inScope, dCursor.SelectStmt)
   337  	cur := plan.NewDeclareCursor(dCursor.Name, selectScope.node)
   338  	inScope.proc.AddCursor(cur.Name)
   339  	outScope.node = cur
   340  	return outScope
   341  }
   342  
   343  func (b *Builder) buildDeclareHandler(inScope *scope, d *ast.Declare, query string) (outScope *scope) {
   344  	outScope = inScope.push()
   345  	dHandler := d.Handler
   346  	if len(dHandler.ConditionValues) != 1 {
   347  		err := sql.ErrUnsupportedSyntax.New(ast.String(d))
   348  		b.handleErr(err)
   349  	}
   350  
   351  	var cond expression.HandlerCondition
   352  
   353  	switch dHandler.ConditionValues[0].ValueType {
   354  	case ast.DeclareHandlerCondition_NotFound:
   355  		cond = expression.HandlerCondition{Type: expression.HandlerConditionNotFound}
   356  	case ast.DeclareHandlerCondition_SqlException:
   357  		cond = expression.HandlerCondition{Type: expression.HandlerConditionSqlException}
   358  	default:
   359  		err := sql.ErrUnsupportedSyntax.New(ast.String(d))
   360  		b.handleErr(err)
   361  	}
   362  
   363  	stmtScope := b.build(inScope, dHandler.Statement, query)
   364  
   365  	var action expression.DeclareHandlerAction
   366  	switch dHandler.Action {
   367  	case ast.DeclareHandlerAction_Continue:
   368  		action = expression.DeclareHandlerAction_Continue
   369  	case ast.DeclareHandlerAction_Exit:
   370  		action = expression.DeclareHandlerAction_Exit
   371  	case ast.DeclareHandlerAction_Undo:
   372  		action = expression.DeclareHandlerAction_Undo
   373  	default:
   374  		err := fmt.Errorf("unknown DECLARE ... HANDLER action: %v", dHandler.Action)
   375  		b.handleErr(err)
   376  	}
   377  	if action == expression.DeclareHandlerAction_Undo {
   378  		err := sql.ErrDeclareHandlerUndo.New()
   379  		b.handleErr(err)
   380  	}
   381  
   382  	handler := &plan.DeclareHandler{
   383  		Action:    action,
   384  		Statement: stmtScope.node,
   385  		Condition: cond,
   386  	}
   387  
   388  	inScope.proc.AddHandler(handler)
   389  	outScope.node = handler
   390  	return outScope
   391  }
   392  
   393  func (b *Builder) buildBlock(inScope *scope, parserStatements ast.Statements) *plan.Block {
   394  	var statements []sql.Node
   395  	for _, s := range parserStatements {
   396  		switch s.(type) {
   397  		case *ast.Declare:
   398  		default:
   399  			if inScope.procActive() {
   400  				inScope.proc.NewState(dsBody)
   401  			}
   402  		}
   403  		stmtScope := b.build(inScope, s, ast.String(s))
   404  		statements = append(statements, stmtScope.node)
   405  	}
   406  	return plan.NewBlock(statements)
   407  }
   408  
   409  func (b *Builder) buildFetchCursor(inScope *scope, fetchCursor *ast.FetchCursor) (outScope *scope) {
   410  	if !inScope.proc.HasCursor(fetchCursor.Name) {
   411  		err := sql.ErrCursorNotFound.New(fetchCursor.Name)
   412  		b.handleErr(err)
   413  	}
   414  
   415  	outScope = inScope.push()
   416  	exprs := make([]sql.Expression, len(fetchCursor.Variables))
   417  	for i, v := range fetchCursor.Variables {
   418  		col, ok := inScope.resolveColumn("", "", strings.ToLower(v), true, false)
   419  		if !ok {
   420  			err := sql.ErrColumnNotFound.New(v)
   421  			b.handleErr(err)
   422  		}
   423  		exprs[i] = col.scalarGf()
   424  	}
   425  	fetch := plan.NewFetch(fetchCursor.Name, exprs)
   426  	outScope.node = fetch
   427  	return outScope
   428  }
   429  
   430  func (b *Builder) buildOpenCursor(inScope *scope, openCursor *ast.OpenCursor) (outScope *scope) {
   431  	if !inScope.proc.HasCursor(openCursor.Name) {
   432  		err := sql.ErrCursorNotFound.New(openCursor.Name)
   433  		b.handleErr(err)
   434  	}
   435  	outScope = inScope.push()
   436  	outScope.node = plan.NewOpen(openCursor.Name)
   437  	return outScope
   438  }
   439  
   440  func (b *Builder) buildCloseCursor(inScope *scope, closeCursor *ast.CloseCursor) (outScope *scope) {
   441  	if !inScope.proc.HasCursor(closeCursor.Name) {
   442  		err := sql.ErrCursorNotFound.New(closeCursor.Name)
   443  		b.handleErr(err)
   444  	}
   445  
   446  	outScope = inScope.push()
   447  	outScope.node = plan.NewClose(closeCursor.Name)
   448  	return outScope
   449  }
   450  
   451  func (b *Builder) buildLoop(inScope *scope, loop *ast.Loop) (outScope *scope) {
   452  	outScope = inScope.push()
   453  	outScope.initProc()
   454  	outScope.proc.AddLabel(loop.Label, true)
   455  	block := b.buildBlock(outScope, loop.Statements)
   456  	outScope.node = plan.NewLoop(loop.Label, block)
   457  	return outScope
   458  }
   459  
   460  func (b *Builder) buildRepeat(inScope *scope, repeat *ast.Repeat) (outScope *scope) {
   461  	outScope = inScope.push()
   462  	outScope.initProc()
   463  	outScope.proc.AddLabel(repeat.Label, true)
   464  	block := b.buildBlock(outScope, repeat.Statements)
   465  	expr := b.buildScalar(inScope, repeat.Condition)
   466  	outScope.node = plan.NewRepeat(repeat.Label, expr, block)
   467  	return outScope
   468  }
   469  
   470  func (b *Builder) buildWhile(inScope *scope, while *ast.While) (outScope *scope) {
   471  	outScope = inScope.push()
   472  	outScope.initProc()
   473  	outScope.proc.AddLabel(while.Label, true)
   474  	block := b.buildBlock(outScope, while.Statements)
   475  	expr := b.buildScalar(inScope, while.Condition)
   476  	outScope.node = plan.NewWhile(while.Label, expr, block)
   477  	return outScope
   478  }
   479  
   480  func (b *Builder) buildLeave(inScope *scope, leave *ast.Leave) (outScope *scope) {
   481  	if exists, _ := inScope.proc.HasLabel(leave.Label); !exists {
   482  		err := sql.ErrLoopLabelNotFound.New("LEAVE", leave.Label)
   483  		b.handleErr(err)
   484  	}
   485  
   486  	outScope = inScope.push()
   487  	outScope.node = plan.NewLeave(leave.Label)
   488  	return outScope
   489  }
   490  
   491  func (b *Builder) buildIterate(inScope *scope, iterate *ast.Iterate) (outScope *scope) {
   492  	if exists, isLoop := inScope.proc.HasLabel(iterate.Label); !exists || !isLoop {
   493  		err := sql.ErrLoopLabelNotFound.New("ITERATE", iterate.Label)
   494  		b.handleErr(err)
   495  	}
   496  
   497  	outScope = inScope.push()
   498  	outScope.node = plan.NewIterate(iterate.Label)
   499  	return outScope
   500  }
   501  
   502  func (b *Builder) buildSignal(inScope *scope, s *ast.Signal) (outScope *scope) {
   503  	outScope = inScope.push()
   504  	// https://dev.mysql.com/doc/refman/8.0/en/signal.html#signal-condition-information-items
   505  	signalInfo := make(map[plan.SignalConditionItemName]plan.SignalInfo)
   506  	for _, info := range s.Info {
   507  		si := plan.SignalInfo{}
   508  		si.ConditionItemName = b.buildSignalConditionItemName(info.ConditionItemName)
   509  		if _, ok := signalInfo[si.ConditionItemName]; ok {
   510  			err := fmt.Errorf("duplicate signal condition item")
   511  			b.handleErr(err)
   512  		}
   513  
   514  		if si.ConditionItemName == plan.SignalConditionItemName_MysqlErrno {
   515  			switch v := info.Value.(type) {
   516  			case *ast.SQLVal:
   517  				number, err := strconv.ParseUint(string(v.Val), 10, 16)
   518  				if err != nil || number == 0 {
   519  					// We use our own error instead
   520  					err := fmt.Errorf("invalid value '%s' for signal condition information item MYSQL_ERRNO", string(v.Val))
   521  					b.handleErr(err)
   522  				}
   523  				si.IntValue = int64(number)
   524  			default:
   525  				err := fmt.Errorf("invalid value '%v' for signal condition information item MYSQL_ERRNO", info.Value)
   526  				b.handleErr(err)
   527  			}
   528  		} else if si.ConditionItemName == plan.SignalConditionItemName_MessageText {
   529  			switch v := info.Value.(type) {
   530  			case *ast.SQLVal:
   531  				val := string(v.Val)
   532  				if len(val) > 128 {
   533  					err := fmt.Errorf("signal condition information item MESSAGE_TEXT has max length of 128")
   534  					b.handleErr(err)
   535  				}
   536  				si.StrValue = val
   537  			case *ast.ColName:
   538  				var ref sql.Expression
   539  				c, ok := inScope.resolveColumn("", "", v.Name.Lowered(), true, false)
   540  				if ok {
   541  					ref = c.scalarGf()
   542  				} else {
   543  					ref, _, ok = b.buildSysVar(&ast.ColName{Name: v.Name}, ast.SetScope_None)
   544  					if !ok {
   545  						b.handleErr(fmt.Errorf("signal column not found: %s", v.Name.String()))
   546  					}
   547  				}
   548  				si.ExprVal = ref
   549  			default:
   550  				err := fmt.Errorf("invalid value '%v' for signal condition information item MESSAGE_TEXT", info.Value)
   551  				b.handleErr(err)
   552  			}
   553  		} else {
   554  			switch v := info.Value.(type) {
   555  			case *ast.SQLVal:
   556  				val := string(v.Val)
   557  				if len(val) > 64 {
   558  					err := fmt.Errorf("signal condition information item %s has max length of 64", strings.ToUpper(string(si.ConditionItemName)))
   559  					b.handleErr(err)
   560  				}
   561  				si.StrValue = val
   562  			default:
   563  				err := fmt.Errorf("invalid value '%v' for signal condition information item '%s''", info.Value, strings.ToUpper(string(si.ConditionItemName)))
   564  				b.handleErr(err)
   565  			}
   566  		}
   567  		signalInfo[si.ConditionItemName] = si
   568  	}
   569  
   570  	sqlStateValue := s.SqlStateValue
   571  	if s.ConditionName != "" {
   572  		signalName := strings.ToLower(s.ConditionName)
   573  		condition := inScope.proc.GetCondition(signalName)
   574  		if condition == nil {
   575  			err := sql.ErrDeclareConditionNotFound.New(signalName)
   576  			b.handleErr(err)
   577  		}
   578  		if condition.SqlStateValue == "" {
   579  			err := sql.ErrSignalOnlySqlState.New()
   580  			b.handleErr(err)
   581  		}
   582  		sqlStateValue = condition.SqlStateValue
   583  	} else {
   584  		if len(sqlStateValue) != 5 {
   585  			err := fmt.Errorf("SQLSTATE VALUE must be a string with length 5 consisting of only integers")
   586  			b.handleErr(err)
   587  		}
   588  		if sqlStateValue[0:2] == "00" {
   589  			err := fmt.Errorf("invalid SQLSTATE VALUE: '%s'", s.SqlStateValue)
   590  			b.handleErr(err)
   591  		}
   592  	}
   593  
   594  	signal := plan.NewSignal(sqlStateValue, signalInfo)
   595  	outScope.node = signal
   596  	return outScope
   597  }
   598  
   599  func (b *Builder) buildSignalConditionItemName(name ast.SignalConditionItemName) plan.SignalConditionItemName {
   600  	// We convert to our own plan equivalents to keep a separation between the parser and implementation
   601  	switch name {
   602  	case ast.SignalConditionItemName_ClassOrigin:
   603  		return plan.SignalConditionItemName_ClassOrigin
   604  	case ast.SignalConditionItemName_SubclassOrigin:
   605  		return plan.SignalConditionItemName_SubclassOrigin
   606  	case ast.SignalConditionItemName_MessageText:
   607  		return plan.SignalConditionItemName_MessageText
   608  	case ast.SignalConditionItemName_MysqlErrno:
   609  		return plan.SignalConditionItemName_MysqlErrno
   610  	case ast.SignalConditionItemName_ConstraintCatalog:
   611  		return plan.SignalConditionItemName_ConstraintCatalog
   612  	case ast.SignalConditionItemName_ConstraintSchema:
   613  		return plan.SignalConditionItemName_ConstraintSchema
   614  	case ast.SignalConditionItemName_ConstraintName:
   615  		return plan.SignalConditionItemName_ConstraintName
   616  	case ast.SignalConditionItemName_CatalogName:
   617  		return plan.SignalConditionItemName_CatalogName
   618  	case ast.SignalConditionItemName_SchemaName:
   619  		return plan.SignalConditionItemName_SchemaName
   620  	case ast.SignalConditionItemName_TableName:
   621  		return plan.SignalConditionItemName_TableName
   622  	case ast.SignalConditionItemName_ColumnName:
   623  		return plan.SignalConditionItemName_ColumnName
   624  	case ast.SignalConditionItemName_CursorName:
   625  		return plan.SignalConditionItemName_CursorName
   626  	default:
   627  		err := fmt.Errorf("unknown signal condition item name: %s", string(name))
   628  		b.handleErr(err)
   629  	}
   630  	return plan.SignalConditionItemName_Unknown
   631  }