Skip to content

Commit a36eec4

Browse files
authored
fix prepare statements and user vars in stored procedures (#2961)
1 parent 524c86f commit a36eec4

File tree

4 files changed

+66
-19
lines changed

4 files changed

+66
-19
lines changed

engine.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -587,20 +587,7 @@ func (e *Engine) analyzeNode(ctx *sql.Context, query string, bound sql.Node, qFl
587587
switch n := bound.(type) {
588588
case *plan.PrepareQuery:
589589
sqlMode := sql.LoadSqlMode(ctx)
590-
591-
// we have to name-resolve to check for structural errors, but we do
592-
// not to cache the name-bound query yet.
593-
// todo(max): improve name resolution so we can cache post name-binding.
594-
// this involves expression memoization, which currently screws up aggregation
595-
// and order by aliases
596-
prepStmt, _, err := e.Parser.ParseOneWithOptions(ctx, query, sqlMode.ParserOptions())
597-
if err != nil {
598-
return nil, err
599-
}
600-
prepare, ok := prepStmt.(*sqlparser.Prepare)
601-
if !ok {
602-
return nil, fmt.Errorf("expected *sqlparser.Prepare, found %T", prepStmt)
603-
}
590+
prepare := n.PrepStmt
604591
cacheStmt, _, err := e.Parser.ParseOneWithOptions(ctx, prepare.Expr, sqlMode.ParserOptions())
605592
if err != nil && strings.HasPrefix(prepare.Expr, "@") {
606593
val, err := expression.NewUserVar(strings.TrimPrefix(prepare.Expr, "@")).Eval(ctx, nil)

enginetest/queries/procedure_queries.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,59 @@ end;
22562256
},
22572257
},
22582258
},
2259+
{
2260+
Name: "user variables are usable within stored procedures",
2261+
SetUpScript: []string{
2262+
`
2263+
create procedure proc()
2264+
begin
2265+
declare v int default 123;
2266+
set @v = v;
2267+
end;
2268+
`,
2269+
},
2270+
Assertions: []ScriptTestAssertion{
2271+
{
2272+
Query: "call proc();",
2273+
Expected: []sql.Row{{}},
2274+
},
2275+
{
2276+
Query: "select @v;",
2277+
Expected: []sql.Row{
2278+
{123},
2279+
},
2280+
},
2281+
},
2282+
},
2283+
{
2284+
Name: "prepare statement inside of stored procedures",
2285+
SetUpScript: []string{
2286+
`
2287+
create procedure create_proc()
2288+
begin
2289+
set @stmt = 'create table t (i int)';
2290+
prepare stmt from @stmt;
2291+
execute stmt;
2292+
deallocate prepare stmt;
2293+
end;
2294+
`,
2295+
},
2296+
Assertions: []ScriptTestAssertion{
2297+
{
2298+
SkipResultCheckOnServerEngine: true,
2299+
Query: "call create_proc();",
2300+
Expected: []sql.Row{
2301+
{types.NewOkResult(0)},
2302+
},
2303+
},
2304+
{
2305+
Query: "insert into t values (1), (2), (3);",
2306+
Expected: []sql.Row{
2307+
{types.NewOkResult(3)},
2308+
},
2309+
},
2310+
},
2311+
},
22592312
}
22602313

22612314
var ProcedureCallTests = []ScriptTest{

sql/plan/prepare.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,27 @@ import (
1919

2020
"github.com/dolthub/go-mysql-server/sql"
2121
"github.com/dolthub/go-mysql-server/sql/types"
22+
23+
"github.com/dolthub/vitess/go/vt/sqlparser"
2224
)
2325

2426
// PrepareQuery is a node that prepares the query
2527
type PrepareQuery struct {
26-
Name string
27-
Child sql.Node
28+
Name string
29+
Child sql.Node
30+
PrepStmt *sqlparser.Prepare
2831
}
2932

3033
var _ sql.Node = (*PrepareQuery)(nil)
3134
var _ sql.CollationCoercible = (*PrepareQuery)(nil)
3235

3336
// NewPrepareQuery creates a new PrepareQuery node.
34-
func NewPrepareQuery(name string, child sql.Node) *PrepareQuery {
35-
return &PrepareQuery{Name: name, Child: child}
37+
func NewPrepareQuery(name string, child sql.Node, prepStmt *sqlparser.Prepare) *PrepareQuery {
38+
return &PrepareQuery{
39+
Name: name,
40+
Child: child,
41+
PrepStmt: prepStmt,
42+
}
3643
}
3744

3845
// Schema implements the Node interface.

sql/planbuilder/transactions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (b *Builder) buildPrepare(inScope *scope, n *ast.Prepare) (outScope *scope)
7070
// test for query structure; bind variables will be discarded
7171
b.bindCtx = &BindvarContext{resolveOnly: true}
7272
childScope := b.build(inScope, childStmt, expr)
73-
outScope.node = plan.NewPrepareQuery(n.Name, childScope.node)
73+
outScope.node = plan.NewPrepareQuery(n.Name, childScope.node, n)
7474
return outScope
7575
}
7676

0 commit comments

Comments
 (0)