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

Reply via email to