mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Support EXECUTE USING in plpgsql.
Pavel Stehule, with some improvements by myself.
This commit is contained in:
parent
d5466e38f0
commit
e2a8804330
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.125 2008/03/28 00:21:55 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.126 2008/04/01 03:51:09 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter id="plpgsql">
|
<chapter id="plpgsql">
|
||||||
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
|
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
|
||||||
@ -1005,20 +1005,23 @@ END;
|
|||||||
<command>EXECUTE</command> statement is provided:
|
<command>EXECUTE</command> statement is provided:
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional>;
|
EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional>;
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
where <replaceable>command-string</replaceable> is an expression
|
where <replaceable>command-string</replaceable> is an expression
|
||||||
yielding a string (of type <type>text</type>) containing the
|
yielding a string (of type <type>text</type>) containing the
|
||||||
command to be executed and <replaceable>target</replaceable> is a
|
command to be executed. The optional <replaceable>target</replaceable>
|
||||||
record variable, row variable, or a comma-separated list of
|
is a record variable, a row variable, or a comma-separated list of
|
||||||
simple variables and record/row fields.
|
simple variables and record/row fields, into which the results of
|
||||||
|
the command will be stored. The optional <literal>USING</> expressions
|
||||||
|
supply values to be inserted into the command.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
No substitution of <application>PL/pgSQL</> variables is done on the
|
No substitution of <application>PL/pgSQL</> variables is done on the
|
||||||
computed command string. Any required variable values must be inserted
|
computed command string. Any required variable values must be inserted
|
||||||
in the command string as it is constructed.
|
in the command string as it is constructed; or you can use parameters
|
||||||
|
as described below.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1047,6 +1050,51 @@ EXECUTE <replaceable class="command">command-string</replaceable> <optional> INT
|
|||||||
unless the query produces exactly one row.
|
unless the query produces exactly one row.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The command string can use parameter values, which are referenced
|
||||||
|
in the command as <literal>$1</>, <literal>$2</>, etc.
|
||||||
|
These symbols refer to values supplied in the <literal>USING</>
|
||||||
|
clause. This method is often preferable to inserting data values
|
||||||
|
into the command string as text: it avoids run-time overhead of
|
||||||
|
converting the values to text and back, and it is much less prone
|
||||||
|
to SQL-injection attacks since there is no need for quoting or escaping.
|
||||||
|
An example is:
|
||||||
|
<programlisting>
|
||||||
|
EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
|
||||||
|
INTO c
|
||||||
|
USING checked_user, checked_date;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
Note that parameter symbols can only be used for data values
|
||||||
|
— if you want to use dynamically determined table or column
|
||||||
|
names, you must insert them into the command string textually.
|
||||||
|
For example, if the preceding query needed to be done against a
|
||||||
|
dynamically selected table, you could do this:
|
||||||
|
<programlisting>
|
||||||
|
EXECUTE 'SELECT count(*) FROM '
|
||||||
|
|| tabname::regclass
|
||||||
|
|| ' WHERE inserted_by = $1 AND inserted <= $2'
|
||||||
|
INTO c
|
||||||
|
USING checked_user, checked_date;
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
An <command>EXECUTE</> with a simple constant command string and some
|
||||||
|
<literal>USING</> parameters, as in the first example above, is
|
||||||
|
functionally equivalent to just writing the command directly in
|
||||||
|
<application>PL/pgSQL</application> and allowing replacement of
|
||||||
|
<application>PL/pgSQL</application> variables to happen automatically.
|
||||||
|
The important difference is that <command>EXECUTE</> will re-plan
|
||||||
|
the command on each execution, generating a plan that is specific
|
||||||
|
to the current parameter values; whereas
|
||||||
|
<application>PL/pgSQL</application> normally creates a generic plan
|
||||||
|
and caches it for re-use. In situations where the best plan depends
|
||||||
|
strongly on the parameter values, <command>EXECUTE</> can be
|
||||||
|
significantly faster; while when the plan is not sensitive to parameter
|
||||||
|
values, re-planning will be a waste.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>SELECT INTO</command> is not currently supported within
|
<command>SELECT INTO</command> is not currently supported within
|
||||||
<command>EXECUTE</command>; instead, execute a plain <command>SELECT</>
|
<command>EXECUTE</command>; instead, execute a plain <command>SELECT</>
|
||||||
@ -1997,7 +2045,7 @@ $$ LANGUAGE plpgsql;
|
|||||||
rows:
|
rows:
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<optional> <<<replaceable>label</replaceable>>> </optional>
|
<optional> <<<replaceable>label</replaceable>>> </optional>
|
||||||
FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
|
FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional> LOOP
|
||||||
<replaceable>statements</replaceable>
|
<replaceable>statements</replaceable>
|
||||||
END LOOP <optional> <replaceable>label</replaceable> </optional>;
|
END LOOP <optional> <replaceable>label</replaceable> </optional>;
|
||||||
</synopsis>
|
</synopsis>
|
||||||
@ -2006,6 +2054,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
|
|||||||
on each entry to the <literal>FOR</> loop. This allows the programmer to
|
on each entry to the <literal>FOR</> loop. This allows the programmer to
|
||||||
choose the speed of a preplanned query or the flexibility of a dynamic
|
choose the speed of a preplanned query or the flexibility of a dynamic
|
||||||
query, just as with a plain <command>EXECUTE</command> statement.
|
query, just as with a plain <command>EXECUTE</command> statement.
|
||||||
|
As with <command>EXECUTE</command>, parameter values can be inserted
|
||||||
|
into the dynamic command via <literal>USING</>.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.108 2008/01/01 19:46:00 momjian Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.109 2008/04/01 03:51:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -21,11 +21,15 @@
|
|||||||
|
|
||||||
static PLpgSQL_expr *read_sql_construct(int until,
|
static PLpgSQL_expr *read_sql_construct(int until,
|
||||||
int until2,
|
int until2,
|
||||||
|
int until3,
|
||||||
const char *expected,
|
const char *expected,
|
||||||
const char *sqlstart,
|
const char *sqlstart,
|
||||||
bool isexpression,
|
bool isexpression,
|
||||||
bool valid_sql,
|
bool valid_sql,
|
||||||
int *endtoken);
|
int *endtoken);
|
||||||
|
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
|
||||||
|
const char *expected,
|
||||||
|
int *endtoken);
|
||||||
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
|
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
|
||||||
static PLpgSQL_type *read_datatype(int tok);
|
static PLpgSQL_type *read_datatype(int tok);
|
||||||
static PLpgSQL_stmt *make_execsql_stmt(const char *sqlstart, int lineno);
|
static PLpgSQL_stmt *make_execsql_stmt(const char *sqlstart, int lineno);
|
||||||
@ -200,6 +204,7 @@ static void check_labels(const char *start_label,
|
|||||||
%token K_THEN
|
%token K_THEN
|
||||||
%token K_TO
|
%token K_TO
|
||||||
%token K_TYPE
|
%token K_TYPE
|
||||||
|
%token K_USING
|
||||||
%token K_WARNING
|
%token K_WARNING
|
||||||
%token K_WHEN
|
%token K_WHEN
|
||||||
%token K_WHILE
|
%token K_WHILE
|
||||||
@ -892,8 +897,11 @@ for_control :
|
|||||||
{
|
{
|
||||||
PLpgSQL_stmt_dynfors *new;
|
PLpgSQL_stmt_dynfors *new;
|
||||||
PLpgSQL_expr *expr;
|
PLpgSQL_expr *expr;
|
||||||
|
int term;
|
||||||
|
|
||||||
expr = plpgsql_read_expression(K_LOOP, "LOOP");
|
expr = read_sql_expression2(K_LOOP, K_USING,
|
||||||
|
"LOOP or USING",
|
||||||
|
&term);
|
||||||
|
|
||||||
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
|
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
|
||||||
new->cmd_type = PLPGSQL_STMT_DYNFORS;
|
new->cmd_type = PLPGSQL_STMT_DYNFORS;
|
||||||
@ -921,6 +929,17 @@ for_control :
|
|||||||
}
|
}
|
||||||
new->query = expr;
|
new->query = expr;
|
||||||
|
|
||||||
|
if (term == K_USING)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
expr = read_sql_expression2(',', K_LOOP,
|
||||||
|
", or LOOP",
|
||||||
|
&term);
|
||||||
|
new->params = lappend(new->params, expr);
|
||||||
|
} while (term == ',');
|
||||||
|
}
|
||||||
|
|
||||||
$$ = (PLpgSQL_stmt *) new;
|
$$ = (PLpgSQL_stmt *) new;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -954,6 +973,7 @@ for_control :
|
|||||||
*/
|
*/
|
||||||
expr1 = read_sql_construct(K_DOTDOT,
|
expr1 = read_sql_construct(K_DOTDOT,
|
||||||
K_LOOP,
|
K_LOOP,
|
||||||
|
0,
|
||||||
"LOOP",
|
"LOOP",
|
||||||
"SELECT ",
|
"SELECT ",
|
||||||
true,
|
true,
|
||||||
@ -973,17 +993,14 @@ for_control :
|
|||||||
check_sql_expr(expr1->query);
|
check_sql_expr(expr1->query);
|
||||||
|
|
||||||
/* Read and check the second one */
|
/* Read and check the second one */
|
||||||
expr2 = read_sql_construct(K_LOOP,
|
expr2 = read_sql_expression2(K_LOOP, K_BY,
|
||||||
K_BY,
|
"LOOP",
|
||||||
"LOOP",
|
&tok);
|
||||||
"SELECT ",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
&tok);
|
|
||||||
|
|
||||||
/* Get the BY clause if any */
|
/* Get the BY clause if any */
|
||||||
if (tok == K_BY)
|
if (tok == K_BY)
|
||||||
expr_by = plpgsql_read_expression(K_LOOP, "LOOP");
|
expr_by = plpgsql_read_expression(K_LOOP,
|
||||||
|
"LOOP");
|
||||||
else
|
else
|
||||||
expr_by = NULL;
|
expr_by = NULL;
|
||||||
|
|
||||||
@ -1217,18 +1234,15 @@ stmt_raise : K_RAISE lno raise_level raise_msg
|
|||||||
|
|
||||||
if (tok == ',')
|
if (tok == ',')
|
||||||
{
|
{
|
||||||
PLpgSQL_expr *expr;
|
do
|
||||||
int term;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
{
|
||||||
expr = read_sql_construct(',', ';', ", or ;",
|
PLpgSQL_expr *expr;
|
||||||
"SELECT ",
|
|
||||||
true, true, &term);
|
expr = read_sql_expression2(',', ';',
|
||||||
|
", or ;",
|
||||||
|
&tok);
|
||||||
new->params = lappend(new->params, expr);
|
new->params = lappend(new->params, expr);
|
||||||
if (term == ';')
|
} while (tok == ',');
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$$ = (PLpgSQL_stmt *)new;
|
$$ = (PLpgSQL_stmt *)new;
|
||||||
@ -1307,7 +1321,8 @@ stmt_dynexecute : K_EXECUTE lno
|
|||||||
PLpgSQL_expr *expr;
|
PLpgSQL_expr *expr;
|
||||||
int endtoken;
|
int endtoken;
|
||||||
|
|
||||||
expr = read_sql_construct(K_INTO, ';', "INTO|;",
|
expr = read_sql_construct(K_INTO, K_USING, ';',
|
||||||
|
"INTO or USING or ;",
|
||||||
"SELECT ",
|
"SELECT ",
|
||||||
true, true, &endtoken);
|
true, true, &endtoken);
|
||||||
|
|
||||||
@ -1319,16 +1334,30 @@ stmt_dynexecute : K_EXECUTE lno
|
|||||||
new->strict = false;
|
new->strict = false;
|
||||||
new->rec = NULL;
|
new->rec = NULL;
|
||||||
new->row = NULL;
|
new->row = NULL;
|
||||||
|
new->params = NIL;
|
||||||
|
|
||||||
/* If we found "INTO", collect the argument */
|
/* If we found "INTO", collect the argument */
|
||||||
if (endtoken == K_INTO)
|
if (endtoken == K_INTO)
|
||||||
{
|
{
|
||||||
new->into = true;
|
new->into = true;
|
||||||
read_into_target(&new->rec, &new->row, &new->strict);
|
read_into_target(&new->rec, &new->row, &new->strict);
|
||||||
if (yylex() != ';')
|
endtoken = yylex();
|
||||||
|
if (endtoken != ';' && endtoken != K_USING)
|
||||||
yyerror("syntax error");
|
yyerror("syntax error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we found "USING", collect the argument(s) */
|
||||||
|
if (endtoken == K_USING)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
expr = read_sql_expression2(',', ';',
|
||||||
|
", or ;",
|
||||||
|
&endtoken);
|
||||||
|
new->params = lappend(new->params, expr);
|
||||||
|
} while (endtoken == ',');
|
||||||
|
}
|
||||||
|
|
||||||
$$ = (PLpgSQL_stmt *)new;
|
$$ = (PLpgSQL_stmt *)new;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -1730,16 +1759,29 @@ assign_expr_param(int dno, int *params, int *nparams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Convenience routine to read an expression with one possible terminator */
|
||||||
PLpgSQL_expr *
|
PLpgSQL_expr *
|
||||||
plpgsql_read_expression(int until, const char *expected)
|
plpgsql_read_expression(int until, const char *expected)
|
||||||
{
|
{
|
||||||
return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);
|
return read_sql_construct(until, 0, 0, expected,
|
||||||
|
"SELECT ", true, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convenience routine to read an expression with two possible terminators */
|
||||||
|
static PLpgSQL_expr *
|
||||||
|
read_sql_expression2(int until, int until2, const char *expected,
|
||||||
|
int *endtoken)
|
||||||
|
{
|
||||||
|
return read_sql_construct(until, until2, 0, expected,
|
||||||
|
"SELECT ", true, true, endtoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convenience routine to read a SQL statement that must end with ';' */
|
||||||
static PLpgSQL_expr *
|
static PLpgSQL_expr *
|
||||||
read_sql_stmt(const char *sqlstart)
|
read_sql_stmt(const char *sqlstart)
|
||||||
{
|
{
|
||||||
return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
|
return read_sql_construct(';', 0, 0, ";",
|
||||||
|
sqlstart, false, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1747,16 +1789,18 @@ read_sql_stmt(const char *sqlstart)
|
|||||||
*
|
*
|
||||||
* until: token code for expected terminator
|
* until: token code for expected terminator
|
||||||
* until2: token code for alternate terminator (pass 0 if none)
|
* until2: token code for alternate terminator (pass 0 if none)
|
||||||
|
* until3: token code for another alternate terminator (pass 0 if none)
|
||||||
* expected: text to use in complaining that terminator was not found
|
* expected: text to use in complaining that terminator was not found
|
||||||
* sqlstart: text to prefix to the accumulated SQL text
|
* sqlstart: text to prefix to the accumulated SQL text
|
||||||
* isexpression: whether to say we're reading an "expression" or a "statement"
|
* isexpression: whether to say we're reading an "expression" or a "statement"
|
||||||
* valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
|
* valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
|
||||||
* endtoken: if not NULL, ending token is stored at *endtoken
|
* endtoken: if not NULL, ending token is stored at *endtoken
|
||||||
* (this is only interesting if until2 isn't zero)
|
* (this is only interesting if until2 or until3 isn't zero)
|
||||||
*/
|
*/
|
||||||
static PLpgSQL_expr *
|
static PLpgSQL_expr *
|
||||||
read_sql_construct(int until,
|
read_sql_construct(int until,
|
||||||
int until2,
|
int until2,
|
||||||
|
int until3,
|
||||||
const char *expected,
|
const char *expected,
|
||||||
const char *sqlstart,
|
const char *sqlstart,
|
||||||
bool isexpression,
|
bool isexpression,
|
||||||
@ -1783,6 +1827,8 @@ read_sql_construct(int until,
|
|||||||
break;
|
break;
|
||||||
if (tok == until2 && parenlevel == 0)
|
if (tok == until2 && parenlevel == 0)
|
||||||
break;
|
break;
|
||||||
|
if (tok == until3 && parenlevel == 0)
|
||||||
|
break;
|
||||||
if (tok == '(' || tok == '[')
|
if (tok == '(' || tok == '[')
|
||||||
parenlevel++;
|
parenlevel++;
|
||||||
else if (tok == ')' || tok == ']')
|
else if (tok == ')' || tok == ']')
|
||||||
@ -2066,15 +2112,17 @@ read_fetch_direction(void)
|
|||||||
else if (pg_strcasecmp(yytext, "absolute") == 0)
|
else if (pg_strcasecmp(yytext, "absolute") == 0)
|
||||||
{
|
{
|
||||||
fetch->direction = FETCH_ABSOLUTE;
|
fetch->direction = FETCH_ABSOLUTE;
|
||||||
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
|
fetch->expr = read_sql_expression2(K_FROM, K_IN,
|
||||||
"SELECT ", true, true, NULL);
|
"FROM or IN",
|
||||||
|
NULL);
|
||||||
check_FROM = false;
|
check_FROM = false;
|
||||||
}
|
}
|
||||||
else if (pg_strcasecmp(yytext, "relative") == 0)
|
else if (pg_strcasecmp(yytext, "relative") == 0)
|
||||||
{
|
{
|
||||||
fetch->direction = FETCH_RELATIVE;
|
fetch->direction = FETCH_RELATIVE;
|
||||||
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
|
fetch->expr = read_sql_expression2(K_FROM, K_IN,
|
||||||
"SELECT ", true, true, NULL);
|
"FROM or IN",
|
||||||
|
NULL);
|
||||||
check_FROM = false;
|
check_FROM = false;
|
||||||
}
|
}
|
||||||
else if (pg_strcasecmp(yytext, "forward") == 0)
|
else if (pg_strcasecmp(yytext, "forward") == 0)
|
||||||
@ -2088,8 +2136,9 @@ read_fetch_direction(void)
|
|||||||
else if (tok != T_SCALAR)
|
else if (tok != T_SCALAR)
|
||||||
{
|
{
|
||||||
plpgsql_push_back_token(tok);
|
plpgsql_push_back_token(tok);
|
||||||
fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
|
fetch->expr = read_sql_expression2(K_FROM, K_IN,
|
||||||
"SELECT ", true, true, NULL);
|
"FROM or IN",
|
||||||
|
NULL);
|
||||||
check_FROM = false;
|
check_FROM = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2233,7 +2282,7 @@ make_return_query_stmt(int lineno)
|
|||||||
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
|
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
|
||||||
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
|
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
|
||||||
new->lineno = lineno;
|
new->lineno = lineno;
|
||||||
new->query = read_sql_construct(';', 0, ")", "", false, true, NULL);
|
new->query = read_sql_stmt("");
|
||||||
|
|
||||||
return (PLpgSQL_stmt *) new;
|
return (PLpgSQL_stmt *) new;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.207 2008/03/28 00:21:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.208 2008/04/01 03:51:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -38,6 +38,15 @@
|
|||||||
|
|
||||||
static const char *const raise_skip_msg = "RAISE";
|
static const char *const raise_skip_msg = "RAISE";
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int nargs; /* number of arguments */
|
||||||
|
Oid *types; /* types of arguments */
|
||||||
|
Datum *values; /* evaluated argument values */
|
||||||
|
char *nulls; /* null markers (' '/'n' style) */
|
||||||
|
bool *freevals; /* which arguments are pfree-able */
|
||||||
|
} PreparedParamsData;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All plpgsql function executions within a single transaction share the same
|
* All plpgsql function executions within a single transaction share the same
|
||||||
* executor EState for evaluating "simple" expressions. Each function call
|
* executor EState for evaluating "simple" expressions. Each function call
|
||||||
@ -178,6 +187,9 @@ static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
|
|||||||
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
|
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
|
||||||
static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
|
static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
|
||||||
static void free_var(PLpgSQL_var *var);
|
static void free_var(PLpgSQL_var *var);
|
||||||
|
static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
|
||||||
|
List *params);
|
||||||
|
static void free_params_data(PreparedParamsData *ppd);
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -2676,9 +2688,21 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
|
|||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call SPI_execute() without preparing a saved plan.
|
* Execute the query without preparing a saved plan.
|
||||||
*/
|
*/
|
||||||
exec_res = SPI_execute(querystr, estate->readonly_func, 0);
|
if (stmt->params)
|
||||||
|
{
|
||||||
|
PreparedParamsData *ppd;
|
||||||
|
|
||||||
|
ppd = exec_eval_using_params(estate, stmt->params);
|
||||||
|
exec_res = SPI_execute_with_args(querystr,
|
||||||
|
ppd->nargs, ppd->types,
|
||||||
|
ppd->values, ppd->nulls,
|
||||||
|
estate->readonly_func, 0);
|
||||||
|
free_params_data(ppd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exec_res = SPI_execute(querystr, estate->readonly_func, 0);
|
||||||
|
|
||||||
switch (exec_res)
|
switch (exec_res)
|
||||||
{
|
{
|
||||||
@ -2826,7 +2850,6 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
|
|||||||
PLpgSQL_row *row = NULL;
|
PLpgSQL_row *row = NULL;
|
||||||
SPITupleTable *tuptab;
|
SPITupleTable *tuptab;
|
||||||
int n;
|
int n;
|
||||||
SPIPlanPtr plan;
|
|
||||||
Portal portal;
|
Portal portal;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
@ -2856,19 +2879,35 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
|
|||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare a plan and open an implicit cursor for the query
|
* 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.
|
||||||
*/
|
*/
|
||||||
plan = SPI_prepare(querystr, 0, NULL);
|
if (stmt->params)
|
||||||
if (plan == NULL)
|
{
|
||||||
elog(ERROR, "SPI_prepare failed for \"%s\": %s",
|
PreparedParamsData *ppd;
|
||||||
querystr, SPI_result_code_string(SPI_result));
|
|
||||||
portal = SPI_cursor_open(NULL, plan, NULL, NULL,
|
ppd = exec_eval_using_params(estate, stmt->params);
|
||||||
estate->readonly_func);
|
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)
|
if (portal == NULL)
|
||||||
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
||||||
querystr, SPI_result_code_string(SPI_result));
|
querystr, SPI_result_code_string(SPI_result));
|
||||||
pfree(querystr);
|
pfree(querystr);
|
||||||
SPI_freeplan(plan);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch the initial 10 tuples
|
* Fetch the initial 10 tuples
|
||||||
@ -5069,3 +5108,79 @@ free_var(PLpgSQL_var *var)
|
|||||||
var->freeval = false;
|
var->freeval = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* exec_eval_using_params --- evaluate params of USING clause
|
||||||
|
*/
|
||||||
|
static PreparedParamsData *
|
||||||
|
exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
|
||||||
|
{
|
||||||
|
PreparedParamsData *ppd;
|
||||||
|
int nargs;
|
||||||
|
int i;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
ppd = (PreparedParamsData *) palloc(sizeof(PreparedParamsData));
|
||||||
|
nargs = list_length(params);
|
||||||
|
|
||||||
|
ppd->nargs = nargs;
|
||||||
|
ppd->types = (Oid *) palloc(nargs * sizeof(Oid));
|
||||||
|
ppd->values = (Datum *) palloc(nargs * sizeof(Datum));
|
||||||
|
ppd->nulls = (char *) palloc(nargs * sizeof(char));
|
||||||
|
ppd->freevals = (bool *) palloc(nargs * sizeof(bool));
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
foreach(lc, params)
|
||||||
|
{
|
||||||
|
PLpgSQL_expr *param = (PLpgSQL_expr *) lfirst(lc);
|
||||||
|
bool isnull;
|
||||||
|
|
||||||
|
ppd->values[i] = exec_eval_expr(estate, param,
|
||||||
|
&isnull,
|
||||||
|
&ppd->types[i]);
|
||||||
|
ppd->nulls[i] = isnull ? 'n' : ' ';
|
||||||
|
ppd->freevals[i] = false;
|
||||||
|
|
||||||
|
/* pass-by-ref non null values must be copied into plpgsql context */
|
||||||
|
if (!isnull)
|
||||||
|
{
|
||||||
|
int16 typLen;
|
||||||
|
bool typByVal;
|
||||||
|
|
||||||
|
get_typlenbyval(ppd->types[i], &typLen, &typByVal);
|
||||||
|
if (!typByVal)
|
||||||
|
{
|
||||||
|
ppd->values[i] = datumCopy(ppd->values[i], typByVal, typLen);
|
||||||
|
ppd->freevals[i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exec_eval_cleanup(estate);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free_params_data --- pfree all pass-by-reference values used in USING clause
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
free_params_data(PreparedParamsData *ppd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ppd->nargs; i++)
|
||||||
|
{
|
||||||
|
if (ppd->freevals[i])
|
||||||
|
pfree(DatumGetPointer(ppd->values[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
pfree(ppd->types);
|
||||||
|
pfree(ppd->values);
|
||||||
|
pfree(ppd->nulls);
|
||||||
|
pfree(ppd->freevals);
|
||||||
|
|
||||||
|
pfree(ppd);
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.67 2008/01/01 19:46:00 momjian Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.68 2008/04/01 03:51:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1007,6 +1007,24 @@ dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
|
|||||||
stmt->strict ? " STRICT" : "",
|
stmt->strict ? " STRICT" : "",
|
||||||
stmt->row->rowno, stmt->row->refname);
|
stmt->row->rowno, stmt->row->refname);
|
||||||
}
|
}
|
||||||
|
if (stmt->params != NIL)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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 -= 2;
|
||||||
|
}
|
||||||
dump_indent -= 2;
|
dump_indent -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1014,12 +1032,30 @@ static void
|
|||||||
dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
|
dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
|
||||||
{
|
{
|
||||||
dump_ind();
|
dump_ind();
|
||||||
printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
|
printf("FORS %s EXECUTE ",
|
||||||
|
(stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
|
||||||
dump_expr(stmt->query);
|
dump_expr(stmt->query);
|
||||||
printf("\n");
|
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;
|
||||||
|
}
|
||||||
dump_stmts(stmt->body);
|
dump_stmts(stmt->body);
|
||||||
|
|
||||||
dump_ind();
|
dump_ind();
|
||||||
printf(" ENDFORS\n");
|
printf(" ENDFORS\n");
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.95 2008/01/01 19:46:00 momjian Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.96 2008/04/01 03:51:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -430,6 +430,7 @@ typedef struct
|
|||||||
PLpgSQL_row *row;
|
PLpgSQL_row *row;
|
||||||
PLpgSQL_expr *query;
|
PLpgSQL_expr *query;
|
||||||
List *body; /* List of statements */
|
List *body; /* List of statements */
|
||||||
|
List *params; /* USING expressions */
|
||||||
} PLpgSQL_stmt_dynfors;
|
} PLpgSQL_stmt_dynfors;
|
||||||
|
|
||||||
|
|
||||||
@ -534,6 +535,7 @@ typedef struct
|
|||||||
bool strict; /* INTO STRICT flag */
|
bool strict; /* INTO STRICT flag */
|
||||||
PLpgSQL_rec *rec; /* INTO target, if record */
|
PLpgSQL_rec *rec; /* INTO target, if record */
|
||||||
PLpgSQL_row *row; /* INTO target, if row */
|
PLpgSQL_row *row; /* INTO target, if row */
|
||||||
|
List *params; /* USING expressions */
|
||||||
} PLpgSQL_stmt_dynexecute;
|
} PLpgSQL_stmt_dynexecute;
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.60 2008/01/01 19:46:00 momjian Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.61 2008/04/01 03:51:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -161,6 +161,7 @@ strict { return K_STRICT; }
|
|||||||
then { return K_THEN; }
|
then { return K_THEN; }
|
||||||
to { return K_TO; }
|
to { return K_TO; }
|
||||||
type { return K_TYPE; }
|
type { return K_TYPE; }
|
||||||
|
using { return K_USING; }
|
||||||
warning { return K_WARNING; }
|
warning { return K_WARNING; }
|
||||||
when { return K_WHEN; }
|
when { return K_WHEN; }
|
||||||
while { return K_WHILE; }
|
while { return K_WHILE; }
|
||||||
|
@ -3128,3 +3128,26 @@ select * from ret_query2(8);
|
|||||||
c9f0f895fb98ab9159f51fd0297e236d | 8 | t
|
c9f0f895fb98ab9159f51fd0297e236d | 8 | t
|
||||||
(9 rows)
|
(9 rows)
|
||||||
|
|
||||||
|
-- test EXECUTE USING
|
||||||
|
create function exc_using(int, text) returns int as $$
|
||||||
|
declare i int;
|
||||||
|
begin
|
||||||
|
for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
|
||||||
|
raise notice '%', i;
|
||||||
|
end loop;
|
||||||
|
execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
|
||||||
|
return i;
|
||||||
|
end
|
||||||
|
$$ language plpgsql;
|
||||||
|
select exc_using(5, 'foobar');
|
||||||
|
NOTICE: 1
|
||||||
|
NOTICE: 2
|
||||||
|
NOTICE: 3
|
||||||
|
NOTICE: 4
|
||||||
|
NOTICE: 5
|
||||||
|
NOTICE: 6
|
||||||
|
exc_using
|
||||||
|
-----------
|
||||||
|
26
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -2581,3 +2581,17 @@ end;
|
|||||||
$$ language plpgsql;
|
$$ language plpgsql;
|
||||||
|
|
||||||
select * from ret_query2(8);
|
select * from ret_query2(8);
|
||||||
|
|
||||||
|
-- test EXECUTE USING
|
||||||
|
create function exc_using(int, text) returns int as $$
|
||||||
|
declare i int;
|
||||||
|
begin
|
||||||
|
for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
|
||||||
|
raise notice '%', i;
|
||||||
|
end loop;
|
||||||
|
execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
|
||||||
|
return i;
|
||||||
|
end
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
select exc_using(5, 'foobar');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user