Skip to content

Commit e2a8804

Browse files
committed
Support EXECUTE USING in plpgsql.
Pavel Stehule, with some improvements by myself.
1 parent d5466e3 commit e2a8804

File tree

8 files changed

+350
-60
lines changed

8 files changed

+350
-60
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.125 2008/03/28 00:21:55 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.126 2008/04/01 03:51:09 tgl Exp $ -->
22

33
<chapter id="plpgsql">
44
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1005,20 +1005,23 @@ END;
10051005
<command>EXECUTE</command> statement is provided:
10061006

10071007
<synopsis>
1008-
EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional>;
1008+
EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional>;
10091009
</synopsis>
10101010

10111011
where <replaceable>command-string</replaceable> is an expression
10121012
yielding a string (of type <type>text</type>) containing the
1013-
command to be executed and <replaceable>target</replaceable> is a
1014-
record variable, row variable, or a comma-separated list of
1015-
simple variables and record/row fields.
1013+
command to be executed. The optional <replaceable>target</replaceable>
1014+
is a record variable, a row variable, or a comma-separated list of
1015+
simple variables and record/row fields, into which the results of
1016+
the command will be stored. The optional <literal>USING</> expressions
1017+
supply values to be inserted into the command.
10161018
</para>
10171019

10181020
<para>
10191021
No substitution of <application>PL/pgSQL</> variables is done on the
10201022
computed command string. Any required variable values must be inserted
1021-
in the command string as it is constructed.
1023+
in the command string as it is constructed; or you can use parameters
1024+
as described below.
10221025
</para>
10231026

10241027
<para>
@@ -1046,6 +1049,51 @@ EXECUTE <replaceable class="command">command-string</replaceable> <optional> INT
10461049
If the <literal>STRICT</> option is given, an error is reported
10471050
unless the query produces exactly one row.
10481051
</para>
1052+
1053+
<para>
1054+
The command string can use parameter values, which are referenced
1055+
in the command as <literal>$1</>, <literal>$2</>, etc.
1056+
These symbols refer to values supplied in the <literal>USING</>
1057+
clause. This method is often preferable to inserting data values
1058+
into the command string as text: it avoids run-time overhead of
1059+
converting the values to text and back, and it is much less prone
1060+
to SQL-injection attacks since there is no need for quoting or escaping.
1061+
An example is:
1062+
<programlisting>
1063+
EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
1064+
INTO c
1065+
USING checked_user, checked_date;
1066+
</programlisting>
1067+
1068+
Note that parameter symbols can only be used for data values
1069+
&mdash; if you want to use dynamically determined table or column
1070+
names, you must insert them into the command string textually.
1071+
For example, if the preceding query needed to be done against a
1072+
dynamically selected table, you could do this:
1073+
<programlisting>
1074+
EXECUTE 'SELECT count(*) FROM '
1075+
|| tabname::regclass
1076+
|| ' WHERE inserted_by = $1 AND inserted <= $2'
1077+
INTO c
1078+
USING checked_user, checked_date;
1079+
</programlisting>
1080+
</para>
1081+
1082+
<para>
1083+
An <command>EXECUTE</> with a simple constant command string and some
1084+
<literal>USING</> parameters, as in the first example above, is
1085+
functionally equivalent to just writing the command directly in
1086+
<application>PL/pgSQL</application> and allowing replacement of
1087+
<application>PL/pgSQL</application> variables to happen automatically.
1088+
The important difference is that <command>EXECUTE</> will re-plan
1089+
the command on each execution, generating a plan that is specific
1090+
to the current parameter values; whereas
1091+
<application>PL/pgSQL</application> normally creates a generic plan
1092+
and caches it for re-use. In situations where the best plan depends
1093+
strongly on the parameter values, <command>EXECUTE</> can be
1094+
significantly faster; while when the plan is not sensitive to parameter
1095+
values, re-planning will be a waste.
1096+
</para>
10491097

10501098
<para>
10511099
<command>SELECT INTO</command> is not currently supported within
@@ -1997,7 +2045,7 @@ $$ LANGUAGE plpgsql;
19972045
rows:
19982046
<synopsis>
19992047
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
2000-
FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
2048+
FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional> LOOP
20012049
<replaceable>statements</replaceable>
20022050
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20032051
</synopsis>
@@ -2006,6 +2054,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
20062054
on each entry to the <literal>FOR</> loop. This allows the programmer to
20072055
choose the speed of a preplanned query or the flexibility of a dynamic
20082056
query, just as with a plain <command>EXECUTE</command> statement.
2057+
As with <command>EXECUTE</command>, parameter values can be inserted
2058+
into the dynamic command via <literal>USING</>.
20092059
</para>
20102060
</sect2>
20112061

src/pl/plpgsql/src/gram.y

Lines changed: 82 additions & 33 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.108 2008/01/01 19:46:00 momjian Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.109 2008/04/01 03:51:09 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -21,11 +21,15 @@
2121

2222
static PLpgSQL_expr *read_sql_construct(int until,
2323
int until2,
24+
int until3,
2425
const char *expected,
2526
const char *sqlstart,
2627
bool isexpression,
2728
bool valid_sql,
2829
int *endtoken);
30+
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
31+
const char *expected,
32+
int *endtoken);
2933
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
3034
static PLpgSQL_type *read_datatype(int tok);
3135
static PLpgSQL_stmt *make_execsql_stmt(const char *sqlstart, int lineno);
@@ -200,6 +204,7 @@ static void check_labels(const char *start_label,
200204
%token K_THEN
201205
%token K_TO
202206
%token K_TYPE
207+
%token K_USING
203208
%token K_WARNING
204209
%token K_WHEN
205210
%token K_WHILE
@@ -892,8 +897,11 @@ for_control :
892897
{
893898
PLpgSQL_stmt_dynfors *new;
894899
PLpgSQL_expr *expr;
900+
int term;
895901

896-
expr = plpgsql_read_expression(K_LOOP, "LOOP");
902+
expr = read_sql_expression2(K_LOOP, K_USING,
903+
"LOOP or USING",
904+
&term);
897905

898906
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
899907
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -921,6 +929,17 @@ for_control :
921929
}
922930
new->query = expr;
923931

932+
if (term == K_USING)
933+
{
934+
do
935+
{
936+
expr = read_sql_expression2(',', K_LOOP,
937+
", or LOOP",
938+
&term);
939+
new->params = lappend(new->params, expr);
940+
} while (term == ',');
941+
}
942+
924943
$$ = (PLpgSQL_stmt *) new;
925944
}
926945
else
@@ -954,6 +973,7 @@ for_control :
954973
*/
955974
expr1 = read_sql_construct(K_DOTDOT,
956975
K_LOOP,
976+
0,
957977
"LOOP",
958978
"SELECT ",
959979
true,
@@ -973,17 +993,14 @@ for_control :
973993
check_sql_expr(expr1->query);
974994

975995
/* Read and check the second one */
976-
expr2 = read_sql_construct(K_LOOP,
977-
K_BY,
978-
"LOOP",
979-
"SELECT ",
980-
true,
981-
true,
982-
&tok);
996+
expr2 = read_sql_expression2(K_LOOP, K_BY,
997+
"LOOP",
998+
&tok);
983999

9841000
/* Get the BY clause if any */
9851001
if (tok == K_BY)
986-
expr_by = plpgsql_read_expression(K_LOOP, "LOOP");
1002+
expr_by = plpgsql_read_expression(K_LOOP,
1003+
"LOOP");
9871004
else
9881005
expr_by = NULL;
9891006

@@ -1217,18 +1234,15 @@ stmt_raise : K_RAISE lno raise_level raise_msg
12171234

12181235
if (tok == ',')
12191236
{
1220-
PLpgSQL_expr *expr;
1221-
int term;
1222-
1223-
for (;;)
1237+
do
12241238
{
1225-
expr = read_sql_construct(',', ';', ", or ;",
1226-
"SELECT ",
1227-
true, true, &term);
1239+
PLpgSQL_expr *expr;
1240+
1241+
expr = read_sql_expression2(',', ';',
1242+
", or ;",
1243+
&tok);
12281244
new->params = lappend(new->params, expr);
1229-
if (term == ';')
1230-
break;
1231-
}
1245+
} while (tok == ',');
12321246
}
12331247

12341248
$$ = (PLpgSQL_stmt *)new;
@@ -1307,7 +1321,8 @@ stmt_dynexecute : K_EXECUTE lno
13071321
PLpgSQL_expr *expr;
13081322
int endtoken;
13091323

1310-
expr = read_sql_construct(K_INTO, ';', "INTO|;",
1324+
expr = read_sql_construct(K_INTO, K_USING, ';',
1325+
"INTO or USING or ;",
13111326
"SELECT ",
13121327
true, true, &endtoken);
13131328

@@ -1319,16 +1334,30 @@ stmt_dynexecute : K_EXECUTE lno
13191334
new->strict = false;
13201335
new->rec = NULL;
13211336
new->row = NULL;
1337+
new->params = NIL;
13221338

13231339
/* If we found "INTO", collect the argument */
13241340
if (endtoken == K_INTO)
13251341
{
13261342
new->into = true;
13271343
read_into_target(&new->rec, &new->row, &new->strict);
1328-
if (yylex() != ';')
1344+
endtoken = yylex();
1345+
if (endtoken != ';' && endtoken != K_USING)
13291346
yyerror("syntax error");
13301347
}
13311348

1349+
/* If we found "USING", collect the argument(s) */
1350+
if (endtoken == K_USING)
1351+
{
1352+
do
1353+
{
1354+
expr = read_sql_expression2(',', ';',
1355+
", or ;",
1356+
&endtoken);
1357+
new->params = lappend(new->params, expr);
1358+
} while (endtoken == ',');
1359+
}
1360+
13321361
$$ = (PLpgSQL_stmt *)new;
13331362
}
13341363
;
@@ -1485,7 +1514,7 @@ stmt_fetch : K_FETCH lno opt_fetch_direction cursor_variable K_INTO
14851514
$$ = (PLpgSQL_stmt *)fetch;
14861515
}
14871516
;
1488-
1517+
14891518
stmt_move : K_MOVE lno opt_fetch_direction cursor_variable ';'
14901519
{
14911520
PLpgSQL_stmt_fetch *fetch = $3;
@@ -1730,33 +1759,48 @@ assign_expr_param(int dno, int *params, int *nparams)
17301759
}
17311760

17321761

1762+
/* Convenience routine to read an expression with one possible terminator */
17331763
PLpgSQL_expr *
17341764
plpgsql_read_expression(int until, const char *expected)
17351765
{
1736-
return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);
1766+
return read_sql_construct(until, 0, 0, expected,
1767+
"SELECT ", true, true, NULL);
17371768
}
17381769

1770+
/* Convenience routine to read an expression with two possible terminators */
1771+
static PLpgSQL_expr *
1772+
read_sql_expression2(int until, int until2, const char *expected,
1773+
int *endtoken)
1774+
{
1775+
return read_sql_construct(until, until2, 0, expected,
1776+
"SELECT ", true, true, endtoken);
1777+
}
1778+
1779+
/* Convenience routine to read a SQL statement that must end with ';' */
17391780
static PLpgSQL_expr *
17401781
read_sql_stmt(const char *sqlstart)
17411782
{
1742-
return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
1783+
return read_sql_construct(';', 0, 0, ";",
1784+
sqlstart, false, true, NULL);
17431785
}
17441786

17451787
/*
17461788
* Read a SQL construct and build a PLpgSQL_expr for it.
17471789
*
17481790
* until: token code for expected terminator
17491791
* until2: token code for alternate terminator (pass 0 if none)
1792+
* until3: token code for another alternate terminator (pass 0 if none)
17501793
* expected: text to use in complaining that terminator was not found
17511794
* sqlstart: text to prefix to the accumulated SQL text
17521795
* isexpression: whether to say we're reading an "expression" or a "statement"
17531796
* valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
17541797
* endtoken: if not NULL, ending token is stored at *endtoken
1755-
* (this is only interesting if until2 isn't zero)
1798+
* (this is only interesting if until2 or until3 isn't zero)
17561799
*/
17571800
static PLpgSQL_expr *
17581801
read_sql_construct(int until,
17591802
int until2,
1803+
int until3,
17601804
const char *expected,
17611805
const char *sqlstart,
17621806
bool isexpression,
@@ -1783,6 +1827,8 @@ read_sql_construct(int until,
17831827
break;
17841828
if (tok == until2 && parenlevel == 0)
17851829
break;
1830+
if (tok == until3 && parenlevel == 0)
1831+
break;
17861832
if (tok == '(' || tok == '[')
17871833
parenlevel++;
17881834
else if (tok == ')' || tok == ']')
@@ -2066,15 +2112,17 @@ read_fetch_direction(void)
20662112
else if (pg_strcasecmp(yytext, "absolute") == 0)
20672113
{
20682114
fetch->direction = FETCH_ABSOLUTE;
2069-
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
2070-
"SELECT ", true, true, NULL);
2115+
fetch->expr = read_sql_expression2(K_FROM, K_IN,
2116+
"FROM or IN",
2117+
NULL);
20712118
check_FROM = false;
20722119
}
20732120
else if (pg_strcasecmp(yytext, "relative") == 0)
20742121
{
20752122
fetch->direction = FETCH_RELATIVE;
2076-
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
2077-
"SELECT ", true, true, NULL);
2123+
fetch->expr = read_sql_expression2(K_FROM, K_IN,
2124+
"FROM or IN",
2125+
NULL);
20782126
check_FROM = false;
20792127
}
20802128
else if (pg_strcasecmp(yytext, "forward") == 0)
@@ -2088,8 +2136,9 @@ read_fetch_direction(void)
20882136
else if (tok != T_SCALAR)
20892137
{
20902138
plpgsql_push_back_token(tok);
2091-
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
2092-
"SELECT ", true, true, NULL);
2139+
fetch->expr = read_sql_expression2(K_FROM, K_IN,
2140+
"FROM or IN",
2141+
NULL);
20932142
check_FROM = false;
20942143
}
20952144
else
@@ -2233,7 +2282,7 @@ make_return_query_stmt(int lineno)
22332282
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
22342283
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
22352284
new->lineno = lineno;
2236-
new->query = read_sql_construct(';', 0, ")", "", false, true, NULL);
2285+
new->query = read_sql_stmt("");
22372286

22382287
return (PLpgSQL_stmt *) new;
22392288
}

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