Skip to content

Commit a584c12

Browse files
committed
Add STRICT to PL/pgSQL SELECT INTO, so exceptions are thrown if more or
less than one row is returned by the SELECT, for Oracle PL/SQL compatibility. Improve SELECT INTO documentation. Matt Miller
1 parent eb5558b commit a584c12

File tree

6 files changed

+78
-44
lines changed

6 files changed

+78
-44
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.95 2006/06/12 16:45:30 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.96 2006/06/15 18:02:22 momjian Exp $ -->
22

33
<chapter id="plpgsql">
44
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1081,7 +1081,7 @@ tax := subtotal * 0.06;
10811081
variable, or list of scalar variables. This is done by:
10821082

10831083
<synopsis>
1084-
SELECT INTO <replaceable>target</replaceable> <replaceable>select_expressions</replaceable> FROM ...;
1084+
SELECT INTO <optional>STRICT</optional> <replaceable>target</replaceable> <replaceable>select_expressions</replaceable> FROM ...;
10851085
</synopsis>
10861086

10871087
where <replaceable>target</replaceable> can be a record variable, a row
@@ -1122,47 +1122,43 @@ SELECT INTO <replaceable>target</replaceable> <replaceable>select_expressions</r
11221122
</para>
11231123

11241124
<para>
1125-
If the query returns zero rows, null values are assigned to the
1126-
target(s). If the query returns multiple rows, the first
1127-
row is assigned to the target(s) and the rest are discarded.
1128-
(Note that <quote>the first row</> is not well-defined unless you've
1129-
used <literal>ORDER BY</>.)
1130-
</para>
1131-
1132-
<para>
1133-
You can check the special <literal>FOUND</literal> variable (see
1134-
<xref linkend="plpgsql-statements-diagnostics">) after a
1135-
<command>SELECT INTO</command> statement to determine whether the
1136-
assignment was successful, that is, at least one row was was returned by
1137-
the query. For example:
1125+
If <literal>STRICT</literal> is not specified then
1126+
<replaceable>target</replaceable> will be set to the first row
1127+
returned by the query, or if the query returned no rows,
1128+
null values are assigned. (Note that <quote>the first row</> is not
1129+
well-defined unless you've used <literal>ORDER BY</>.)
1130+
You can check the special <literal>FOUND</literal> variable to
1131+
determine if any rows were found:
11381132

11391133
<programlisting>
1140-
SELECT INTO myrec * FROM emp WHERE empname = myname;
1134+
SELECT INTO STRICT myrec * FROM emp WHERE empname = myname;
11411135
IF NOT FOUND THEN
11421136
RAISE EXCEPTION 'employee % not found', myname;
11431137
END IF;
11441138
</programlisting>
1145-
</para>
11461139

11471140
<para>
1148-
To test for whether a record/row result is null, you can use the
1149-
<literal>IS NULL</literal> conditional. There is, however, no
1150-
way to tell whether any additional rows might have been
1151-
discarded. Here is an example that handles the case where no
1152-
rows have been returned:
1153-
<programlisting>
1154-
DECLARE
1155-
users_rec RECORD;
1156-
BEGIN
1157-
SELECT INTO users_rec * FROM users WHERE user_id=3;
1141+
If the <literal>STRICT</literal> option is specified, a query must
1142+
return exactly one row or a run-time error will be thrown, either
1143+
<literal>NO_DATA_FOUND</> (no rows) or <literal>TOO_MANY_ROWS</>
1144+
(more than one row). You can must use exception blocks to determine
1145+
the number of rows generated by the query:
11581146

1159-
IF users_rec.homepage IS NULL THEN
1160-
-- user entered no homepage, return "http://"
1161-
RETURN 'http://';
1162-
END IF;
1147+
<programlisting>
1148+
BEGIN;
1149+
SELECT INTO STRICT myrec * FROM emp WHERE empname = myname;
1150+
EXCEPTION
1151+
WHEN NO_DATA_FOUND THEN
1152+
RAISE EXCEPTION 'employee % not found', myname;
1153+
WHEN TOO_MANY_ROWS THEN
1154+
RAISE EXCEPTION 'employee % not unique', myname;
11631155
END;
11641156
</programlisting>
1157+
Only <command>SELECT INTO STRICT</command> allows you to check if more
1158+
than one row was retrieved. <command>SELECT INTO STRICT</command>
1159+
matches Oracle's PL/SQL <command>SELECT INTO</command> behavior.
11651160
</para>
1161+
11661162
</sect2>
11671163

11681164
<sect2 id="plpgsql-statements-perform">
@@ -1424,8 +1420,8 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
14241420
<itemizedlist>
14251421
<listitem>
14261422
<para>
1427-
A <command>SELECT INTO</command> statement sets
1428-
<literal>FOUND</literal> true if it returns a row, false if no
1423+
A <command>SELECT INTO</command> statement sets
1424+
<literal>FOUND</literal> true if a row is assigned, false if no
14291425
row is returned.
14301426
</para>
14311427
</listitem>

src/pl/plpgsql/src/gram.y

Lines changed: 9 additions & 1 deletion
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.91 2006/06/12 16:45:30 momjian Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.92 2006/06/15 18:02:22 momjian Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -157,6 +157,7 @@ static void check_labels(const char *start_label,
157157
%token K_ELSE
158158
%token K_ELSIF
159159
%token K_END
160+
%token K_STRICT
160161
%token K_EXCEPTION
161162
%token K_EXECUTE
162163
%token K_EXIT
@@ -2001,6 +2002,7 @@ make_select_stmt(void)
20012002
PLpgSQL_rec *rec = NULL;
20022003
int tok;
20032004
bool have_into = false;
2005+
bool have_strict = false;
20042006

20052007
plpgsql_dstring_init(&ds);
20062008
plpgsql_dstring_append(&ds, "SELECT ");
@@ -2028,6 +2030,11 @@ make_select_stmt(void)
20282030
errmsg("INTO specified more than once")));
20292031
}
20302032
tok = yylex();
2033+
if (tok == K_STRICT)
2034+
{
2035+
have_strict = true;
2036+
tok = yylex();
2037+
}
20312038
switch (tok)
20322039
{
20332040
case T_ROW:
@@ -2108,6 +2115,7 @@ make_select_stmt(void)
21082115
select->rec = rec;
21092116
select->row = row;
21102117
select->query = expr;
2118+
select->strict = have_strict;
21112119

21122120
return (PLpgSQL_stmt *)select;
21132121
}

src/pl/plpgsql/src/pl_exec.c

Lines changed: 25 additions & 7 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.170 2006/06/12 16:45:30 momjian Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.171 2006/06/15 18:02:22 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1701,23 +1701,41 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
17011701

17021702
/*
17031703
* Run the query
1704+
* Retrieving two rows can be slower than a single row, e.g.
1705+
* a sequential scan where the scan has to be completed to
1706+
* check for a second row. For this reason, we only do the
1707+
* second-line check for STRICT.
17041708
*/
1705-
exec_run_select(estate, stmt->query, 1, NULL);
1709+
exec_run_select(estate, stmt->query, stmt->strict ? 2 : 1, NULL);
17061710
tuptab = estate->eval_tuptable;
17071711
n = estate->eval_processed;
17081712

17091713
/*
1710-
* If the query didn't return any rows, set the target to NULL and return.
1714+
* If SELECT ... INTO specified STRICT, and the query didn't
1715+
* find exactly one row, throw an error. If STRICT was not specified,
1716+
* then allow the query to find any number of rows.
17111717
*/
17121718
if (n == 0)
17131719
{
1714-
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
1715-
exec_eval_cleanup(estate);
1716-
return PLPGSQL_RC_OK;
1720+
if (!stmt->strict)
1721+
{
1722+
/* null the target */
1723+
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
1724+
exec_eval_cleanup(estate);
1725+
return PLPGSQL_RC_OK;
1726+
}
1727+
else
1728+
ereport(ERROR,
1729+
(errcode(ERRCODE_NO_DATA),
1730+
errmsg("query returned no rows")));
17171731
}
1732+
else if (n > 1 && stmt->strict)
1733+
ereport(ERROR,
1734+
(errcode(ERRCODE_CARDINALITY_VIOLATION),
1735+
errmsg("query more than one row")));
17181736

17191737
/*
1720-
* Put the result into the target and set found to true
1738+
* Put the first result into the target and set found to true
17211739
*/
17221740
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
17231741
exec_set_found(estate, true);

src/pl/plpgsql/src/plerrcodes.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
* Copyright (c) 2003-2006, PostgreSQL Global Development Group
1111
*
12-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.7 2006/03/05 15:59:10 momjian Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.8 2006/06/15 18:02:22 momjian Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -722,3 +722,13 @@
722722
{
723723
"index_corrupted", ERRCODE_INDEX_CORRUPTED
724724
},
725+
726+
{
727+
"no_data_found", ERRCODE_NO_DATA
728+
},
729+
730+
{
731+
"too_many_rows", ERRCODE_CARDINALITY_VIOLATION
732+
},
733+
734+

src/pl/plpgsql/src/plpgsql.h

Lines changed: 2 additions & 1 deletion
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.75 2006/06/12 16:45:30 momjian Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.76 2006/06/15 18:02:22 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -432,6 +432,7 @@ typedef struct
432432
{ /* SELECT ... INTO statement */
433433
int cmd_type;
434434
int lineno;
435+
bool strict;
435436
PLpgSQL_rec *rec;
436437
PLpgSQL_row *row;
437438
PLpgSQL_expr *query;

src/pl/plpgsql/src/scan.l

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.50 2006/06/12 16:45:30 momjian Exp $
12+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.51 2006/06/15 18:02:22 momjian Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -129,6 +129,7 @@ else { return K_ELSE; }
129129
elseif { return K_ELSIF; }
130130
elsif { return K_ELSIF; }
131131
end { return K_END; }
132+
strict { return K_STRICT; }
132133
exception { return K_EXCEPTION; }
133134
execute { return K_EXECUTE; }
134135
exit { return K_EXIT; }

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