diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-08-14 21:14:42 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-08-14 21:14:42 +0000 |
commit | 3d1e01caa46b87b734e4758ad9885e1e12bc77c8 (patch) | |
tree | 5bb57f3ebf3e752145f32b0a9ce8fca093ce321d /doc/src | |
parent | 29fa051316115fd0b7e8f965c77d5e04a155dcd6 (diff) |
Support INSERT/UPDATE/DELETE RETURNING in plpgsql, with rowcount checking
as per yesterday's proposal. Also make things a tad more orthogonal by
adding the recent STRICT addition to EXECUTE INTO.
Jonah Harris and Tom Lane
Diffstat (limited to 'doc/src')
-rw-r--r-- | doc/src/sgml/plpgsql.sgml | 294 |
1 files changed, 185 insertions, 109 deletions
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index fb2fe735a6c..245cf8b79fb 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.98 2006/08/12 20:05:54 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.99 2006/08/14 21:14:41 tgl Exp $ --> <chapter id="plpgsql"> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> @@ -228,17 +228,6 @@ $$ LANGUAGE plpgsql; <type>void</> if it has no useful return value. </para> - <note> - <para> - <application>PL/pgSQL</> does not currently have full support for - domain types: it treats a domain the same as the underlying scalar - type. This means that constraints associated with the domain will - not be enforced. This is not an issue for function arguments, but - it is a hazard if you declare a <application>PL/pgSQL</> function - as returning a domain type. - </para> - </note> - <para> <application>PL/pgSQL</> functions can also be declared with output parameters in place of an explicit specification of the return type. @@ -1024,21 +1013,17 @@ $$ LANGUAGE plpgsql; types that are explicitly understood by <application>PL/pgSQL</application>. Anything not recognized as one of these statement types is presumed - to be an SQL command and is sent to the main database engine to execute - (after substitution of any <application>PL/pgSQL</application> variables - used in the statement). Thus, - for example, the SQL commands <command>INSERT</>, <command>UPDATE</>, and - <command>DELETE</> may be considered to be statements of - <application>PL/pgSQL</application>, but they are not specifically - listed here. + to be an SQL command and is sent to the main database engine to execute, + as described in <xref linkend="plpgsql-statements-sql-noresult"> + and <xref linkend="plpgsql-statements-sql-onerow">. </para> <sect2 id="plpgsql-statements-assignment"> <title>Assignment</title> <para> - An assignment of a value to a variable or row/record field is - written as: + An assignment of a value to a <application>PL/pgSQL</application> + variable or row/record field is written as: <synopsis> <replaceable>identifier</replaceable> := <replaceable>expression</replaceable>; </synopsis> @@ -1067,65 +1052,177 @@ tax := subtotal * 0.06; </para> </sect2> - <sect2 id="plpgsql-select-into"> - <title><command>SELECT INTO</command></title> + <sect2 id="plpgsql-statements-sql-noresult"> + <title>Executing a Query With No Result</title> + + <para> + For any SQL query that does not return rows, for example + <command>INSERT</> without a <literal>RETURNING</> clause, you can + execute the query within a <application>PL/pgSQL</application> function + just by writing the query. + </para> + + <para> + Any <application>PL/pgSQL</application> variable name appearing + in the query text is replaced by a parameter symbol, and then the + current value of the variable is provided as the parameter value + at runtime. This allows the same textual query to do different + things in different calls of the function. + </para> + + <note> + <para> + This two-step process allows + <application>PL/pgSQL</application> to plan the query just once + and re-use the plan on subsequent executions. As an example, + if you write +<programlisting> +DECLARE + key TEXT; + delta INTEGER; +BEGIN + ... + UPDATE mytab SET val = val + delta WHERE id = key; +</programlisting> + the query text seen by the main SQL engine will look like +<programlisting> + UPDATE mytab SET val = val + $1 WHERE id = $2; +</programlisting> + Although you don't normally have to think about this, it's helpful + to know it when you need to make sense of syntax-error messages. + </para> + </note> + + <caution> + <para> + <application>PL/pgSQL</application> will substitute for any identifier + matching one of the function's declared variables; it is not bright + enough to know whether that's what you meant! Thus, it is a bad idea + to use a variable name that is the same as any table or column name + that you need to reference in queries within the function. Sometimes + you can work around this by using qualified names in the query: + <application>PL/pgSQL</application> will not substitute in a + qualified name <replaceable>foo</>.<replaceable>bar</>, even if + <replaceable>foo</> or <replaceable>bar</> is a declared variable + name. + </para> + </caution> + + <para> + Sometimes it is useful to evaluate an expression or <command>SELECT</> + query but discard the result, for example when calling a function + that has side-effects but no useful result value. To do + this in <application>PL/pgSQL</application>, use the + <command>PERFORM</command> statement: + +<synopsis> +PERFORM <replaceable>query</replaceable>; +</synopsis> + + This executes <replaceable>query</replaceable> and discards the + result. Write the <replaceable>query</replaceable> the same + way you would write an SQL <command>SELECT</> command, but replace the + initial keyword <command>SELECT</> with <command>PERFORM</command>. + <application>PL/pgSQL</application> variables will be + substituted into the query as usual. Also, the special variable + <literal>FOUND</literal> is set to true if the query produced at + least one row, or false if it produced no rows. + </para> + + <note> + <para> + One might expect that writing <command>SELECT</command> directly + would accomplish this result, but at + present the only accepted way to do it is + <command>PERFORM</command>. A SQL command that can return rows, + such as <command>SELECT</command>, will be rejected as an error + unless it has an <literal>INTO</> clause as discussed in the + next section. + </para> + </note> + + <para> + An example: +<programlisting> +PERFORM create_mv('cs_session_page_requests_mv', my_query); +</programlisting> + </para> + </sect2> + + <sect2 id="plpgsql-statements-sql-onerow"> + <title>Executing a Query with a Single-Row Result</title> - <indexterm zone="plpgsql-select-into"> + <indexterm zone="plpgsql-statements-sql-onerow"> <primary>SELECT INTO</primary> <secondary>in PL/pgSQL</secondary> </indexterm> + <indexterm zone="plpgsql-statements-sql-onerow"> + <primary>RETURNING INTO</primary> + <secondary>in PL/pgSQL</secondary> + </indexterm> + <para> - The result of a <command>SELECT</command> command yielding multiple - columns (but only one row) can be assigned to a record variable, row-type - variable, or list of scalar variables. This is done by: + The result of a SQL command yielding a single row (possibly of multiple + columns) can be assigned to a record variable, row-type variable, or list + of scalar variables. This is done by writing the base SQL command and + adding an <literal>INTO</> clause. For example, <synopsis> -SELECT INTO <optional>STRICT</optional> <replaceable>target</replaceable> <replaceable>select_expressions</replaceable> FROM ...; +SELECT <replaceable>select_expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>target</replaceable> FROM ...; +INSERT ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>target</replaceable>; +UPDATE ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>target</replaceable>; +DELETE ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>target</replaceable>; </synopsis> where <replaceable>target</replaceable> can be a record variable, a row variable, or a comma-separated list of simple variables and - record/row fields. The <replaceable>select_expressions</replaceable> - and the remainder of the command are the same as in regular SQL. + record/row fields. + <application>PL/pgSQL</application> variables will be + substituted into the rest of the query as usual. + This works for <command>SELECT</>, + <command>INSERT</>/<command>UPDATE</>/<command>DELETE</> with + <literal>RETURNING</>, and utility commands that return rowset + results (such as <command>EXPLAIN</>). + Except for the <literal>INTO</> clause, the SQL command is the same + as it would be written outside <application>PL/pgSQL</application>. </para> + <tip> <para> - Note that this is quite different from - <productname>PostgreSQL</>'s normal interpretation of - <command>SELECT INTO</command>, where the <literal>INTO</> target - is a newly created table. If you want to create a table from a + Note that this interpretation of <command>SELECT</> with <literal>INTO</> + is quite different from <productname>PostgreSQL</>'s regular + <command>SELECT INTO</command> command, wherein the <literal>INTO</> + target is a newly created table. If you want to create a table from a <command>SELECT</> result inside a <application>PL/pgSQL</application> function, use the syntax <command>CREATE TABLE ... AS SELECT</command>. </para> + </tip> <para> - If a row or a variable list is used as target, the selected values - must exactly match the structure of the target, or a run-time error + If a row or a variable list is used as target, the query's result columns + must exactly match the structure of the target as to number and data + types, or a run-time error occurs. When a record variable is the target, it automatically configures itself to the row type of the query result columns. </para> <para> - Except for the <literal>INTO</> clause, the <command>SELECT</> - statement is the same as a normal SQL <command>SELECT</> command - and can use its full power. - </para> - - <para> - The <literal>INTO</> clause can appear almost anywhere in the - <command>SELECT</command> statement. Customarily it is written - either just after <literal>SELECT</> as shown above, or - just before <literal>FROM</> — that is, either just before - or just after the list of <replaceable>select_expressions</replaceable>. + The <literal>INTO</> clause can appear almost anywhere in the SQL + command. Customarily it is written either just before or just after + the list of <replaceable>select_expressions</replaceable> in a + <command>SELECT</> command, or at the end of the command for other + command types. It is recommended that you follow this convention + in case the <application>PL/pgSQL</application> parser becomes + stricter in future versions. </para> <para> - If <literal>STRICT</literal> is not specified then + If <literal>STRICT</literal> is not specified, then <replaceable>target</replaceable> will be set to the first row - returned by the query, or if the query returned no rows, - null values are assigned. (Note that <quote>the first row</> is not + returned by the query, or to nulls if the query returned no rows. + (Note that <quote>the first row</> is not well-defined unless you've used <literal>ORDER BY</>.) Any result rows after the first row are discarded. You can check the special <literal>FOUND</literal> variable (see @@ -1133,21 +1230,21 @@ SELECT INTO <optional>STRICT</optional> <replaceable>target</replaceable> <repla determine whether a row was returned: <programlisting> -SELECT INTO myrec * FROM emp WHERE empname = myname; +SELECT * INTO myrec FROM emp WHERE empname = myname; IF NOT FOUND THEN RAISE EXCEPTION 'employee % not found', myname; END IF; </programlisting> If the <literal>STRICT</literal> option is specified, the query must - return exactly one row or a run-time error will be thrown, either + return exactly one row or a run-time error will be reported, either <literal>NO_DATA_FOUND</> (no rows) or <literal>TOO_MANY_ROWS</> (more than one row). You can use an exception block if you wish to catch the error, for example: <programlisting> BEGIN; - SELECT INTO STRICT myrec * FROM emp WHERE empname = myname; + SELECT * INTO STRICT myrec FROM emp WHERE empname = myname; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE EXCEPTION 'employee % not found', myname; @@ -1155,58 +1252,31 @@ BEGIN; RAISE EXCEPTION 'employee % not unique', myname; END; </programlisting> - Successful execution of <command>SELECT INTO STRICT</command> + Successful execution of a command with <literal>STRICT</> always sets <literal>FOUND</literal> to true. </para> - <note> - <para> - <command>SELECT INTO STRICT</command> matches the behavior of - Oracle PL/SQL's <command>SELECT INTO</command> statement. - </para> - </note> - - </sect2> - - <sect2 id="plpgsql-statements-perform"> - <title>Executing an Expression or Query With No Result</title> - <para> - Sometimes one wishes to evaluate an expression or query but - discard the result (typically because one is calling a function - that has useful side-effects but no useful result value). To do - this in <application>PL/pgSQL</application>, use the - <command>PERFORM</command> statement: - -<synopsis> -PERFORM <replaceable>query</replaceable>; -</synopsis> - - This executes <replaceable>query</replaceable> and discards the - result. Write the <replaceable>query</replaceable> the same - way as you would in an SQL <command>SELECT</> command, but replace the - initial keyword <command>SELECT</> with <command>PERFORM</command>. - <application>PL/pgSQL</application> variables will be - substituted into the query as usual. Also, the special variable - <literal>FOUND</literal> is set to true if the query produced at - least one row or false if it produced no rows. + For <command>INSERT</>/<command>UPDATE</>/<command>DELETE</> with + <literal>RETURNING</>, <application>PL/pgSQL</application> reports + an error for more than one returned row, even when + <literal>STRICT</literal> is not specified. This is because there + is no option such as <literal>ORDER BY</> with which to determine + which affected row would be returned. </para> <note> <para> - One might expect that <command>SELECT</command> with no - <literal>INTO</> clause would accomplish this result, but at - present the only accepted way to do it is - <command>PERFORM</command>. + The <literal>STRICT</> option matches the behavior of + Oracle PL/SQL's <command>SELECT INTO</command> and related statements. </para> </note> <para> - An example: -<programlisting> -PERFORM create_mv('cs_session_page_requests_mv', my_query); -</programlisting> + To handle cases where you need to process multiple result rows + from a SQL query, see <xref linkend="plpgsql-records-iterating">. </para> + </sect2> <sect2 id="plpgsql-statements-null"> @@ -1268,7 +1338,7 @@ NULL; <command>EXECUTE</command> statement is provided: <synopsis> -EXECUTE <replaceable class="command">command-string</replaceable> [ INTO <replaceable>target</replaceable> ]; +EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional>; </synopsis> where <replaceable>command-string</replaceable> is an expression @@ -1280,8 +1350,8 @@ EXECUTE <replaceable class="command">command-string</replaceable> [ INTO <replac <para> Note in particular that no substitution of <application>PL/pgSQL</> - variables is done on the command string. The values of variables must - be inserted in the command string as it is constructed. + variables is done on the computed command string. The values of + variables must be inserted in the command string as it is constructed. </para> <para> @@ -1295,16 +1365,20 @@ EXECUTE <replaceable class="command">command-string</replaceable> [ INTO <replac <para> The <literal>INTO</literal> clause specifies where the results of - a <command>SELECT</command> command should be assigned. If a row + a SQL command returning rows should be assigned. If a row or variable list is provided, it must exactly match the structure - of the results produced by the <command>SELECT</command> (when a + of the query's results (when a record variable is used, it will configure itself to match the - result's structure automatically). If multiple rows are returned, + result structure automatically). If multiple rows are returned, only the first will be assigned to the <literal>INTO</literal> variable. If no rows are returned, NULL is assigned to the <literal>INTO</literal> variable. If no <literal>INTO</literal> - clause is specified, the results of a <command>SELECT</command> - command are discarded. + clause is specified, the query results are discarded. + </para> + + <para> + If the <literal>STRICT</> option is given, an error is reported + unless the query produces exactly one row. </para> <para> @@ -2070,8 +2144,8 @@ $$ LANGUAGE plpgsql; <para> The <replaceable>query</replaceable> used in this type of <literal>FOR</> - statement can be any query that returns rows to the caller: - <command>SELECT</> (without <literal>INTO</>) is the most common case, + statement can be any SQL command that returns rows to the caller: + <command>SELECT</> is the most common case, but you can also use <command>INSERT</>, <command>UPDATE</>, or <command>DELETE</> with a <literal>RETURNING</> clause. Some utility commands such as <command>EXPLAIN</> will work too. @@ -3158,17 +3232,19 @@ SELECT * FROM sales_summary_bytime; <listitem> <para> - You cannot use parameter names that are the same as columns - that are referenced in the function. Oracle allows you to do this - if you qualify the parameter name using - <literal>function_name.paramater_name</>. + You can overload function names in <productname>PostgreSQL</>. This is + often used to work around the lack of default parameters. </para> </listitem> <listitem> <para> - You can overload function names in <productname>PostgreSQL</>. This is - often used to work around the lack of default parameters. + You cannot use parameter names that are the same as columns + that are referenced in the function. Oracle allows you to do this + if you qualify the parameter name using + <literal>function_name.parameter_name</>. + In <application>PL/pgSQL</>, you can instead avoid a conflict by + qualifying the column or table name. </para> </listitem> @@ -3684,7 +3760,7 @@ $$ LANGUAGE plpgsql; <function>quote_ident</function> as described in <xref linkend="plpgsql-statements-executing-dyn">. Constructs of the type <literal>EXECUTE 'SELECT * FROM $1';</literal> will not work - unless you use these functions. + reliably unless you use these functions. </para> </sect3> |