Skip to content

Commit 4739159

Browse files
committed
Support RETURN QUERY EXECUTE in plpgsql.
Pavel Stehule
1 parent 511b798 commit 4739159

File tree

7 files changed

+185
-62
lines changed

7 files changed

+185
-62
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.127 2008/04/06 23:43:29 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.128 2008/05/03 00:11:36 tgl Exp $ -->
22

33
<chapter id="plpgsql">
44
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1467,6 +1467,7 @@ RETURN <replaceable>expression</replaceable>;
14671467
<synopsis>
14681468
RETURN NEXT <replaceable>expression</replaceable>;
14691469
RETURN QUERY <replaceable>query</replaceable>;
1470+
RETURN QUERY EXECUTE <replaceable class="command">command-string</replaceable> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional>;
14701471
</synopsis>
14711472

14721473
<para>
@@ -1500,6 +1501,14 @@ RETURN QUERY <replaceable>query</replaceable>;
15001501
let control reach the end of the function).
15011502
</para>
15021503

1504+
<para>
1505+
<command>RETURN QUERY</command> has a variant
1506+
<command>RETURN QUERY EXECUTE</command>, which specifies the
1507+
query to be executed dynamically. Parameter expressions can
1508+
be inserted into the computed query string via <literal>USING</>,
1509+
in just the same way as in the <command>EXECUTE</> command.
1510+
</para>
1511+
15031512
<para>
15041513
If you declared the function with output parameters, write just
15051514
<command>RETURN NEXT</command> with no expression. On each
@@ -1544,7 +1553,6 @@ SELECT * FROM getallfoo();
15441553
Note that functions using <command>RETURN NEXT</command> or
15451554
<command>RETURN QUERY</command> must be called as a table source in
15461555
a <literal>FROM</literal> clause.
1547-
15481556
</para>
15491557

15501558
<note>

src/pl/plpgsql/src/gram.y

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.110 2008/04/06 23:43:29 tgl Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.111 2008/05/03 00:11:36 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -2239,14 +2239,40 @@ static PLpgSQL_stmt *
22392239
make_return_query_stmt(int lineno)
22402240
{
22412241
PLpgSQL_stmt_return_query *new;
2242+
int tok;
22422243

22432244
if (!plpgsql_curr_compile->fn_retset)
22442245
yyerror("cannot use RETURN QUERY in a non-SETOF function");
22452246

22462247
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
22472248
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
22482249
new->lineno = lineno;
2249-
new->query = read_sql_stmt("");
2250+
2251+
/* check for RETURN QUERY EXECUTE */
2252+
if ((tok = yylex()) != K_EXECUTE)
2253+
{
2254+
/* ordinary static query */
2255+
plpgsql_push_back_token(tok);
2256+
new->query = read_sql_stmt("");
2257+
}
2258+
else
2259+
{
2260+
/* dynamic SQL */
2261+
int term;
2262+
2263+
new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
2264+
&term);
2265+
if (term == K_USING)
2266+
{
2267+
do
2268+
{
2269+
PLpgSQL_expr *expr;
2270+
2271+
expr = read_sql_expression2(',', ';', ", or ;", &term);
2272+
new->params = lappend(new->params, expr);
2273+
} while (term == ',');
2274+
}
2275+
}
22502276

22512277
return (PLpgSQL_stmt *) new;
22522278
}

src/pl/plpgsql/src/pl_exec.c

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.210 2008/04/17 21:37:28 alvherre Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.211 2008/05/03 00:11:36 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -197,6 +197,8 @@ static void assign_text_var(PLpgSQL_var *var, const char *str);
197197
static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
198198
List *params);
199199
static void free_params_data(PreparedParamsData *ppd);
200+
static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
201+
PLpgSQL_expr *query, List *params);
200202

201203

202204
/* ----------
@@ -1968,7 +1970,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
19681970
PLpgSQL_row *row = (PLpgSQL_row *) retvar;
19691971

19701972
Assert(row->rowtupdesc);
1971-
estate->retval =
1973+
estate->retval =
19721974
PointerGetDatum(make_tuple_from_row(estate, row,
19731975
row->rowtupdesc));
19741976
if (DatumGetPointer(estate->retval) == NULL) /* should not happen */
@@ -2189,7 +2191,18 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
21892191
if (estate->tuple_store == NULL)
21902192
exec_init_tuple_store(estate);
21912193

2192-
exec_run_select(estate, stmt->query, 0, &portal);
2194+
if (stmt->query != NULL)
2195+
{
2196+
/* static query */
2197+
exec_run_select(estate, stmt->query, 0, &portal);
2198+
}
2199+
else
2200+
{
2201+
/* RETURN QUERY EXECUTE */
2202+
Assert(stmt->dynquery != NULL);
2203+
portal = exec_dynquery_with_params(estate, stmt->dynquery,
2204+
stmt->params);
2205+
}
21932206

21942207
if (!compatible_tupdesc(estate->rettupdesc, portal->tupDesc))
21952208
ereport(ERROR,
@@ -2841,58 +2854,10 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
28412854
static int
28422855
exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
28432856
{
2844-
Datum query;
2845-
bool isnull;
2846-
Oid restype;
2847-
char *querystr;
28482857
Portal portal;
28492858
int rc;
28502859

2851-
/*
2852-
* Evaluate the string expression after the EXECUTE keyword. It's result
2853-
* is the querystring we have to execute.
2854-
*/
2855-
query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
2856-
if (isnull)
2857-
ereport(ERROR,
2858-
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2859-
errmsg("cannot EXECUTE a null querystring")));
2860-
2861-
/* Get the C-String representation */
2862-
querystr = convert_value_to_string(query, restype);
2863-
2864-
exec_eval_cleanup(estate);
2865-
2866-
/*
2867-
* Open an implicit cursor for the query. We use SPI_cursor_open_with_args
2868-
* even when there are no params, because this avoids making and freeing
2869-
* one copy of the plan.
2870-
*/
2871-
if (stmt->params)
2872-
{
2873-
PreparedParamsData *ppd;
2874-
2875-
ppd = exec_eval_using_params(estate, stmt->params);
2876-
portal = SPI_cursor_open_with_args(NULL,
2877-
querystr,
2878-
ppd->nargs, ppd->types,
2879-
ppd->values, ppd->nulls,
2880-
estate->readonly_func, 0);
2881-
free_params_data(ppd);
2882-
}
2883-
else
2884-
{
2885-
portal = SPI_cursor_open_with_args(NULL,
2886-
querystr,
2887-
0, NULL,
2888-
NULL, NULL,
2889-
estate->readonly_func, 0);
2890-
}
2891-
2892-
if (portal == NULL)
2893-
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
2894-
querystr, SPI_result_code_string(SPI_result));
2895-
pfree(querystr);
2860+
portal = exec_dynquery_with_params(estate, stmt->query, stmt->params);
28962861

28972862
/*
28982863
* Execute the loop
@@ -5208,3 +5173,65 @@ free_params_data(PreparedParamsData *ppd)
52085173

52095174
pfree(ppd);
52105175
}
5176+
5177+
/*
5178+
* Open portal for dynamic query
5179+
*/
5180+
static Portal
5181+
exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
5182+
List *params)
5183+
{
5184+
Portal portal;
5185+
Datum query;
5186+
bool isnull;
5187+
Oid restype;
5188+
char *querystr;
5189+
5190+
/*
5191+
* Evaluate the string expression after the EXECUTE keyword. Its result
5192+
* is the querystring we have to execute.
5193+
*/
5194+
query = exec_eval_expr(estate, dynquery, &isnull, &restype);
5195+
if (isnull)
5196+
ereport(ERROR,
5197+
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5198+
errmsg("cannot EXECUTE a null querystring")));
5199+
5200+
/* Get the C-String representation */
5201+
querystr = convert_value_to_string(query, restype);
5202+
5203+
exec_eval_cleanup(estate);
5204+
5205+
/*
5206+
* Open an implicit cursor for the query. We use SPI_cursor_open_with_args
5207+
* even when there are no params, because this avoids making and freeing
5208+
* one copy of the plan.
5209+
*/
5210+
if (params)
5211+
{
5212+
PreparedParamsData *ppd;
5213+
5214+
ppd = exec_eval_using_params(estate, params);
5215+
portal = SPI_cursor_open_with_args(NULL,
5216+
querystr,
5217+
ppd->nargs, ppd->types,
5218+
ppd->values, ppd->nulls,
5219+
estate->readonly_func, 0);
5220+
free_params_data(ppd);
5221+
}
5222+
else
5223+
{
5224+
portal = SPI_cursor_open_with_args(NULL,
5225+
querystr,
5226+
0, NULL,
5227+
NULL, NULL,
5228+
estate->readonly_func, 0);
5229+
}
5230+
5231+
if (portal == NULL)
5232+
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
5233+
querystr, SPI_result_code_string(SPI_result));
5234+
pfree(querystr);
5235+
5236+
return portal;
5237+
}

src/pl/plpgsql/src/pl_funcs.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.69 2008/04/06 23:43:29 tgl Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.70 2008/05/03 00:11:36 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -963,9 +963,37 @@ static void
963963
dump_return_query(PLpgSQL_stmt_return_query *stmt)
964964
{
965965
dump_ind();
966-
printf("RETURN QUERY ");
967-
dump_expr(stmt->query);
968-
printf("\n");
966+
if (stmt->query)
967+
{
968+
printf("RETURN QUERY ");
969+
dump_expr(stmt->query);
970+
printf("\n");
971+
}
972+
else
973+
{
974+
printf("RETURN QUERY EXECUTE ");
975+
dump_expr(stmt->dynquery);
976+
printf("\n");
977+
if (stmt->params != NIL)
978+
{
979+
ListCell *lc;
980+
int i;
981+
982+
dump_indent += 2;
983+
dump_ind();
984+
printf(" USING\n");
985+
dump_indent += 2;
986+
i = 1;
987+
foreach(lc, stmt->params)
988+
{
989+
dump_ind();
990+
printf(" parameter $%d: ", i++);
991+
dump_expr((PLpgSQL_expr *) lfirst(lc));
992+
printf("\n");
993+
}
994+
dump_indent -= 4;
995+
}
996+
}
969997
}
970998

971999
static void

src/pl/plpgsql/src/plpgsql.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.97 2008/04/06 23:43:29 tgl Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.98 2008/05/03 00:11:36 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -529,7 +529,9 @@ typedef struct
529529
{ /* RETURN QUERY statement */
530530
int cmd_type;
531531
int lineno;
532-
PLpgSQL_expr *query;
532+
PLpgSQL_expr *query; /* if static query */
533+
PLpgSQL_expr *dynquery; /* if dynamic query (RETURN QUERY EXECUTE) */
534+
List *params; /* USING arguments for dynamic query */
533535
} PLpgSQL_stmt_return_query;
534536

535537
typedef struct

src/test/regress/expected/plpgsql.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,3 +3267,21 @@ end;
32673267
$$ language plpgsql;
32683268
ERROR: cursor FOR loop must use a bound cursor variable
32693269
CONTEXT: compile of PL/pgSQL function "forc_bad" near line 4
3270+
-- return query execute
3271+
create or replace function return_dquery()
3272+
returns setof int as $$
3273+
begin
3274+
return query execute 'select * from (values(10),(20)) f';
3275+
return query execute 'select * from (values($1),($2)) f' using 40,50;
3276+
end;
3277+
$$ language plpgsql;
3278+
select * from return_dquery();
3279+
return_dquery
3280+
---------------
3281+
10
3282+
20
3283+
40
3284+
50
3285+
(4 rows)
3286+
3287+
drop function return_dquery();

src/test/regress/sql/plpgsql.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,3 +2669,17 @@ begin
26692669
end loop;
26702670
end;
26712671
$$ language plpgsql;
2672+
2673+
-- return query execute
2674+
2675+
create or replace function return_dquery()
2676+
returns setof int as $$
2677+
begin
2678+
return query execute 'select * from (values(10),(20)) f';
2679+
return query execute 'select * from (values($1),($2)) f' using 40,50;
2680+
end;
2681+
$$ language plpgsql;
2682+
2683+
select * from return_dquery();
2684+
2685+
drop function return_dquery();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy