Use fast path in plpgsql's RETURN/RETURN NEXT in more cases.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Feb 2015 20:28:40 +0000 (15:28 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Feb 2015 20:28:48 +0000 (15:28 -0500)
exec_stmt_return() and exec_stmt_return_next() have fast-path code for
handling a simple variable reference (i.e. "return var") without going
through the full expression evaluation machinery.  For some reason,
pl_gram.y was under the impression that this fast path only applied for
record/row variables; but in reality code for handling regular scalar
variables has been there all along.  Adjusting the logic to allow that
code to be used actually results in a net savings of code in pl_gram.y
(by eliminating some redundancy), and it buys a measurable though not
very impressive amount of speedup.

Noted while fooling with my expanded-array patch, wherein this makes a much
bigger difference because it enables returning an expanded array variable
without an extra flattening step.  But AFAICS this is a win regardless,
so commit it separately.

src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_gram.y

index f5ed02a048b2c70ad9fc1aad3636e32fc3550c47..b7e3bc42354085c15d817d75f66486f753af5f85 100644 (file)
@@ -2452,8 +2452,9 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
        estate->retisnull = true;
 
        /*
-        * This special-case path covers record/row variables in fn_retistuple
-        * functions, as well as functions with one or more OUT parameters.
+        * Special case path when the RETURN expression is a simple variable
+        * reference; in particular, this path is always taken in functions with
+        * one or more OUT parameters.
         */
        if (stmt->retvarno >= 0)
        {
@@ -2576,8 +2577,9 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
        natts = tupdesc->natts;
 
        /*
-        * This special-case path covers record/row variables in fn_retistuple
-        * functions, as well as functions with one or more OUT parameters.
+        * Special case path when the RETURN NEXT expression is a simple variable
+        * reference; in particular, this path is always taken in functions with
+        * one or more OUT parameters.
         */
        if (stmt->retvarno >= 0)
        {
index 590aac501b08cc21d3b22108ace63271e8bfcafa..506a313f7015ff21206cf60effe01d09879bc3cf 100644 (file)
@@ -3036,16 +3036,17 @@ make_return_stmt(int location)
                                         errmsg("RETURN cannot have a parameter in function returning void"),
                                         parser_errposition(yylloc)));
        }
-       else if (plpgsql_curr_compile->fn_retistuple)
+       else
        {
                /*
-                * We want to special-case simple row or record references for
-                * efficiency.  So peek ahead to see if that's what we have.
+                * We want to special-case simple variable references for efficiency.
+                * So peek ahead to see if that's what we have.
                 */
                int             tok = yylex();
 
                if (tok == T_DATUM && plpgsql_peek() == ';' &&
-                       (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+                       (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+                        yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
                         yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
                {
                        new->retvarno = yylval.wdatum.datum->dno;
@@ -3055,19 +3056,16 @@ make_return_stmt(int location)
                }
                else
                {
-                       /* Not (just) a row/record name, so treat as expression */
+                       /*
+                        * Not (just) a variable name, so treat as expression.
+                        *
+                        * Note that a well-formed expression is _required_ here;
+                        * anything else is a compile-time error.
+                        */
                        plpgsql_push_back_token(tok);
                        new->expr = read_sql_expression(';', ";");
                }
        }
-       else
-       {
-               /*
-                * Note that a well-formed expression is _required_ here;
-                * anything else is a compile-time error.
-                */
-               new->expr = read_sql_expression(';', ";");
-       }
 
        return (PLpgSQL_stmt *) new;
 }
@@ -3099,16 +3097,17 @@ make_return_next_stmt(int location)
                                         parser_errposition(yylloc)));
                new->retvarno = plpgsql_curr_compile->out_param_varno;
        }
-       else if (plpgsql_curr_compile->fn_retistuple)
+       else
        {
                /*
-                * We want to special-case simple row or record references for
-                * efficiency.  So peek ahead to see if that's what we have.
+                * We want to special-case simple variable references for efficiency.
+                * So peek ahead to see if that's what we have.
                 */
                int             tok = yylex();
 
                if (tok == T_DATUM && plpgsql_peek() == ';' &&
-                       (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+                       (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+                        yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
                         yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
                {
                        new->retvarno = yylval.wdatum.datum->dno;
@@ -3118,13 +3117,16 @@ make_return_next_stmt(int location)
                }
                else
                {
-                       /* Not (just) a row/record name, so treat as expression */
+                       /*
+                        * Not (just) a variable name, so treat as expression.
+                        *
+                        * Note that a well-formed expression is _required_ here;
+                        * anything else is a compile-time error.
+                        */
                        plpgsql_push_back_token(tok);
                        new->expr = read_sql_expression(';', ";");
                }
        }
-       else
-               new->expr = read_sql_expression(';', ";");
 
        return (PLpgSQL_stmt *) new;
 }