Skip to content

Commit 8a1af52

Browse files
authored
return correct error for ddl in create proc statements (#2828)
1 parent 97b350e commit 8a1af52

File tree

8 files changed

+179
-140
lines changed

8 files changed

+179
-140
lines changed

enginetest/queries/procedure_queries.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,6 +2823,63 @@ var ProcedureCreateInSubroutineTests = []ScriptTest{
28232823
},
28242824
},
28252825
},
2826+
{
2827+
Name: "procedure must not contain CREATE TABLE",
2828+
Assertions: []ScriptTestAssertion{
2829+
{
2830+
Query: "create procedure p() create table t (pk int);",
2831+
ExpectedErrStr: "creating tables in stored procedures is currently unsupported and will be added in a future release",
2832+
},
2833+
{
2834+
Query: "create procedure p() begin create table t (pk int); end;",
2835+
ExpectedErrStr: "creating tables in stored procedures is currently unsupported and will be added in a future release",
2836+
},
2837+
},
2838+
},
2839+
{
2840+
Name: "procedure must not contain CREATE TRIGGER",
2841+
SetUpScript: []string{
2842+
"create table t (i int);",
2843+
},
2844+
Assertions: []ScriptTestAssertion{
2845+
{
2846+
Query: "create procedure p() create trigger trig before insert on t for each row begin select 1; end;",
2847+
ExpectedErrStr: "creating triggers in stored procedures is currently unsupported and will be added in a future release",
2848+
},
2849+
{
2850+
Query: "create procedure p() begin create trigger trig before insert on t for each row begin select 1; end; end;",
2851+
ExpectedErrStr: "creating triggers in stored procedures is currently unsupported and will be added in a future release",
2852+
},
2853+
},
2854+
},
2855+
{
2856+
Name: "procedure must not contain CREATE DB",
2857+
SetUpScript: []string{},
2858+
Assertions: []ScriptTestAssertion{
2859+
{
2860+
Query: "create procedure p() create database procdb;",
2861+
ExpectedErrStr: "creating databases in stored procedures is currently unsupported and will be added in a future release",
2862+
},
2863+
{
2864+
Query: "create procedure p() begin create database procdb; end;",
2865+
ExpectedErrStr: "creating databases in stored procedures is currently unsupported and will be added in a future release",
2866+
},
2867+
},
2868+
},
2869+
{
2870+
Name: "procedure must not contain CREATE VIEW",
2871+
SetUpScript: []string{},
2872+
Assertions: []ScriptTestAssertion{
2873+
{
2874+
Query: "create procedure p() create view v as select 1;",
2875+
ExpectedErrStr: "creating views in stored procedures is currently unsupported and will be added in a future release",
2876+
},
2877+
{
2878+
Query: "create procedure p() begin create view v as select 1; end;",
2879+
ExpectedErrStr: "creating views in stored procedures is currently unsupported and will be added in a future release",
2880+
},
2881+
},
2882+
},
28262883
}
28272884

28282885
var NoDbProcedureTests = []ScriptTestAssertion{

sql/analyzer/rule_ids.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const (
2121
resolveUnionsId // resolveUnions
2222
ValidateColumnDefaultsId // validateColumnDefaults
2323
validateCreateTriggerId // validateCreateTrigger
24-
validateCreateProcedureId // validateCreateProcedure
2524
validateReadOnlyDatabaseId // validateReadOnlyDatabase
2625
validateReadOnlyTransactionId // validateReadOnlyTransaction
2726
validateDatabaseSetId // validateDatabaseSet

sql/analyzer/ruleid_string.go

Lines changed: 56 additions & 57 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sql/analyzer/rules.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ var OnceBeforeDefault = []Rule{
3737
{validateCreateTableId, validateCreateTable},
3838
{validateAlterTableId, validateAlterTable},
3939
{validateExprSemId, validateExprSem},
40-
{validateCreateProcedureId, validateCreateProcedure},
4140
{resolveDropConstraintId, resolveDropConstraint},
4241
{resolveAlterColumnId, resolveAlterColumn},
4342
{validateDropTablesId, validateDropTables},

sql/analyzer/stored_procedures.go

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ package analyzer
1717
import (
1818
"fmt"
1919
"slices"
20-
"strings"
21-
22-
"gopkg.in/src-d/go-errors.v1"
2320

2421
"github.com/dolthub/go-mysql-server/sql"
2522
"github.com/dolthub/go-mysql-server/sql/expression"
@@ -84,11 +81,8 @@ func loadStoredProcedures(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan
8481

8582
// analyzeCreateProcedure checks the plan.CreateProcedure and returns a valid plan.Procedure or an error
8683
func analyzeCreateProcedure(ctx *sql.Context, a *Analyzer, cp *plan.CreateProcedure, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (*plan.Procedure, error) {
87-
err := validateStoredProcedure(ctx, cp.Procedure)
88-
if err != nil {
89-
return nil, err
90-
}
9184
var analyzedNode sql.Node
85+
var err error
9286
analyzedNode, _, err = analyzeProcedureBodies(ctx, a, cp.Procedure, false, scope, sel, qFlags)
9387
if err != nil {
9488
return nil, err
@@ -164,80 +158,6 @@ func analyzeProcedureBodies(ctx *sql.Context, a *Analyzer, node sql.Node, skipCa
164158
return node, transform.NewTree, nil
165159
}
166160

167-
// validateCreateProcedure handles CreateProcedure nodes, ensuring that all nodes in Procedure are supported.
168-
func validateCreateProcedure(ctx *sql.Context, a *Analyzer, node sql.Node, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
169-
cp, ok := node.(*plan.CreateProcedure)
170-
if !ok {
171-
return node, transform.SameTree, nil
172-
}
173-
174-
err := validateStoredProcedure(ctx, cp.Procedure)
175-
if err != nil {
176-
return nil, transform.SameTree, err
177-
}
178-
179-
return node, transform.SameTree, nil
180-
}
181-
182-
// validateStoredProcedure handles Procedure nodes, resolving references to the parameters, along with ensuring
183-
// that all logic contained within the stored procedure body is valid.
184-
func validateStoredProcedure(_ *sql.Context, proc *plan.Procedure) error {
185-
// For now, we don't support creating any of the following within stored procedures.
186-
// These will be removed in the future, but cause issues with the current execution plan.
187-
var err error
188-
spUnsupportedErr := errors.NewKind("creating %s in stored procedures is currently unsupported " +
189-
"and will be added in a future release")
190-
transform.Inspect(proc, func(n sql.Node) bool {
191-
switch n.(type) {
192-
case *plan.CreateTable:
193-
err = spUnsupportedErr.New("tables")
194-
case *plan.CreateTrigger:
195-
err = spUnsupportedErr.New("triggers")
196-
case *plan.CreateProcedure:
197-
err = spUnsupportedErr.New("procedures")
198-
case *plan.CreateDB:
199-
err = spUnsupportedErr.New("databases")
200-
case *plan.CreateForeignKey:
201-
err = spUnsupportedErr.New("foreign keys")
202-
case *plan.CreateIndex:
203-
err = spUnsupportedErr.New("indexes")
204-
case *plan.CreateView:
205-
err = spUnsupportedErr.New("views")
206-
default:
207-
return true
208-
}
209-
return false
210-
})
211-
if err != nil {
212-
return err
213-
}
214-
215-
transform.Inspect(proc, func(n sql.Node) bool {
216-
switch n := n.(type) {
217-
case *plan.Call:
218-
if proc.Name == strings.ToLower(n.Name) {
219-
err = sql.ErrProcedureRecursiveCall.New(proc.Name)
220-
}
221-
case *plan.LockTables: // Blocked in vitess, but this is for safety
222-
err = sql.ErrProcedureInvalidBodyStatement.New("LOCK TABLES")
223-
case *plan.UnlockTables: // Blocked in vitess, but this is for safety
224-
err = sql.ErrProcedureInvalidBodyStatement.New("UNLOCK TABLES")
225-
case *plan.Use: // Blocked in vitess, but this is for safety
226-
err = sql.ErrProcedureInvalidBodyStatement.New("USE")
227-
case *plan.LoadData:
228-
err = sql.ErrProcedureInvalidBodyStatement.New("LOAD DATA")
229-
default:
230-
return true
231-
}
232-
return false
233-
})
234-
if err != nil {
235-
return err
236-
}
237-
238-
return nil
239-
}
240-
241161
// applyProcedures applies the relevant stored procedures to the node given (if necessary).
242162
func applyProcedures(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
243163
if _, ok := n.(*plan.CreateProcedure); ok {

sql/planbuilder/create_ddl.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/dolthub/go-mysql-server/sql"
2626
"github.com/dolthub/go-mysql-server/sql/expression"
2727
"github.com/dolthub/go-mysql-server/sql/plan"
28+
"github.com/dolthub/go-mysql-server/sql/transform"
2829
"github.com/dolthub/go-mysql-server/sql/types"
2930
)
3031

@@ -132,6 +133,9 @@ func getCurrentUserForDefiner(ctx *sql.Context, definer string) string {
132133
}
133134

134135
func (b *Builder) buildCreateProcedure(inScope *scope, subQuery string, fullQuery string, c *ast.DDL) (outScope *scope) {
136+
b.qFlags.Set(sql.QFlagCreateProcedure)
137+
defer func() { b.qFlags.Unset(sql.QFlagCreateProcedure) }()
138+
135139
var params []plan.ProcedureParam
136140
for _, param := range c.ProcedureSpec.Params {
137141
var direction plan.ProcedureParamDirection
@@ -200,6 +204,20 @@ func (b *Builder) buildCreateProcedure(inScope *scope, subQuery string, fullQuer
200204
bodyStr := strings.TrimSpace(fullQuery[c.SubStatementPositionStart:c.SubStatementPositionEnd])
201205

202206
bodyScope := b.buildSubquery(inScope, c.ProcedureSpec.Body, bodyStr, fullQuery)
207+
b.validateStoredProcedure(bodyScope.node)
208+
209+
// Check for recursive calls to same procedure
210+
transform.Inspect(bodyScope.node, func(node sql.Node) bool {
211+
switch n := node.(type) {
212+
case *plan.Call:
213+
if strings.EqualFold(procName, n.Name) {
214+
b.handleErr(sql.ErrProcedureRecursiveCall.New(procName))
215+
}
216+
return false
217+
default:
218+
return true
219+
}
220+
})
203221

204222
var db sql.Database = nil
205223
dbName := c.ProcedureSpec.ProcName.Qualifier.String()

0 commit comments

Comments
 (0)