Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7de8112

Browse files
committed
Create a function quote_nullable(), which works the same as quote_literal()
except that it returns the string 'NULL', rather than a SQL null, when called with a null argument. This is often a much more useful behavior for constructing dynamic queries. Add more discussion to the documentation about how to use these functions. Brendan Jurd
1 parent 40a3dfb commit 7de8112

File tree

6 files changed

+134
-27
lines changed

6 files changed

+134
-27
lines changed

doc/src/sgml/func.sgml

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.424 2008/03/10 12:39:22 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.425 2008/03/23 00:24:19 tgl Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -1262,6 +1262,9 @@
12621262
<indexterm>
12631263
<primary>quote_literal</primary>
12641264
</indexterm>
1265+
<indexterm>
1266+
<primary>quote_nullable</primary>
1267+
</indexterm>
12651268
<indexterm>
12661269
<primary>repeat</primary>
12671270
</indexterm>
@@ -1523,6 +1526,7 @@
15231526
Quotes are added only if necessary (i.e., if the string contains
15241527
non-identifier characters or would be case-folded).
15251528
Embedded quotes are properly doubled.
1529+
See also <xref linkend="plpgsql-quote-literal-example">.
15261530
</entry>
15271531
<entry><literal>quote_ident('Foo bar')</literal></entry>
15281532
<entry><literal>"Foo bar"</literal></entry>
@@ -1535,6 +1539,10 @@
15351539
Return the given string suitably quoted to be used as a string literal
15361540
in an <acronym>SQL</acronym> statement string.
15371541
Embedded single-quotes and backslashes are properly doubled.
1542+
Note that <function>quote_literal</function> returns null on null
1543+
input; if the argument might be null,
1544+
<function>quote_nullable</function> is often more suitable.
1545+
See also <xref linkend="plpgsql-quote-literal-example">.
15381546
</entry>
15391547
<entry><literal>quote_literal('O\'Reilly')</literal></entry>
15401548
<entry><literal>'O''Reilly'</literal></entry>
@@ -1551,6 +1559,32 @@
15511559
<entry><literal>'42.5'</literal></entry>
15521560
</row>
15531561

1562+
<row>
1563+
<entry><literal><function>quote_nullable</function>(<parameter>string</parameter> <type>text</type>)</literal></entry>
1564+
<entry><type>text</type></entry>
1565+
<entry>
1566+
Return the given string suitably quoted to be used as a string literal
1567+
in an <acronym>SQL</acronym> statement string; or, if the argument
1568+
is null, return <literal>NULL</>.
1569+
Embedded single-quotes and backslashes are properly doubled.
1570+
See also <xref linkend="plpgsql-quote-literal-example">.
1571+
</entry>
1572+
<entry><literal>quote_nullable(NULL)</literal></entry>
1573+
<entry><literal>NULL</literal></entry>
1574+
</row>
1575+
1576+
<row>
1577+
<entry><literal><function>quote_nullable</function>(<parameter>value</parameter> <type>anyelement</type>)</literal></entry>
1578+
<entry><type>text</type></entry>
1579+
<entry>
1580+
Coerce the given value to text and then quote it as a literal;
1581+
or, if the argument is null, return <literal>NULL</>.
1582+
Embedded single-quotes and backslashes are properly doubled.
1583+
</entry>
1584+
<entry><literal>quote_nullable(42.5)</literal></entry>
1585+
<entry><literal>'42.5'</literal></entry>
1586+
</row>
1587+
15541588
<row>
15551589
<entry><literal><function>regexp_matches</function>(<parameter>string</parameter> <type>text</type>, <parameter>pattern</parameter> <type>text</type> [, <parameter>flags</parameter> <type>text</type>])</literal></entry>
15561590
<entry><type>setof text[]</type></entry>

doc/src/sgml/plpgsql.sgml

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.123 2008/01/23 02:04:47 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.124 2008/03/23 00:24:19 tgl Exp $ -->
22

33
<chapter id="plpgsql">
44
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1066,6 +1066,24 @@ EXECUTE <replaceable class="command">command-string</replaceable> <optional> INT
10661066
</para>
10671067
</note>
10681068

1069+
<example id="plpgsql-quote-literal-example">
1070+
<title>Quoting values in dynamic queries</title>
1071+
1072+
<indexterm>
1073+
<primary>quote_ident</primary>
1074+
<secondary>use in PL/PgSQL</secondary>
1075+
</indexterm>
1076+
1077+
<indexterm>
1078+
<primary>quote_literal</primary>
1079+
<secondary>use in PL/PgSQL</secondary>
1080+
</indexterm>
1081+
1082+
<indexterm>
1083+
<primary>quote_nullable</primary>
1084+
<secondary>use in PL/PgSQL</secondary>
1085+
</indexterm>
1086+
10691087
<para>
10701088
When working with dynamic commands you will often have to handle escaping
10711089
of single quotes. The recommended method for quoting fixed text in your
@@ -1091,32 +1109,64 @@ EXECUTE 'UPDATE tbl SET '
10911109
</programlisting>
10921110
</para>
10931111

1094-
<indexterm>
1095-
<primary>quote_ident</primary>
1096-
<secondary>use in PL/PgSQL</secondary>
1097-
</indexterm>
1098-
1099-
<indexterm>
1100-
<primary>quote_literal</primary>
1101-
<secondary>use in PL/PgSQL</secondary>
1102-
</indexterm>
1103-
11041112
<para>
11051113
This example demonstrates the use of the
11061114
<function>quote_ident</function> and
1107-
<function>quote_literal</function> functions. For safety,
1108-
expressions containing column and table identifiers should be
1109-
passed to <function>quote_ident</function>. Expressions containing
1110-
values that should be literal strings in the constructed command
1111-
should be passed to <function>quote_literal</function>. Both
1112-
take the appropriate steps to return the input text enclosed in
1113-
double or single quotes respectively, with any embedded special
1114-
characters properly escaped.
1115+
<function>quote_literal</function> functions (see <xref
1116+
linkend="functions-string">). For safety, expressions containing column
1117+
or table identifiers should be passed through
1118+
<function>quote_ident</function> before insertion in a dynamic query.
1119+
Expressions containing values that should be literal strings in the
1120+
constructed command should be passed through <function>quote_literal</>.
1121+
These functions take the appropriate steps to return the input text
1122+
enclosed in double or single quotes respectively, with any embedded
1123+
special characters properly escaped.
1124+
</para>
1125+
1126+
<para>
1127+
Because <function>quote_literal</function> is labelled
1128+
<literal>STRICT</literal>, it will always return null when called with a
1129+
null argument. In the above example, if <literal>newvalue</> or
1130+
<literal>keyvalue</> were null, the entire dynamic query string would
1131+
become null, leading to an error from <command>EXECUTE</command>.
1132+
You can avoid this problem by using the <function>quote_nullable</>
1133+
function, which works the same as <function>quote_literal</> except that
1134+
when called with a null argument it returns the string <literal>NULL</>.
1135+
For example,
1136+
<programlisting>
1137+
EXECUTE 'UPDATE tbl SET '
1138+
|| quote_ident(colname)
1139+
|| ' = '
1140+
|| quote_nullable(newvalue)
1141+
|| ' WHERE key = '
1142+
|| quote_nullable(keyvalue);
1143+
</programlisting>
1144+
If you are dealing with values that might be null, you should usually
1145+
use <function>quote_nullable</> in place of <function>quote_literal</>.
1146+
</para>
1147+
1148+
<para>
1149+
As always, care must be taken to ensure that null values in a query do
1150+
not deliver unintended results. For example the <literal>WHERE</> clause
1151+
<programlisting>
1152+
'WHERE key = ' || quote_nullable(keyvalue)
1153+
</programlisting>
1154+
will never succeed if <literal>keyvalue</> is null, because the
1155+
result of using the equality operator <literal>=</> with a null operand
1156+
is always null. If you wish null to work like an ordinary key value,
1157+
you would need to rewrite the above as
1158+
<programlisting>
1159+
'WHERE key IS NOT DISTINCT FROM ' || quote_nullable(keyvalue)
1160+
</programlisting>
1161+
(At present, <literal>IS NOT DISTINCT FROM</> is handled much less
1162+
efficiently than <literal>=</>, so don't do this unless you must.
1163+
See <xref linkend="functions-comparison"> for
1164+
more information on nulls and <literal>IS DISTINCT</>.)
11151165
</para>
11161166

11171167
<para>
11181168
Note that dollar quoting is only useful for quoting fixed text.
1119-
It would be a very bad idea to try to do the above example as:
1169+
It would be a very bad idea to try to write this example as:
11201170
<programlisting>
11211171
EXECUTE 'UPDATE tbl SET '
11221172
|| quote_ident(colname)
@@ -1129,8 +1179,10 @@ EXECUTE 'UPDATE tbl SET '
11291179
happened to contain <literal>$$</>. The same objection would
11301180
apply to any other dollar-quoting delimiter you might pick.
11311181
So, to safely quote text that is not known in advance, you
1132-
<emphasis>must</> use <function>quote_literal</function>.
1182+
<emphasis>must</> use <function>quote_literal</>,
1183+
<function>quote_nullable</>, or <function>quote_ident</>, as appropriate.
11331184
</para>
1185+
</example>
11341186

11351187
<para>
11361188
A much larger example of a dynamic command and

src/backend/utils/adt/quote.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/quote.c,v 1.23 2008/01/01 19:45:52 momjian Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/quote.c,v 1.24 2008/03/23 00:24:19 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -96,3 +96,19 @@ quote_literal(PG_FUNCTION_ARGS)
9696

9797
PG_RETURN_TEXT_P(result);
9898
}
99+
100+
/*
101+
* quote_nullable -
102+
* Returns a properly quoted literal, with null values returned
103+
* as the text string 'NULL'.
104+
*/
105+
Datum
106+
quote_nullable(PG_FUNCTION_ARGS)
107+
{
108+
if (PG_ARGISNULL(0))
109+
PG_RETURN_DATUM(DirectFunctionCall1(textin,
110+
CStringGetDatum("NULL")));
111+
else
112+
PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
113+
PG_GETARG_DATUM(0)));
114+
}

src/include/catalog/catversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.443 2008/03/22 01:55:14 ishii Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.444 2008/03/23 00:24:19 tgl Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200803221
56+
#define CATALOG_VERSION_NO 200803222
5757

5858
#endif

src/include/catalog/pg_proc.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.483 2008/03/22 01:55:14 ishii Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.484 2008/03/23 00:24:19 tgl Exp $
1111
*
1212
* NOTES
1313
* The script catalog/genbki.sh reads this file and generates .bki
@@ -2635,6 +2635,10 @@ DATA(insert OID = 1283 ( quote_literal PGNSP PGUID 12 1 0 f f t f i 1 25 "25
26352635
DESCR("quote a literal for usage in a querystring");
26362636
DATA(insert OID = 1285 ( quote_literal PGNSP PGUID 14 1 0 f f t f v 1 25 "2283" _null_ _null_ _null_ "select pg_catalog.quote_literal($1::pg_catalog.text)" - _null_ _null_ ));
26372637
DESCR("quote a data value for usage in a querystring");
2638+
DATA(insert OID = 1289 ( quote_nullable PGNSP PGUID 12 1 0 f f f f i 1 25 "25" _null_ _null_ _null_ quote_nullable - _null_ _null_ ));
2639+
DESCR("quote a possibly-null literal for usage in a querystring");
2640+
DATA(insert OID = 1290 ( quote_nullable PGNSP PGUID 14 1 0 f f f f v 1 25 "2283" _null_ _null_ _null_ "select pg_catalog.quote_nullable($1::pg_catalog.text)" - _null_ _null_ ));
2641+
DESCR("quote a possibly-null data value for usage in a querystring");
26382642

26392643
DATA(insert OID = 1798 ( oidin PGNSP PGUID 12 1 0 f f t f i 1 26 "2275" _null_ _null_ _null_ oidin - _null_ _null_ ));
26402644
DESCR("I/O");

src/include/utils/builtins.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.308 2008/01/01 19:45:59 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.309 2008/03/23 00:24:20 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -914,6 +914,7 @@ extern int32 type_maximum_size(Oid type_oid, int32 typemod);
914914
/* quote.c */
915915
extern Datum quote_ident(PG_FUNCTION_ARGS);
916916
extern Datum quote_literal(PG_FUNCTION_ARGS);
917+
extern Datum quote_nullable(PG_FUNCTION_ARGS);
917918

918919
/* guc.c */
919920
extern Datum show_config_by_name(PG_FUNCTION_ARGS);

0 commit comments

Comments
 (0)