@@ -202,6 +202,8 @@ func (b *Builder) buildCreateProcedure(inScope *scope, subQuery string, fullQuer
202202 db = b .currentDb ()
203203 }
204204
205+ b .validateCreateProcedure (inScope , subQuery )
206+
205207 now := time .Now ()
206208 spd := sql.StoredProcedureDetails {
207209 Name : strings .ToLower (c .ProcedureSpec .ProcName .Name .String ()),
@@ -213,17 +215,123 @@ func (b *Builder) buildCreateProcedure(inScope *scope, subQuery string, fullQuer
213215
214216 bodyStr := strings .TrimSpace (fullQuery [c .SubStatementPositionStart :c .SubStatementPositionEnd ])
215217
216- // TODO: need to validate limit clauses for non-integers???
217- // TODO only return some errors??? somehow
218- BuildProcedureHelper (b .ctx , b .cat , true , inScope .push (), db , nil , spd )
219-
218+ // TODO: need to validate limit clauses for non-integers, but not other bugs
220219 // TODO: validate for recursion and other ddl here
221220
222221 outScope = inScope .push ()
223222 outScope .node = plan .NewCreateProcedure (db , spd , bodyStr )
224223 return outScope
225224}
226225
226+ func (b * Builder ) validateBlock (inScope * scope , stmts ast.Statements ) {
227+ for _ , s := range stmts {
228+ switch s .(type ) {
229+ case * ast.Declare :
230+ default :
231+ if inScope .procActive () {
232+ inScope .proc .NewState (dsBody )
233+ }
234+ }
235+ b .validateStatement (inScope , s )
236+ }
237+ }
238+
239+ func (b * Builder ) validateStatement (inScope * scope , stmt ast.Statement ) {
240+ switch s := stmt .(type ) {
241+ case * ast.DDL :
242+ b .handleErr (fmt .Errorf ("DDL in CREATE PROCEDURE not yet supported" ))
243+ case * ast.Declare :
244+ if s .Condition != nil {
245+ inScope .proc .AddCondition (plan .NewDeclareCondition (s .Condition .Name , 0 , "" ))
246+ } else if s .Variables != nil {
247+ typ , err := types .ColumnTypeToType (& s .Variables .VarType )
248+ if err != nil {
249+ b .handleErr (err )
250+ }
251+ for _ , v := range s .Variables .Names {
252+ varName := strings .ToLower (v .String ())
253+ param := expression .NewProcedureParam (varName , typ )
254+ inScope .proc .AddVar (param )
255+ inScope .newColumn (scopeColumn {col : varName , typ : typ , scalar : param })
256+ }
257+ } else if s .Cursor != nil {
258+ inScope .proc .AddCursor (s .Cursor .Name )
259+ } else if s .Handler != nil {
260+ switch s .Handler .ConditionValues [0 ].ValueType {
261+ case ast .DeclareHandlerCondition_NotFound :
262+ case ast .DeclareHandlerCondition_SqlException :
263+ default :
264+ err := sql .ErrUnsupportedSyntax .New (ast .String (s ))
265+ b .handleErr (err )
266+ }
267+ inScope .proc .AddHandler (nil )
268+ }
269+ case * ast.BeginEndBlock :
270+ blockScope := inScope .push ()
271+ blockScope .initProc ()
272+ blockScope .proc .AddLabel (s .Label , false )
273+ b .validateBlock (blockScope , s .Statements )
274+ case * ast.Loop :
275+ blockScope := inScope .push ()
276+ blockScope .initProc ()
277+ blockScope .proc .AddLabel (s .Label , true )
278+ b .validateBlock (blockScope , s .Statements )
279+ case * ast.Repeat :
280+ blockScope := inScope .push ()
281+ blockScope .initProc ()
282+ blockScope .proc .AddLabel (s .Label , true )
283+ b .validateBlock (blockScope , s .Statements )
284+ case * ast.While :
285+ blockScope := inScope .push ()
286+ blockScope .initProc ()
287+ blockScope .proc .AddLabel (s .Label , true )
288+ b .validateBlock (blockScope , s .Statements )
289+ case * ast.IfStatement :
290+ for _ , cond := range s .Conditions {
291+ b .validateBlock (inScope , cond .Statements )
292+ }
293+ if s .Else != nil {
294+ b .validateBlock (inScope , s .Else )
295+ }
296+ case * ast.Iterate :
297+ if exists , isLoop := inScope .proc .HasLabel (s .Label ); ! exists || ! isLoop {
298+ err := sql .ErrLoopLabelNotFound .New ("ITERATE" , s .Label )
299+ b .handleErr (err )
300+ }
301+ case * ast.Signal :
302+ if s .ConditionName != "" {
303+ signalName := strings .ToLower (s .ConditionName )
304+ condition := inScope .proc .GetCondition (signalName )
305+ if condition == nil {
306+ err := sql .ErrDeclareConditionNotFound .New (signalName )
307+ b .handleErr (err )
308+ }
309+ }
310+ }
311+ }
312+
313+ func (b * Builder ) validateCreateProcedure (inScope * scope , createStmt string ) {
314+ stmt , _ , _ , _ := b .parser .ParseWithOptions (b .ctx , createStmt , ';' , false , b .parserOpts )
315+ procStmt := stmt .(* ast.DDL )
316+
317+ // validate parameters
318+ procParams := b .buildProcedureParams (procStmt .ProcedureSpec .Params )
319+ paramNames := make (map [string ]struct {})
320+ for _ , param := range procParams {
321+ paramName := strings .ToLower (param .Name )
322+ if _ , ok := paramNames [paramName ]; ok {
323+ b .handleErr (sql .ErrDeclareVariableDuplicate .New (paramName ))
324+ }
325+ paramNames [param .Name ] = struct {}{}
326+ }
327+ // TODO: add params to tmpScope?
328+
329+ bodyStmt := procStmt .ProcedureSpec .Body
330+ b .validateStatement (inScope , bodyStmt )
331+
332+ // TODO: check for limit clauses that are not integers
333+ }
334+
227335func (b * Builder ) buildCreateEvent (inScope * scope , subQuery string , fullQuery string , c * ast.DDL ) (outScope * scope ) {
228336 outScope = inScope .push ()
229337 eventSpec := c .EventSpec
0 commit comments