Skip to content

Commit 2058262

Browse files
Merge branch 'main' into customNetListener
2 parents a28eedb + 0a8f917 commit 2058262

File tree

9 files changed

+169
-81
lines changed

9 files changed

+169
-81
lines changed

enginetest/queries/procedure_queries.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,53 @@ END;`,
12361236
},
12371237
},
12381238
},
1239+
{
1240+
Name: "SQLEXCEPTION declare handler",
1241+
SetUpScript: []string{
1242+
`DROP TABLE IF EXISTS t1;`,
1243+
`CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`,
1244+
`CREATE PROCEDURE eof()
1245+
BEGIN
1246+
DECLARE a, b INT DEFAULT 1;
1247+
DECLARE cur1 CURSOR FOR SELECT * FROM t1;
1248+
OPEN cur1;
1249+
BEGIN
1250+
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET a = 7;
1251+
tloop: LOOP
1252+
FETCH cur1 INTO b;
1253+
IF a > 1000 THEN
1254+
LEAVE tloop;
1255+
END IF;
1256+
END LOOP;
1257+
END;
1258+
CLOSE cur1;
1259+
SELECT a;
1260+
END;`,
1261+
`CREATE PROCEDURE duplicate_key()
1262+
BEGIN
1263+
DECLARE a, b INT DEFAULT 1;
1264+
BEGIN
1265+
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET a = 7;
1266+
INSERT INTO t1 values (0);
1267+
END;
1268+
SELECT a;
1269+
END;`,
1270+
},
1271+
Assertions: []ScriptTestAssertion{
1272+
{
1273+
Query: "CALL eof();",
1274+
Expected: []sql.Row{},
1275+
},
1276+
{
1277+
Query: "CALL duplicate_key();",
1278+
Expected: []sql.Row{{1}},
1279+
},
1280+
{
1281+
Query: "CALL duplicate_key();",
1282+
Expected: []sql.Row{{7}},
1283+
},
1284+
},
1285+
},
12391286
{
12401287
Name: "DECLARE HANDLERs exit according to the block they were declared in",
12411288
SetUpScript: []string{

sql/expression/procedurereference.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package expression
1616

1717
import (
18+
"errors"
1819
"fmt"
1920
"strings"
2021

@@ -47,8 +48,9 @@ type procedureCursorReferenceValue struct {
4748
type procedureHandlerReferenceValue struct {
4849
Stmt sql.Node
4950
IsExit bool
51+
Action DeclareHandlerAction
52+
Cond HandlerCondition
5053
ScopeHeight int
51-
//TODO: support more than just NOT FOUND
5254
}
5355

5456
// ProcedureReferencable indicates that a sql.Node takes a *ProcedureReference returns a new copy with the reference set.
@@ -83,10 +85,11 @@ func (ppr *ProcedureReference) InitializeCursor(name string, selectStmt sql.Node
8385
}
8486

8587
// InitializeHandler sets the given handler's statement.
86-
func (ppr *ProcedureReference) InitializeHandler(stmt sql.Node, returnsExitError bool) {
88+
func (ppr *ProcedureReference) InitializeHandler(stmt sql.Node, action DeclareHandlerAction, cond HandlerCondition) {
8789
ppr.InnermostScope.Handlers = append(ppr.InnermostScope.Handlers, &procedureHandlerReferenceValue{
8890
Stmt: stmt,
89-
IsExit: returnsExitError,
91+
Cond: cond,
92+
Action: action,
9093
ScopeHeight: ppr.height,
9194
})
9295
}
@@ -393,12 +396,35 @@ func (upp *UnresolvedProcedureParam) WithChildren(children ...sql.Expression) (s
393396
return upp, nil
394397
}
395398

396-
// ProcedureBlockExitError contains the scope height that should exit.
397-
type ProcedureBlockExitError int
399+
// FetchEOF is a special EOF error that lets the loop implementation
400+
// differentiate between this io.EOF
401+
var FetchEOF = errors.New("exhausted fetch iterator")
398402

399-
var _ error = ProcedureBlockExitError(0)
403+
type HandlerConditionType uint8
400404

401-
// Error implements the error interface.
402-
func (b ProcedureBlockExitError) Error() string {
403-
return "Block that EXIT handler was declared in could somehow not be found"
405+
const (
406+
HandlerConditionUnknown HandlerConditionType = iota
407+
HandlerConditionNotFound
408+
HandlerConditionSqlException
409+
)
410+
411+
type HandlerCondition struct {
412+
SqlStatePrefix string
413+
Type HandlerConditionType
414+
}
415+
416+
type DeclareHandlerAction byte
417+
418+
const (
419+
DeclareHandlerAction_Continue DeclareHandlerAction = iota
420+
DeclareHandlerAction_Exit
421+
DeclareHandlerAction_Undo
422+
)
423+
424+
func (c *HandlerCondition) Matches(err error) bool {
425+
if errors.Is(err, FetchEOF) {
426+
return c.Type == HandlerConditionNotFound
427+
} else {
428+
return c.Type == HandlerConditionSqlException
429+
}
404430
}

sql/plan/declare_handler.go

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,12 @@ import (
2121
"github.com/dolthub/go-mysql-server/sql/expression"
2222
)
2323

24-
type DeclareHandlerAction byte
25-
26-
const (
27-
DeclareHandlerAction_Continue DeclareHandlerAction = iota
28-
DeclareHandlerAction_Exit
29-
DeclareHandlerAction_Undo
30-
)
31-
3224
// DeclareHandler represents the DECLARE ... HANDLER statement.
3325
type DeclareHandler struct {
34-
Action DeclareHandlerAction
26+
Action expression.DeclareHandlerAction
3527
Statement sql.Node
3628
Pref *expression.ProcedureReference
37-
//TODO: implement other conditions besides NOT FOUND
29+
Condition expression.HandlerCondition
3830
}
3931

4032
var _ sql.Node = (*DeclareHandler)(nil)
@@ -43,13 +35,14 @@ var _ sql.DebugStringer = (*DeclareHandler)(nil)
4335
var _ expression.ProcedureReferencable = (*DeclareHandler)(nil)
4436

4537
// NewDeclareHandler returns a new *DeclareHandler node.
46-
func NewDeclareHandler(action DeclareHandlerAction, statement sql.Node) (*DeclareHandler, error) {
47-
if action == DeclareHandlerAction_Undo {
38+
func NewDeclareHandler(action expression.DeclareHandlerAction, statement sql.Node, cond expression.HandlerCondition) (*DeclareHandler, error) {
39+
if action == expression.DeclareHandlerAction_Undo {
4840
return nil, sql.ErrDeclareHandlerUndo.New()
4941
}
5042
return &DeclareHandler{
5143
Action: action,
5244
Statement: statement,
45+
Condition: cond,
5346
}, nil
5447
}
5548

@@ -66,11 +59,11 @@ func (d *DeclareHandler) IsReadOnly() bool {
6659
func (d *DeclareHandler) String() string {
6760
var action string
6861
switch d.Action {
69-
case DeclareHandlerAction_Continue:
62+
case expression.DeclareHandlerAction_Continue:
7063
action = "CONTINUE"
71-
case DeclareHandlerAction_Exit:
64+
case expression.DeclareHandlerAction_Exit:
7265
action = "EXIT"
73-
case DeclareHandlerAction_Undo:
66+
case expression.DeclareHandlerAction_Undo:
7467
action = "UNDO"
7568
}
7669
return fmt.Sprintf("DECLARE %s HANDLER FOR NOT FOUND %s", action, d.Statement.String())
@@ -80,11 +73,11 @@ func (d *DeclareHandler) String() string {
8073
func (d *DeclareHandler) DebugString() string {
8174
var action string
8275
switch d.Action {
83-
case DeclareHandlerAction_Continue:
76+
case expression.DeclareHandlerAction_Continue:
8477
action = "CONTINUE"
85-
case DeclareHandlerAction_Exit:
78+
case expression.DeclareHandlerAction_Exit:
8679
action = "EXIT"
87-
case DeclareHandlerAction_Undo:
80+
case expression.DeclareHandlerAction_Undo:
8881
action = "UNDO"
8982
}
9083
return fmt.Sprintf("DECLARE %s HANDLER FOR NOT FOUND %s", action, sql.DebugString(d.Statement))

sql/planbuilder/ddl.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ func (b *Builder) buildExternalCreateIndex(inScope *scope, ddl *ast.DDL) (outSco
997997
config,
998998
)
999999
createIndex.Catalog = b.cat
1000+
outScope.node = createIndex
10001001
return
10011002
}
10021003

sql/planbuilder/proc.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const (
4040

4141
type procCtx struct {
4242
s *scope
43-
handler *plan.DeclareHandler
43+
handlers []*plan.DeclareHandler
4444
conditions map[string]*plan.DeclareCondition
4545
vars map[string]scopeColumn
4646
cursors map[string]struct{}
@@ -119,15 +119,11 @@ func (p *procCtx) HasCursor(name string) bool {
119119

120120
func (p *procCtx) AddHandler(h *plan.DeclareHandler) {
121121
p.NewState(dsHandler)
122-
if p.handler != nil {
123-
err := sql.ErrDeclareHandlerDuplicate.New()
124-
p.s.b.handleErr(err)
125-
}
126-
p.handler = h
122+
p.handlers = append(p.handlers, h)
127123
}
128124

129125
func (p *procCtx) HasHandler(name string) bool {
130-
return p.handler != nil
126+
return p.handlers != nil
131127
}
132128

133129
func (p *procCtx) AddLabel(label string, isLoop bool) {
@@ -347,33 +343,46 @@ func (b *Builder) buildDeclareCursor(inScope *scope, d *ast.Declare) (outScope *
347343
func (b *Builder) buildDeclareHandler(inScope *scope, d *ast.Declare, query string) (outScope *scope) {
348344
outScope = inScope.push()
349345
dHandler := d.Handler
350-
//TODO: support other condition values besides NOT FOUND
351-
if len(dHandler.ConditionValues) != 1 || dHandler.ConditionValues[0].ValueType != ast.DeclareHandlerCondition_NotFound {
346+
if len(dHandler.ConditionValues) != 1 {
352347
err := sql.ErrUnsupportedSyntax.New(ast.String(d))
353348
b.handleErr(err)
354349
}
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+
355363
stmtScope := b.build(inScope, dHandler.Statement, query)
356364

357-
var action plan.DeclareHandlerAction
365+
var action expression.DeclareHandlerAction
358366
switch dHandler.Action {
359367
case ast.DeclareHandlerAction_Continue:
360-
action = plan.DeclareHandlerAction_Continue
368+
action = expression.DeclareHandlerAction_Continue
361369
case ast.DeclareHandlerAction_Exit:
362-
action = plan.DeclareHandlerAction_Exit
370+
action = expression.DeclareHandlerAction_Exit
363371
case ast.DeclareHandlerAction_Undo:
364-
action = plan.DeclareHandlerAction_Undo
372+
action = expression.DeclareHandlerAction_Undo
365373
default:
366374
err := fmt.Errorf("unknown DECLARE ... HANDLER action: %v", dHandler.Action)
367375
b.handleErr(err)
368376
}
369-
if action == plan.DeclareHandlerAction_Undo {
377+
if action == expression.DeclareHandlerAction_Undo {
370378
err := sql.ErrDeclareHandlerUndo.New()
371379
b.handleErr(err)
372380
}
373381

374382
handler := &plan.DeclareHandler{
375383
Action: action,
376384
Statement: stmtScope.node,
385+
Condition: cond,
377386
}
378387

379388
inScope.proc.AddHandler(handler)

sql/rowexec/other.go

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -71,39 +71,7 @@ func (b *BaseBuilder) buildDeallocateQuery(ctx *sql.Context, n *plan.DeallocateQ
7171
func (b *BaseBuilder) buildFetch(ctx *sql.Context, n *plan.Fetch, row sql.Row) (sql.RowIter, error) {
7272
row, sch, err := n.Pref.FetchCursor(ctx, n.Name)
7373
if err == io.EOF {
74-
scope := n.Pref.InnermostScope
75-
for scope != nil {
76-
for i := len(scope.Handlers) - 1; i >= 0; i-- {
77-
//TODO: handle more than NOT FOUND handlers, handlers should check if the error applies to them first
78-
originalScope := n.Pref.InnermostScope
79-
defer func() {
80-
n.Pref.InnermostScope = originalScope
81-
}()
82-
n.Pref.InnermostScope = scope
83-
handlerRefVal := scope.Handlers[i]
84-
85-
handlerRowIter, err := b.buildNodeExec(ctx, handlerRefVal.Stmt, nil)
86-
if err != nil {
87-
return sql.RowsToRowIter(), err
88-
}
89-
defer handlerRowIter.Close(ctx)
90-
91-
for {
92-
_, err := handlerRowIter.Next(ctx)
93-
if err == io.EOF {
94-
break
95-
} else if err != nil {
96-
return sql.RowsToRowIter(), err
97-
}
98-
}
99-
if handlerRefVal.IsExit {
100-
return sql.RowsToRowIter(), expression.ProcedureBlockExitError(handlerRefVal.ScopeHeight)
101-
}
102-
return sql.RowsToRowIter(), io.EOF
103-
}
104-
scope = scope.Parent
105-
}
106-
return sql.RowsToRowIter(), err
74+
return sql.RowsToRowIter(), expression.FetchEOF
10775
} else if err != nil {
10876
return nil, err
10977
}

0 commit comments

Comments
 (0)