Support RETURN QUERY EXECUTE in plpgsql.
authorTom Lane
Sat, 3 May 2008 00:11:36 +0000 (00:11 +0000)
committerTom Lane
Sat, 3 May 2008 00:11:36 +0000 (00:11 +0000)
Pavel Stehule

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/plpgsql.h
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 8eac671fabfeb67d9266644775cc68b58907ac7f..1065eddc74efb52c270bee0a89c0e5b9d4e9045f 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
   <application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language
@@ -1467,6 +1467,7 @@ RETURN expression;
 
 RETURN NEXT expression;
 RETURN QUERY query;
+RETURN QUERY EXECUTE command-string  USING expression , ... ;
 
 
      
@@ -1500,6 +1501,14 @@ RETURN QUERY query;
       let control reach the end of the function).
      
 
+     
+      RETURN QUERY has a variant
+      RETURN QUERY EXECUTE, which specifies the
+      query to be executed dynamically.  Parameter expressions can
+      be inserted into the computed query string via USING,
+      in just the same way as in the EXECUTE command.
+     
+
      
       If you declared the function with output parameters, write just
       RETURN NEXT with no expression.  On each
@@ -1544,7 +1553,6 @@ SELECT * FROM getallfoo();
       Note that functions using RETURN NEXT or
       RETURN QUERY must be called as a table source in
       a FROM clause.
-
      
 
      
index 495b625a450835d4cfa0f8f1fcbc611f281547f8..979ebee3ca330a5de962c0ebedc0540db67d6346 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.110 2008/04/06 23:43:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.111 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2239,6 +2239,7 @@ static PLpgSQL_stmt *
 make_return_query_stmt(int lineno)
 {
    PLpgSQL_stmt_return_query *new;
+   int         tok;
 
    if (!plpgsql_curr_compile->fn_retset)
        yyerror("cannot use RETURN QUERY in a non-SETOF function");
@@ -2246,7 +2247,32 @@ make_return_query_stmt(int lineno)
    new = palloc0(sizeof(PLpgSQL_stmt_return_query));
    new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
    new->lineno = lineno;
-   new->query = read_sql_stmt("");
+
+   /* check for RETURN QUERY EXECUTE */
+   if ((tok = yylex()) != K_EXECUTE)
+   {
+       /* ordinary static query */
+       plpgsql_push_back_token(tok);
+       new->query = read_sql_stmt("");
+   }
+   else
+   {
+       /* dynamic SQL */
+       int     term;
+
+       new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
+                                            &term);
+       if (term == K_USING)
+       {
+           do
+           {
+               PLpgSQL_expr *expr;
+
+               expr = read_sql_expression2(',', ';', ", or ;", &term);
+               new->params = lappend(new->params, expr);
+           } while (term == ',');
+       }
+   }
 
    return (PLpgSQL_stmt *) new;
 }
index a1774bf50340bb43a204823fd4c5963f56c36839..64845f296564285964228144abc416219cf4c5c7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.210 2008/04/17 21:37:28 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.211 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -197,6 +197,8 @@ static void assign_text_var(PLpgSQL_var *var, const char *str);
 static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
                                                  List *params);
 static void free_params_data(PreparedParamsData *ppd);
+static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
+                                       PLpgSQL_expr *query, List *params);
 
 
 /* ----------
@@ -1968,7 +1970,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
                    PLpgSQL_row *row = (PLpgSQL_row *) retvar;
 
                    Assert(row->rowtupdesc);
-                   estate->retval = 
+                   estate->retval =
                        PointerGetDatum(make_tuple_from_row(estate, row,
                                                            row->rowtupdesc));
                    if (DatumGetPointer(estate->retval) == NULL) /* should not happen */
@@ -2189,7 +2191,18 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
    if (estate->tuple_store == NULL)
        exec_init_tuple_store(estate);
 
-   exec_run_select(estate, stmt->query, 0, &portal);
+   if (stmt->query != NULL)
+   {
+       /* static query */
+       exec_run_select(estate, stmt->query, 0, &portal);
+   }
+   else
+   {
+       /* RETURN QUERY EXECUTE */
+       Assert(stmt->dynquery != NULL);
+       portal = exec_dynquery_with_params(estate, stmt->dynquery,
+                                          stmt->params);
+   }
 
    if (!compatible_tupdesc(estate->rettupdesc, portal->tupDesc))
        ereport(ERROR,
@@ -2841,58 +2854,10 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
 static int
 exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
 {
-   Datum       query;
-   bool        isnull;
-   Oid         restype;
-   char       *querystr;
    Portal      portal;
    int         rc;
 
-   /*
-    * Evaluate the string expression after the EXECUTE keyword. It's result
-    * is the querystring we have to execute.
-    */
-   query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
-   if (isnull)
-       ereport(ERROR,
-               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                errmsg("cannot EXECUTE a null querystring")));
-
-   /* Get the C-String representation */
-   querystr = convert_value_to_string(query, restype);
-
-   exec_eval_cleanup(estate);
-
-   /*
-    * Open an implicit cursor for the query.  We use SPI_cursor_open_with_args
-    * even when there are no params, because this avoids making and freeing
-    * one copy of the plan.
-    */
-   if (stmt->params)
-   {
-       PreparedParamsData *ppd;
-
-       ppd = exec_eval_using_params(estate, stmt->params);
-       portal = SPI_cursor_open_with_args(NULL,
-                                          querystr,
-                                          ppd->nargs, ppd->types,
-                                          ppd->values, ppd->nulls,
-                                          estate->readonly_func, 0);
-       free_params_data(ppd);
-   }
-   else
-   {
-       portal = SPI_cursor_open_with_args(NULL,
-                                          querystr,
-                                          0, NULL,
-                                          NULL, NULL,
-                                          estate->readonly_func, 0);
-   }
-
-   if (portal == NULL)
-       elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
-            querystr, SPI_result_code_string(SPI_result));
-   pfree(querystr);
+   portal = exec_dynquery_with_params(estate, stmt->query, stmt->params);
 
    /*
     * Execute the loop
@@ -5208,3 +5173,65 @@ free_params_data(PreparedParamsData *ppd)
 
    pfree(ppd);
 }
+
+/*
+ * Open portal for dynamic query
+ */
+static Portal
+exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
+                         List *params)
+{
+   Portal      portal;
+   Datum       query;
+   bool        isnull;
+   Oid         restype;
+   char       *querystr;
+
+   /*
+    * Evaluate the string expression after the EXECUTE keyword. Its result
+    * is the querystring we have to execute.
+    */
+   query = exec_eval_expr(estate, dynquery, &isnull, &restype);
+   if (isnull)
+       ereport(ERROR,
+               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                errmsg("cannot EXECUTE a null querystring")));
+
+   /* Get the C-String representation */
+   querystr = convert_value_to_string(query, restype);
+
+   exec_eval_cleanup(estate);
+
+   /*
+    * Open an implicit cursor for the query.  We use SPI_cursor_open_with_args
+    * even when there are no params, because this avoids making and freeing
+    * one copy of the plan.
+    */
+   if (params)
+   {
+       PreparedParamsData *ppd;
+
+       ppd = exec_eval_using_params(estate, params);
+       portal = SPI_cursor_open_with_args(NULL,
+                                          querystr,
+                                          ppd->nargs, ppd->types,
+                                          ppd->values, ppd->nulls,
+                                          estate->readonly_func, 0);
+       free_params_data(ppd);
+   }
+   else
+   {
+       portal = SPI_cursor_open_with_args(NULL,
+                                          querystr,
+                                          0, NULL,
+                                          NULL, NULL,
+                                          estate->readonly_func, 0);
+   }
+
+   if (portal == NULL)
+       elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
+            querystr, SPI_result_code_string(SPI_result));
+   pfree(querystr);
+
+   return portal;
+}
index 1a68c9339f2f7bb6969af2ffa5ed2e94633bc23d..f7624a6c33d6cac1f548482d072671c9eb1afa12 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.69 2008/04/06 23:43:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.70 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -963,9 +963,37 @@ static void
 dump_return_query(PLpgSQL_stmt_return_query *stmt)
 {
    dump_ind();
-   printf("RETURN QUERY ");
-   dump_expr(stmt->query);
-   printf("\n");
+   if (stmt->query)
+   {
+       printf("RETURN QUERY ");
+       dump_expr(stmt->query);
+       printf("\n");
+   }
+   else
+   {
+       printf("RETURN QUERY EXECUTE ");
+       dump_expr(stmt->dynquery);
+       printf("\n");
+       if (stmt->params != NIL)
+       {
+           ListCell   *lc;
+           int         i;
+
+           dump_indent += 2;
+           dump_ind();
+           printf("    USING\n");
+           dump_indent += 2;
+           i = 1;
+           foreach(lc, stmt->params)
+           {
+               dump_ind();
+               printf("    parameter $%d: ", i++);
+               dump_expr((PLpgSQL_expr *) lfirst(lc));
+               printf("\n");
+           }
+           dump_indent -= 4;
+       }
+   }
 }
 
 static void
index a50d5721f437dc93b2e9e9cd5eb4fe61dd5b3cc7..f1206932bb5e871af6366d2b25d87831334c28a3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.97 2008/04/06 23:43:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.98 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -529,7 +529,9 @@ typedef struct
 {                              /* RETURN QUERY statement */
    int         cmd_type;
    int         lineno;
-   PLpgSQL_expr *query;
+   PLpgSQL_expr *query;        /* if static query */
+   PLpgSQL_expr *dynquery;     /* if dynamic query (RETURN QUERY EXECUTE) */
+   List       *params;         /* USING arguments for dynamic query */
 } PLpgSQL_stmt_return_query;
 
 typedef struct
index 018c8c2b50f974ade5fc0a1f409e8d9c6b7d7667..da987b22e460218f406c4ee5982d30f3449b018a 100644 (file)
@@ -3267,3 +3267,21 @@ end;
 $$ language plpgsql;
 ERROR:  cursor FOR loop must use a bound cursor variable
 CONTEXT:  compile of PL/pgSQL function "forc_bad" near line 4
+-- return query execute
+create or replace function return_dquery()
+returns setof int as $$
+begin
+  return query execute 'select * from (values(10),(20)) f';
+  return query execute 'select * from (values($1),($2)) f' using 40,50;
+end;
+$$ language plpgsql;
+select * from return_dquery();
+ return_dquery 
+---------------
+            10
+            20
+            40
+            50
+(4 rows)
+
+drop function return_dquery();
index 066ccbeba639bcfebe29a4a893956e2f476cc099..b0799dcdc705d53acdd5ad99570cea7dc869a04a 100644 (file)
@@ -2669,3 +2669,17 @@ begin
   end loop;
 end;
 $$ language plpgsql;
+
+-- return query execute
+
+create or replace function return_dquery()
+returns setof int as $$
+begin
+  return query execute 'select * from (values(10),(20)) f';
+  return query execute 'select * from (values($1),($2)) f' using 40,50;
+end;
+$$ language plpgsql;
+
+select * from return_dquery();
+
+drop function return_dquery();