@@ -363,10 +363,11 @@ type plBlock struct {
363363 // cursor before it is opened.
364364 cursors map [ast.Variable ]ast.CursorDeclaration
365365
366- // hiddenVars is an ordered list of *hidden* variables that were not declared
367- // by the user, but are used internally by the builder. Hidden variables are
368- // not visible to the user, and are identified by their metadata name. They
369- // can only be assigned to by directly calling assignToHiddenVariable().
366+ // hiddenVars lists the names of *hidden* variables that were not declared by
367+ // the user, but are used internally by the builder. Hidden variables are
368+ // not visible to the user, and are identified only by their ordinal position.
369+ // They can only be assigned to by directly calling assignToHiddenVariable().
370+ // The name of a hidden variable is only used for display purposes.
370371 //
371372 // As an example, the internal counter variable for a FOR loop is a
372373 // hidden variable.
@@ -376,7 +377,9 @@ type plBlock struct {
376377 hiddenVars []string
377378
378379 // hiddenVarTypes maps from each hidden variable in the scope to its type.
379- hiddenVarTypes map [string ]* types.T
380+ // It is a list instead of a map because hidden variables are only identified
381+ // by their ordinal position.
382+ hiddenVarTypes []* types.T
380383
381384 // hasExceptionHandler tracks whether this block has an exception handler.
382385 hasExceptionHandler bool
@@ -1358,27 +1361,22 @@ func (b *plpgsqlBuilder) handleIntForLoop(
13581361 stepName = "_loop_step"
13591362 counterName = "_loop_counter"
13601363 )
1361- b .addHiddenVariable (lowerName , types .Int )
1362- b .addHiddenVariable (upperName , types .Int )
1363- b .addHiddenVariable (stepName , types .Int )
1364- b .addHiddenVariable (counterName , types .Int )
1364+ // User-visible variable must be declared before hidden variables in a block.
13651365 b .addVariable (forLoop .Target [0 ], types .Int )
1366+ lowerOrd := b .addHiddenVariable (lowerName , types .Int )
1367+ upperOrd := b .addHiddenVariable (upperName , types .Int )
1368+ stepOrd := b .addHiddenVariable (stepName , types .Int )
1369+ counterOrd := b .addHiddenVariable (counterName , types .Int )
13661370
13671371 // Initialize the constant bounds and step size.
13681372 stepSize := control .Step
13691373 if stepSize == nil {
13701374 // The default step size is 1.
13711375 stepSize = tree .NewDInt (1 )
13721376 }
1373- s = b .assignToHiddenVariable (s , lowerName , control .Lower )
1374- s = b .assignToHiddenVariable (s , upperName , control .Upper )
1375- s = b .assignToHiddenVariable (s , stepName , stepSize )
1376-
1377- // When referencing a hidden variable, make sure to check the correct scope,
1378- // as different columns can represent the variable depending on context.
1379- refHiddenVar := func (s * scope , name string ) * scopeColumn {
1380- return s .findAnonymousColumnWithMetadataName (name )
1381- }
1377+ s = b .assignToHiddenVariable (s , lowerOrd , control .Lower )
1378+ s = b .assignToHiddenVariable (s , upperOrd , control .Upper )
1379+ s = b .assignToHiddenVariable (s , stepOrd , stepSize )
13821380
13831381 // Add runtime checks for the bounds and step size.
13841382 branches := make (memo.ScalarListExpr , 0 , 4 )
@@ -1393,21 +1391,21 @@ func (b *plpgsqlBuilder) handleIntForLoop(
13931391 message := fmt .Sprintf ("%s of FOR loop cannot be null" , context )
13941392 addCheck (message , pgcode .NullValueNotAllowed .String (), checkCond )
13951393 }
1396- addNullCheck ("lower bound" /* context */ , refHiddenVar ( s , lowerName ))
1397- addNullCheck ("upper bound" /* context */ , refHiddenVar ( s , upperName ))
1398- addNullCheck ("BY value" /* context */ , refHiddenVar ( s , stepName ))
1394+ addNullCheck ("lower bound" /* context */ , s . findFuncArgCol ( lowerOrd ))
1395+ addNullCheck ("upper bound" /* context */ , s . findFuncArgCol ( upperOrd ))
1396+ addNullCheck ("BY value" /* context */ , s . findFuncArgCol ( stepOrd ))
13991397 addCheck ("BY value of FOR loop must be greater than zero" , /* message */
14001398 pgcode .InvalidParameterValue .String (),
14011399 b .buildSQLExpr (& tree.ComparisonExpr {
14021400 Operator : treecmp .MakeComparisonOperator (treecmp .LE ),
1403- Left : refHiddenVar ( s , stepName ),
1401+ Left : s . findFuncArgCol ( stepOrd ),
14041402 Right : tree .DZero ,
14051403 }, types .Bool , s ))
14061404 b .addRuntimeCheck (s , branches , raiseErrArgs )
14071405
14081406 // Initialize the loop counter target variables with the lower bound.
1409- s = b .assignToHiddenVariable (s , counterName , refHiddenVar ( s , lowerName ))
1410- s = b .addPLpgSQLAssign (s , forLoop .Target [0 ], refHiddenVar ( s , lowerName ), noIndirection )
1407+ s = b .assignToHiddenVariable (s , counterOrd , s . findFuncArgCol ( lowerOrd ))
1408+ s = b .addPLpgSQLAssign (s , forLoop .Target [0 ], s . findFuncArgCol ( lowerOrd ), noIndirection )
14111409
14121410 // The looping will be implemented by two continuations: one to execute the
14131411 // loop body, and one to increment the counter variable. The loop body and
@@ -1429,8 +1427,8 @@ func (b *plpgsqlBuilder) handleIntForLoop(
14291427 }
14301428 cond := & tree.ComparisonExpr {
14311429 Operator : cmpOp ,
1432- Left : refHiddenVar ( loopCon .s , counterName ),
1433- Right : refHiddenVar ( loopCon .s , upperName ),
1430+ Left : loopCon .s . findFuncArgCol ( counterOrd ),
1431+ Right : loopCon .s . findFuncArgCol ( upperOrd ),
14341432 }
14351433 ifStmt := & ast.If {Condition : cond , ThenBody : forLoop .Body , ElseBody : []ast.Statement {& ast.Exit {}}}
14361434 b .appendPlpgSQLStmts (& loopCon , []ast.Statement {ifStmt })
@@ -1449,12 +1447,12 @@ func (b *plpgsqlBuilder) handleIntForLoop(
14491447 }
14501448 inc := & tree.BinaryExpr {
14511449 Operator : binOp ,
1452- Left : refHiddenVar ( incScope , counterName ),
1453- Right : refHiddenVar ( incScope , stepName ),
1450+ Left : incScope . findFuncArgCol ( counterOrd ),
1451+ Right : incScope . findFuncArgCol ( stepOrd ),
14541452 }
1455- incScope = b .assignToHiddenVariable (incScope , counterName , inc )
1453+ incScope = b .assignToHiddenVariable (incScope , counterOrd , inc )
14561454 incScope = b .addPLpgSQLAssign (
1457- incScope , forLoop .Target [0 ], refHiddenVar ( incScope , counterName ), noIndirection ,
1455+ incScope , forLoop .Target [0 ], incScope . findFuncArgCol ( counterOrd ), noIndirection ,
14581456 )
14591457 // Call recursively into the loop body continuation.
14601458 incScope = b .callContinuation (& loopCon , incScope )
@@ -1578,20 +1576,20 @@ func (b *plpgsqlBuilder) addPLpgSQLAssign(
15781576
15791577// assignToHiddenVariable is similar to addPLpgSQLAssign, but it assigns to a
15801578// hidden variable that is not visible to the user.
1581- func (b * plpgsqlBuilder ) assignToHiddenVariable (inScope * scope , name string , val ast.Expr ) * scope {
1582- typ , ord := b .resolveHiddenVariableForAssign ( name )
1579+ func (b * plpgsqlBuilder ) assignToHiddenVariable (inScope * scope , ord int , val ast.Expr ) * scope {
1580+ typ , name := b .resolveVariableForAssignByOrd ( ord )
15831581 assignScope := inScope .push ()
15841582 for i := range inScope .cols {
15851583 col := & inScope .cols [i ]
1586- if col .name . MetadataName () == name {
1584+ if col .getParamOrd () == ord {
15871585 // Allow the assignment to shadow previous values for this column.
15881586 continue
15891587 }
15901588 // If the column is not an outer column, add the column as a pass-through
15911589 // column from the previous scope.
15921590 assignScope .appendColumn (col )
15931591 }
1594- colName := scopeColName ("" ).WithMetadataName (name )
1592+ colName := scopeColName ("" ).WithMetadataName (string ( name ) )
15951593 scalar := b .buildSQLExpr (val , typ , inScope )
15961594 b .addBarrierIfVolatile (inScope , scalar )
15971595 col := b .ob .synthesizeColumn (assignScope , colName , typ , nil , scalar )
@@ -2240,10 +2238,10 @@ func (b *plpgsqlBuilder) makeContinuation(conName string) continuation {
22402238 for _ , name := range block .vars {
22412239 addParam (scopeColName (name ), block .varTypes [name ])
22422240 }
2243- for _ , name := range block .hiddenVars {
2241+ for varIdx , name := range block .hiddenVars {
22442242 // Do not give the column constructed for a hidden variable a reference
22452243 // name, since hidden variables cannot be referenced by the user.
2246- addParam (scopeColName ("" ).WithMetadataName (name ), block .hiddenVarTypes [name ])
2244+ addParam (scopeColName ("" ).WithMetadataName (name ), block .hiddenVarTypes [varIdx ])
22472245 }
22482246 }
22492247 b .ensureScopeHasExpr (s )
@@ -2378,7 +2376,7 @@ func (b *plpgsqlBuilder) makeContinuationArgs(con *continuation, s *scope) memo.
23782376 args = append (args , b .ob .factory .ConstructVariable (source .(* scopeColumn ).id ))
23792377 }
23802378 for _ , name := range block .hiddenVars {
2381- col := s .findAnonymousColumnWithMetadataName ( name )
2379+ col := s .findFuncArgCol ( len ( args ) )
23822380 if col == nil {
23832381 panic (errors .AssertionFailedf ("hidden variable %s not found" , name ))
23842382 }
@@ -2496,12 +2494,12 @@ func (b *plpgsqlBuilder) coerceType(scalar opt.ScalarExpr, typ *types.T) opt.Sca
24962494// the given name, as well as its ordinal position within the set of all
24972495// variables in the current scope. It throws an error if no such variable
24982496// exists.
2499- func (b * plpgsqlBuilder ) resolveVariableForAssign (name ast.Variable ) (varTyp * types.T , varIdx int ) {
2497+ func (b * plpgsqlBuilder ) resolveVariableForAssign (name ast.Variable ) (typ * types.T , ord int ) {
25002498 // Search the blocks in reverse order to ensure that more recent declarations
25012499 // are encountered first.
25022500 for i := len (b .blocks ) - 1 ; i >= 0 ; i -- {
25032501 block := & b .blocks [i ]
2504- typ , ok := block .varTypes [name ]
2502+ varTyp , ok := block .varTypes [name ]
25052503 if ! ok {
25062504 continue
25072505 }
@@ -2512,42 +2510,49 @@ func (b *plpgsqlBuilder) resolveVariableForAssign(name ast.Variable) (varTyp *ty
25122510 }
25132511 // Get the ordinal position of the variable within the set of variables in
25142512 // the current scope.
2515- varIdx = b .variableCount (i )
2513+ ord = b .variableCount (i )
25162514 for j := range block .vars {
25172515 if block .vars [j ] == name {
2518- varIdx += j
2516+ ord += j
25192517 break
25202518 }
25212519 }
2522- return typ , varIdx
2520+ return varTyp , ord
25232521 }
25242522 panic (pgerror .Newf (pgcode .Syntax , "\" %s\" is not a known variable" , name ))
25252523}
25262524
2527- // resolveHiddenVariableForAssign is similar to resolveVariableForAssign, but
2528- // applies to hidden variables, which are identified only by their name in the
2529- // query's metadata. It panics if the hidden variable is not found.
2530- func (b * plpgsqlBuilder ) resolveHiddenVariableForAssign (name string ) (varTyp * types.T , varIdx int ) {
2531- // Search the blocks in reverse order to ensure that more recent declarations
2532- // are encountered first.
2533- for i := len (b .blocks ) - 1 ; i >= 0 ; i -- {
2525+ // resolveVariableForAssignByOrd is similar to resolveVariableForAssign, but
2526+ // resolves a variable by its ordinal position in the list of all variables in
2527+ // the current scope. It returns the name and type of the variable. It panics if
2528+ // the variable is not found.
2529+ //
2530+ // NOTE: unlike resolveVariableForAssign, resolveVariableForAssignByOrd is able
2531+ // to resolve hidden variables.
2532+ func (b * plpgsqlBuilder ) resolveVariableForAssignByOrd (ord int ) (typ * types.T , name ast.Variable ) {
2533+ originalOrd := ord
2534+ for i := range b .blocks {
25342535 block := & b .blocks [i ]
2535- typ , ok := block .hiddenVarTypes [name ]
2536- if ! ok {
2537- continue
2538- }
2539- // Get the ordinal position of the variable within the set of variables in
2540- // the current scope.
2541- varIdx = b .variableCount (i ) + len (block .vars )
2542- for j := range block .hiddenVars {
2543- if block .hiddenVars [j ] == name {
2544- varIdx += j
2545- break
2536+ if ord < len (block .vars ) {
2537+ // This is a user-visible variable.
2538+ name = block .vars [ord ]
2539+ if block .constants != nil {
2540+ if _ , ok := block .constants [name ]; ok {
2541+ panic (pgerror .Newf (pgcode .ErrorInAssignment , "variable \" %s\" is declared CONSTANT" , name ))
2542+ }
25462543 }
2544+ return block .varTypes [name ], name
2545+ }
2546+ ord -= len (block .vars )
2547+ if ord < len (block .hiddenVars ) {
2548+ // This is a hidden variable.
2549+ return block .hiddenVarTypes [ord ], ast .Variable (block .hiddenVars [ord ])
25472550 }
2548- return typ , varIdx
2551+ ord -= len ( block . hiddenVars )
25492552 }
2550- panic (errors .AssertionFailedf ("hidden variable %s not found" , name ))
2553+ // Increment the ordinal for the error message, since the placeholder syntax
2554+ // is 1-based.
2555+ panic (pgerror .Newf (pgcode .Syntax , "\" $%d\" is not a known variable" , originalOrd + 1 ))
25512556}
25522557
25532558// projectTupleAsIntoTarget maps from the elements of a tuple column to the
@@ -2739,6 +2744,9 @@ func (b *plpgsqlBuilder) getContinuation(
27392744// PL/pgSQL block scope.
27402745func (b * plpgsqlBuilder ) addVariable (name ast.Variable , typ * types.T ) {
27412746 curBlock := b .block ()
2747+ if len (curBlock .hiddenVars ) > 0 {
2748+ panic (errors .AssertionFailedf ("hidden variables must be declared after all visible variables" ))
2749+ }
27422750 if _ , ok := curBlock .varTypes [name ]; ok {
27432751 panic (pgerror .Newf (pgcode .Syntax , "duplicate declaration at or near \" %s\" " , name ))
27442752 }
@@ -2756,14 +2764,15 @@ func (b *plpgsqlBuilder) addVariable(name ast.Variable, typ *types.T) {
27562764}
27572765
27582766// addHiddenVariable adds a hidden variable with the given (metadata) name and
2759- // type to the current PL/pgSQL block scope.
2760- func (b * plpgsqlBuilder ) addHiddenVariable (metadataName string , typ * types.T ) {
2767+ // type to the current PL/pgSQL block scope. It returns the ordinal position of
2768+ // the variable within the set of all variables in the current scope. This will
2769+ // be used to uniquely identify the hidden variable going forward.
2770+ func (b * plpgsqlBuilder ) addHiddenVariable (metadataName string , typ * types.T ) (ord int ) {
2771+ ord = b .variableCount (len (b .blocks ))
27612772 curBlock := b .block ()
27622773 curBlock .hiddenVars = append (curBlock .hiddenVars , metadataName )
2763- if curBlock .hiddenVarTypes == nil {
2764- curBlock .hiddenVarTypes = make (map [string ]* types.T )
2765- }
2766- curBlock .hiddenVarTypes [metadataName ] = typ
2774+ curBlock .hiddenVarTypes = append (curBlock .hiddenVarTypes , typ )
2775+ return ord
27672776}
27682777
27692778// block returns the block for the current PL/pgSQL block.
0 commit comments