Hi patchers,
Per discussion on -hackers, I've implemented a new internal function
quote_nullable, as an alternative to quote_literal. The difference is
that quote_nullable returns the text value 'NULL' on NULL input, which
is suitable for insertion into an SQL statement.
The idea is that when you're writing a plpgsql function with dynamic
queries, you can use quote_nullable for values which are
possibly-null. You're still responsible for handling NULLs sensibly
within your query, but at least you get a syntactically valid SQL
statement.
I've included doc updates but no new regression tests. I did not add
tests because there are currently no tests for quote_literal and when
I recently suggested addition of tests for quote_ident [1] they were
rejected. I still don't fully understand the criteria for inclusion
of regression tests, but this is a similar situation, so I'm following
the same guidance.
Patch compiles cleanly and passes make check on x86 gentoo.
Thanks for your time,
BJ
[1] http://archives.postgresql.org/pgsql-patches/2007-10/msg00080.php
On 10/11/07, Tom Lane <[EMAIL PROTECTED]> wrote:
> Well, it's clearly useful in INSERT and UPDATE. For WHERE cases, you
> might or might not be able to use it, but I note that quote_nullable()
> would work much more like what happens if you use a parameter symbol
> and then bind NULL as the actual parameter value ...
>
> In hindsight we should probably have done quote_literal the way the OP
> suggests, but I concur that it's too late to change it. An additional
> function seems a reasonable compromise.
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /home/brendanjurd/src/pgsql.cvs/pgsql/doc/src/sgml/func.sgml,v
retrieving revision 1.400
diff -c -r1.400 func.sgml
*** doc/src/sgml/func.sgml 11 Oct 2007 02:43:55 -0000 1.400
--- doc/src/sgml/func.sgml 11 Oct 2007 15:53:37 -0000
***************
*** 1263,1268 ****
--- 1263,1271 ----
<primary>quote_literal</primary>
</indexterm>
<indexterm>
+ <primary>quote_nullable</primary>
+ </indexterm>
+ <indexterm>
<primary>repeat</primary>
</indexterm>
<indexterm>
***************
*** 1532,1545 ****
<entry><type>text</type></entry>
<entry>
Return the given string suitably quoted to be used as a string literal
! in an <acronym>SQL</acronym> statement string.
! Embedded single-quotes and backslashes are properly doubled.
</entry>
<entry><literal>quote_literal('O\'Reilly')</literal></entry>
<entry><literal>'O''Reilly'</literal></entry>
</row>
<row>
<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>
<entry><type>setof text[]</type></entry>
<entry>
--- 1535,1564 ----
<entry><type>text</type></entry>
<entry>
Return the given string suitably quoted to be used as a string literal
! in an <acronym>SQL</acronym> statement string. Embedded single-quotes
! and backslashes are properly doubled. Note that
! <literal><function>quote_literal</function></literal> returns null on
! null input; use <literal><function>quote_nullable</function></literal>
! instead if you want to use nulls in your <acronym>SQL</acronym>
! statement.
</entry>
<entry><literal>quote_literal('O\'Reilly')</literal></entry>
<entry><literal>'O''Reilly'</literal></entry>
</row>
<row>
+
<entry><literal><function>quote_nullable</function>(<parameter>string</parameter>)</literal></entry>
+ <entry><type>text</type></entry>
+ <entry>
+ Return the given string (which may be null) suitably quoted to be used
+ as a string literal in an <acronym>SQL</acronym> statement string.
+ Embedded single-quotes and backslashes are properly doubled.
+ </entry>
+ <entry><literal>quote_literal(NULL)</literal></entry>
+ <entry><literal>NULL</literal></entry>
+ </row>
+
+ <row>
<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>
<entry><type>setof text[]</type></entry>
<entry>
Index: src/backend/utils/adt/quote.c
===================================================================
RCS file: /home/brendanjurd/src/pgsql.cvs/pgsql/src/backend/utils/adt/quote.c,v
retrieving revision 1.22
diff -c -r1.22 quote.c
*** src/backend/utils/adt/quote.c 27 Feb 2007 23:48:08 -0000 1.22
--- src/backend/utils/adt/quote.c 11 Oct 2007 15:52:45 -0000
***************
*** 96,98 ****
--- 96,114 ----
PG_RETURN_TEXT_P(result);
}
+
+ /*
+ * quote_nullable -
+ * Returns a properly quoted literal, with null values returned as the SQL
+ * value NULL.
+ */
+ Datum
+ quote_nullable(PG_FUNCTION_ARGS)
+ {
+ if (PG_ARGISNULL(0))
+ PG_RETURN_DATUM(DirectFunctionCall1(textin,
+
CStringGetDatum("NULL")));
+ else
+ PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
+
PG_GETARG_DATUM(0)));
+ }
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /home/brendanjurd/src/pgsql.cvs/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.473
diff -c -r1.473 pg_proc.h
*** src/include/catalog/pg_proc.h 25 Sep 2007 20:03:38 -0000 1.473
--- src/include/catalog/pg_proc.h 11 Oct 2007 14:08:04 -0000
***************
*** 2627,2636 ****
DATA(insert OID = 1768 ( to_char PGNSP PGUID 12 1 0 f f
t f s 2 25 "1186 25" _null_ _null_ _null_ interval_to_char - _null_ _null_ ));
DESCR("format interval to text");
! DATA(insert OID = 1282 ( quote_ident PGNSP PGUID 12 1 0 f f t f i 1 25
"25" _null_ _null_ _null_ quote_ident - _null_ _null_ ));
DESCR("quote an identifier for usage in a querystring");
! DATA(insert OID = 1283 ( quote_literal PGNSP PGUID 12 1 0 f f t f i 1 25
"25" _null_ _null_ _null_ quote_literal - _null_ _null_ ));
DESCR("quote a literal for usage in a querystring");
DATA(insert OID = 1798 ( oidin PGNSP PGUID 12 1 0 f
f t f i 1 26 "2275" _null_ _null_ _null_ oidin - _null_ _null_ ));
DESCR("I/O");
--- 2627,2638 ----
DATA(insert OID = 1768 ( to_char PGNSP PGUID 12 1 0 f f
t f s 2 25 "1186 25" _null_ _null_ _null_ interval_to_char - _null_ _null_ ));
DESCR("format interval to text");
! DATA(insert OID = 1282 ( quote_ident PGNSP PGUID 12 1 0 f f t f i 1 25
"25" _null_ _null_ _null_ quote_ident - _null_ _null_ ));
DESCR("quote an identifier for usage in a querystring");
! DATA(insert OID = 1283 ( quote_literal PGNSP PGUID 12 1 0 f f t f i 1 25
"25" _null_ _null_ _null_ quote_literal - _null_ _null_ ));
DESCR("quote a literal for usage in a querystring");
+ DATA(insert OID = 1285 ( quote_nullable PGNSP PGUID 12 1 0 f f f f i 1 25
"25" _null_ _null_ _null_ quote_nullable - _null_ _null_ ));
+ DESCR("quote a possibly-null literal for usage in a querystring");
DATA(insert OID = 1798 ( oidin PGNSP PGUID 12 1 0 f
f t f i 1 26 "2275" _null_ _null_ _null_ oidin - _null_ _null_ ));
DESCR("I/O");
Index: src/include/utils/builtins.h
===================================================================
RCS file: /home/brendanjurd/src/pgsql.cvs/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.304
diff -c -r1.304 builtins.h
*** src/include/utils/builtins.h 24 Sep 2007 01:29:30 -0000 1.304
--- src/include/utils/builtins.h 11 Oct 2007 14:08:04 -0000
***************
*** 916,921 ****
--- 916,922 ----
/* quote.c */
extern Datum quote_ident(PG_FUNCTION_ARGS);
extern Datum quote_literal(PG_FUNCTION_ARGS);
+ extern Datum quote_nullable(PG_FUNCTION_ARGS);
/* guc.c */
extern Datum show_config_by_name(PG_FUNCTION_ARGS);
---------------------------(end of broadcast)---------------------------
TIP 6: explain analyze is your friend