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 }