pl/python quoting functions

Started by Jan Urbańskiabout 15 years ago7 messages
#1Jan Urbański
wulczer@wulczer.org
1 attachment(s)

Here's a patch that adds a few PL/Python functions for quoting strings.
It's an incremental patch on top of the plpython-refactor patch sent in
http://archives.postgresql.org/message-id/4D135170.3080705@wulczer.org.

Git branch for this patch:
https://github.com/wulczer/postgres/tree/functions

The new functions are plpy.quote_literal, plpy.quote_nullable and
plpy.quote_ident, and work just like their sql or plperl equivalents.

Cheers,
Jan

Attachments:

plpython-functions.difftext/x-patch; name=plpython-functions.diffDownload
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 16d78ae..292e360 100644
*** a/src/pl/plpython/Makefile
--- b/src/pl/plpython/Makefile
*************** REGRESS = \
*** 79,84 ****
--- 79,85 ----
  	plpython_types \
  	plpython_error \
  	plpython_unicode \
+ 	plpython_quote \
  	plpython_drop
  # where to find psql for running the tests
  PSQLDIR = $(bindir)
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index ...b33ee3f .
*** a/src/pl/plpython/expected/plpython_quote.out
--- b/src/pl/plpython/expected/plpython_quote.out
***************
*** 0 ****
--- 1,87 ----
+ -- test quoting functions
+ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
+     if how == "literal":
+         return plpy.quote_literal(t)
+     elif how == "nullable":
+         return plpy.quote_nullable(t)
+     elif how == "ident":
+         return plpy.quote_ident(t)
+     else:
+         raise plpy.Error("unrecognized quote type %s" % how)
+ $$ LANGUAGE plpythonu;
+ SELECT quote(t, 'literal') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        ('xyzv')) AS v(t);
+    quote   
+ -----------
+  'abc'
+  'a''bc'
+  '''abc'''
+  ''
+  ''''
+  'xyzv'
+ (6 rows)
+ 
+ SELECT quote(t, 'nullable') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        (NULL)) AS v(t);
+    quote   
+ -----------
+  'abc'
+  'a''bc'
+  '''abc'''
+  ''
+  ''''
+  NULL
+ (6 rows)
+ 
+ SELECT quote(t, 'ident') FROM (VALUES
+        ('abc'),
+        ('a b c'),
+        ('a " ''abc''')) AS v(t);
+     quote     
+ --------------
+  abc
+  "a b c"
+  "a "" 'abc'"
+ (3 rows)
+ 
+ -- test errors
+ SELECT quote(NULL::text, 'literal');
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python function "quote"
+ SELECT quote(NULL::text, 'ident');
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python function "quote"
+ SELECT quote('abc', 'random');
+ ERROR:  plpy.Error: unrecognized quote type random
+ CONTEXT:  PL/Python function "quote"
+ DO $$ plpy.quote_literal(3) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not int
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal(None) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal({'a': 'b'}) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not dict
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal() $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: function takes exactly 1 argument (0 given)
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal(1, 2) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: function takes exactly 1 argument (2 given)
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_nullable([1, 2]) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string or None, not list
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_ident(1.5) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not float
+ CONTEXT:  PL/Python anonymous code block
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index d92c987..961f6c0 100644
*** a/src/pl/plpython/expected/plpython_test.out
--- b/src/pl/plpython/expected/plpython_test.out
*************** contents.sort()
*** 43,51 ****
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                       module_contents                                      
! -------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
--- 43,51 ----
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                                             module_contents                                                            
! ---------------------------------------------------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 2307627..ba57519 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** static PyObject *PLy_spi_execute_query(c
*** 2524,2529 ****
--- 2524,2533 ----
  static PyObject *PLy_spi_execute_plan(PyObject *, PyObject *, long);
  static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int);
  
+ static PyObject *PLy_quote_literal(PyObject *, PyObject *);
+ static PyObject *PLy_quote_nullable(PyObject *, PyObject *);
+ static PyObject *PLy_quote_ident(PyObject *, PyObject *);
+ 
  
  static PyMethodDef PLy_plan_methods[] = {
  	{"status", PLy_plan_status, METH_VARARGS, NULL},
*************** static PyMethodDef PLy_methods[] = {
*** 2638,2643 ****
--- 2642,2654 ----
  	 */
  	{"execute", PLy_spi_execute, METH_VARARGS, NULL},
  
+ 	/*
+ 	 * escaping strings
+ 	 */
+ 	{"quote_literal", PLy_quote_literal, METH_VARARGS, NULL},
+ 	{"quote_nullable", PLy_quote_nullable, METH_VARARGS, NULL},
+ 	{"quote_ident", PLy_quote_ident, METH_VARARGS, NULL},
+ 
  	{NULL, NULL, 0, NULL}
  };
  
*************** PLy_output(volatile int level, PyObject
*** 3508,3513 ****
--- 3519,3583 ----
  	return Py_None;
  }
  
+ static PyObject *
+ PLy_quote_literal(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "s", &str))
+ 		return NULL;
+ 
+ 	quoted = quote_literal_cstr(str);
+ 	ret = PyString_FromString(quoted);
+ 	pfree(quoted);
+ 
+ 	return ret;
+ }
+ 
+ static PyObject *
+ PLy_quote_nullable(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "z", &str))
+ 		return NULL;
+ 
+ 	if (str == NULL)
+ 		return PyString_FromString("NULL");
+ 
+ 	quoted = quote_literal_cstr(str);
+ 	ret = PyString_FromString(quoted);
+ 	pfree(quoted);
+ 
+ 	return ret;
+ }
+ 
+ static PyObject *
+ PLy_quote_ident(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "s", &str))
+ 		return NULL;
+ 
+ 	/* cast to char * to avoid a "discards qualifier" warning */
+ 	quoted = (char *) quote_identifier(str);
+ 	ret = PyString_FromString(quoted);
+ 	/*
+ 	 * quote_identifier sometimes returns a palloc'd string, and sometimes
+ 	 * just the pointer passed
+ 	 */
+ 	if (quoted != str)
+ 		pfree(quoted);
+ 
+ 	return ret;
+ }
  
  /*
   * Get the name of the last procedure called by the backend (the
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index ...15cab3c .
*** a/src/pl/plpython/sql/plpython_quote.sql
--- b/src/pl/plpython/sql/plpython_quote.sql
***************
*** 0 ****
--- 1,45 ----
+ -- test quoting functions
+ 
+ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
+     if how == "literal":
+         return plpy.quote_literal(t)
+     elif how == "nullable":
+         return plpy.quote_nullable(t)
+     elif how == "ident":
+         return plpy.quote_ident(t)
+     else:
+         raise plpy.Error("unrecognized quote type %s" % how)
+ $$ LANGUAGE plpythonu;
+ 
+ SELECT quote(t, 'literal') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        ('xyzv')) AS v(t);
+ 
+ SELECT quote(t, 'nullable') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        (NULL)) AS v(t);
+ 
+ SELECT quote(t, 'ident') FROM (VALUES
+        ('abc'),
+        ('a b c'),
+        ('a " ''abc''')) AS v(t);
+ 
+ -- test errors
+ SELECT quote(NULL::text, 'literal');
+ SELECT quote(NULL::text, 'ident');
+ SELECT quote('abc', 'random');
+ DO $$ plpy.quote_literal(3) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal(None) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal({'a': 'b'}) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal() $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal(1, 2) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_nullable([1, 2]) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_ident(1.5) $$ LANGUAGE plpythonu;
#2Hitoshi Harada
umi.tanuki@gmail.com
In reply to: Jan Urbański (#1)
Re: pl/python quoting functions

2011/1/11 Jan Urbański <wulczer@wulczer.org>:

Here's a patch that adds a few PL/Python functions for quoting strings.
It's an incremental patch on top of the plpython-refactor patch sent in
http://archives.postgresql.org/message-id/4D135170.3080705@wulczer.org.

Git branch for this patch:
https://github.com/wulczer/postgres/tree/functions

The new functions are plpy.quote_literal, plpy.quote_nullable and
plpy.quote_ident, and work just like their sql or plperl equivalents.

I reviewed this.

The patch applies and compiles cleanly and all the tests are passed.
The patch adds 3 functions which works as the corresponding SQL
functions. The test is enough, without any additional docs. No
feature/performance issues found.

I mark this "Reader for Committer".

Regards,

--
Hitoshi Harada

#3Jan Urbański
wulczer@wulczer.org
In reply to: Hitoshi Harada (#2)
Re: pl/python quoting functions

On 04/02/11 18:10, Hitoshi Harada wrote:

2011/1/11 Jan Urbański <wulczer@wulczer.org>:

Here's a patch that adds a few PL/Python functions for quoting strings.
It's an incremental patch on top of the plpython-refactor patch sent in
http://archives.postgresql.org/message-id/4D135170.3080705@wulczer.org.

Git branch for this patch:
https://github.com/wulczer/postgres/tree/functions

The new functions are plpy.quote_literal, plpy.quote_nullable and
plpy.quote_ident, and work just like their sql or plperl equivalents.

I reviewed this.

The patch applies and compiles cleanly and all the tests are passed.
The patch adds 3 functions which works as the corresponding SQL
functions. The test is enough, without any additional docs. No
feature/performance issues found.

I mark this "Reader for Committer".

Thanks!

I guess a short paragraph in the Utility Functions section of the
PL/Python docs would be in order, I'll try to add it today.

Jan

#4Jan Urbański
wulczer@wulczer.org
In reply to: Jan Urbański (#3)
1 attachment(s)
Re: pl/python quoting functions

On 06/02/11 10:54, Jan Urbański wrote:

On 04/02/11 18:10, Hitoshi Harada wrote:

2011/1/11 Jan Urbański <wulczer@wulczer.org>:

Here's a patch that adds a few PL/Python functions for quoting strings.
It's an incremental patch on top of the plpython-refactor patch sent in
http://archives.postgresql.org/message-id/4D135170.3080705@wulczer.org.

Git branch for this patch:
https://github.com/wulczer/postgres/tree/functions

The new functions are plpy.quote_literal, plpy.quote_nullable and
plpy.quote_ident, and work just like their sql or plperl equivalents.

I reviewed this.

The patch applies and compiles cleanly and all the tests are passed.
The patch adds 3 functions which works as the corresponding SQL
functions. The test is enough, without any additional docs. No
feature/performance issues found.

I mark this "Reader for Committer".

Thanks!

I guess a short paragraph in the Utility Functions section of the
PL/Python docs would be in order, I'll try to add it today.

Added docs and merged with master.

Attachments:

plpython-functions.difftext/x-patch; name=plpython-functions.diffDownload
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index e05c293..8a995b2 100644
*** a/doc/src/sgml/plpython.sgml
--- b/doc/src/sgml/plpython.sgml
*************** $$ LANGUAGE plpythonu;
*** 997,1002 ****
--- 997,1018 ----
     <xref linkend="guc-client-min-messages"> configuration
     variables. See <xref linkend="runtime-config"> for more information.
    </para>
+   <para>
+    Another set of utility functions
+    are <literal>plpy.quote_literal(<replaceable>string</>)</literal>,
+    <literal>plpy.quote_nullable(<replaceable>string</>)</literal>
+    and <literal>plpy.quote_ident(<replaceable>string</>)</literal>. They are
+    equivalent to the builtin quoting functions described
+    in <xref linkend="functions-string">. They are useful when constructing
+    ad-hoc queries. A PL/Python equivalent of dynamic SQL
+    from <xref linkend="plpgsql-quote-literal-example"> would be:
+ <programlisting>
+ plpy.execute("UPDATE tbl SET %s = %s where key = %s" % (
+     plpy.quote_ident(colname),
+     plpy.quote_nullable(newvalue),
+     plpy.quote_literal(keyvalue)))
+ </programlisting>
+   </para>
   </sect1>
  
   <sect1 id="plpython-envar">
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 16d78ae..292e360 100644
*** a/src/pl/plpython/Makefile
--- b/src/pl/plpython/Makefile
*************** REGRESS = \
*** 79,84 ****
--- 79,85 ----
  	plpython_types \
  	plpython_error \
  	plpython_unicode \
+ 	plpython_quote \
  	plpython_drop
  # where to find psql for running the tests
  PSQLDIR = $(bindir)
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index ...b33ee3f .
*** a/src/pl/plpython/expected/plpython_quote.out
--- b/src/pl/plpython/expected/plpython_quote.out
***************
*** 0 ****
--- 1,87 ----
+ -- test quoting functions
+ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
+     if how == "literal":
+         return plpy.quote_literal(t)
+     elif how == "nullable":
+         return plpy.quote_nullable(t)
+     elif how == "ident":
+         return plpy.quote_ident(t)
+     else:
+         raise plpy.Error("unrecognized quote type %s" % how)
+ $$ LANGUAGE plpythonu;
+ SELECT quote(t, 'literal') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        ('xyzv')) AS v(t);
+    quote   
+ -----------
+  'abc'
+  'a''bc'
+  '''abc'''
+  ''
+  ''''
+  'xyzv'
+ (6 rows)
+ 
+ SELECT quote(t, 'nullable') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        (NULL)) AS v(t);
+    quote   
+ -----------
+  'abc'
+  'a''bc'
+  '''abc'''
+  ''
+  ''''
+  NULL
+ (6 rows)
+ 
+ SELECT quote(t, 'ident') FROM (VALUES
+        ('abc'),
+        ('a b c'),
+        ('a " ''abc''')) AS v(t);
+     quote     
+ --------------
+  abc
+  "a b c"
+  "a "" 'abc'"
+ (3 rows)
+ 
+ -- test errors
+ SELECT quote(NULL::text, 'literal');
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python function "quote"
+ SELECT quote(NULL::text, 'ident');
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python function "quote"
+ SELECT quote('abc', 'random');
+ ERROR:  plpy.Error: unrecognized quote type random
+ CONTEXT:  PL/Python function "quote"
+ DO $$ plpy.quote_literal(3) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not int
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal(None) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not None
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal({'a': 'b'}) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not dict
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal() $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: function takes exactly 1 argument (0 given)
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_literal(1, 2) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: function takes exactly 1 argument (2 given)
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_nullable([1, 2]) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string or None, not list
+ CONTEXT:  PL/Python anonymous code block
+ DO $$ plpy.quote_ident(1.5) $$ LANGUAGE plpythonu;
+ ERROR:  TypeError: argument 1 must be string, not float
+ CONTEXT:  PL/Python anonymous code block
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index d92c987..961f6c0 100644
*** a/src/pl/plpython/expected/plpython_test.out
--- b/src/pl/plpython/expected/plpython_test.out
*************** contents.sort()
*** 43,51 ****
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                       module_contents                                      
! -------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
--- 43,51 ----
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                                             module_contents                                                            
! ---------------------------------------------------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index fff7de7..39c7e53 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** static PyObject *PLy_spi_execute_query(c
*** 2558,2563 ****
--- 2558,2567 ----
  static PyObject *PLy_spi_execute_plan(PyObject *, PyObject *, long);
  static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int);
  
+ static PyObject *PLy_quote_literal(PyObject *, PyObject *);
+ static PyObject *PLy_quote_nullable(PyObject *, PyObject *);
+ static PyObject *PLy_quote_ident(PyObject *, PyObject *);
+ 
  
  static PyMethodDef PLy_plan_methods[] = {
  	{"status", PLy_plan_status, METH_VARARGS, NULL},
*************** static PyMethodDef PLy_methods[] = {
*** 2672,2677 ****
--- 2676,2688 ----
  	 */
  	{"execute", PLy_spi_execute, METH_VARARGS, NULL},
  
+ 	/*
+ 	 * escaping strings
+ 	 */
+ 	{"quote_literal", PLy_quote_literal, METH_VARARGS, NULL},
+ 	{"quote_nullable", PLy_quote_nullable, METH_VARARGS, NULL},
+ 	{"quote_ident", PLy_quote_ident, METH_VARARGS, NULL},
+ 
  	{NULL, NULL, 0, NULL}
  };
  
*************** PLy_output(volatile int level, PyObject
*** 3608,3613 ****
--- 3619,3683 ----
  	return Py_None;
  }
  
+ static PyObject *
+ PLy_quote_literal(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "s", &str))
+ 		return NULL;
+ 
+ 	quoted = quote_literal_cstr(str);
+ 	ret = PyString_FromString(quoted);
+ 	pfree(quoted);
+ 
+ 	return ret;
+ }
+ 
+ static PyObject *
+ PLy_quote_nullable(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "z", &str))
+ 		return NULL;
+ 
+ 	if (str == NULL)
+ 		return PyString_FromString("NULL");
+ 
+ 	quoted = quote_literal_cstr(str);
+ 	ret = PyString_FromString(quoted);
+ 	pfree(quoted);
+ 
+ 	return ret;
+ }
+ 
+ static PyObject *
+ PLy_quote_ident(PyObject *self, PyObject *args)
+ {
+ 	char		*str;
+ 	char		*quoted;
+ 	PyObject	*ret;
+ 
+ 	if (!PyArg_ParseTuple(args, "s", &str))
+ 		return NULL;
+ 
+ 	/* cast to char * to avoid a "discards qualifier" warning */
+ 	quoted = (char *) quote_identifier(str);
+ 	ret = PyString_FromString(quoted);
+ 	/*
+ 	 * quote_identifier sometimes returns a palloc'd string, and sometimes
+ 	 * just the pointer passed
+ 	 */
+ 	if (quoted != str)
+ 		pfree(quoted);
+ 
+ 	return ret;
+ }
  
  /*
   * Get the name of the last procedure called by the backend (the
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index ...15cab3c .
*** a/src/pl/plpython/sql/plpython_quote.sql
--- b/src/pl/plpython/sql/plpython_quote.sql
***************
*** 0 ****
--- 1,45 ----
+ -- test quoting functions
+ 
+ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
+     if how == "literal":
+         return plpy.quote_literal(t)
+     elif how == "nullable":
+         return plpy.quote_nullable(t)
+     elif how == "ident":
+         return plpy.quote_ident(t)
+     else:
+         raise plpy.Error("unrecognized quote type %s" % how)
+ $$ LANGUAGE plpythonu;
+ 
+ SELECT quote(t, 'literal') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        ('xyzv')) AS v(t);
+ 
+ SELECT quote(t, 'nullable') FROM (VALUES
+        ('abc'),
+        ('a''bc'),
+        ('''abc'''),
+        (''),
+        (''''),
+        (NULL)) AS v(t);
+ 
+ SELECT quote(t, 'ident') FROM (VALUES
+        ('abc'),
+        ('a b c'),
+        ('a " ''abc''')) AS v(t);
+ 
+ -- test errors
+ SELECT quote(NULL::text, 'literal');
+ SELECT quote(NULL::text, 'ident');
+ SELECT quote('abc', 'random');
+ DO $$ plpy.quote_literal(3) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal(None) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal({'a': 'b'}) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal() $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_literal(1, 2) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_nullable([1, 2]) $$ LANGUAGE plpythonu;
+ DO $$ plpy.quote_ident(1.5) $$ LANGUAGE plpythonu;
#5Peter Eisentraut
peter_e@gmx.net
In reply to: Jan Urbański (#4)
Re: pl/python quoting functions

Committed this, with two changes: Changed some things around with the
way const char * is propagated. Just casting it away is not nice. Also
dropped the error tests in the _quote.sql regression test. This
generates three different wordings of error messages from Python with
2.6, 3.1, and 3.2, which I don't care to maintain. Maybe one day we'll
have a better solution for this.

#6Jan Urbański
wulczer@wulczer.org
In reply to: Peter Eisentraut (#5)
Re: pl/python quoting functions

On 22/02/11 22:48, Peter Eisentraut wrote:

Committed this, with two changes: Changed some things around with the
way const char * is propagated. Just casting it away is not nice. Also
dropped the error tests in the _quote.sql regression test. This
generates three different wordings of error messages from Python with
2.6, 3.1, and 3.2, which I don't care to maintain. Maybe one day we'll
have a better solution for this.

Thanks.

One thing: you removed the conditional pfree from PLy_quote_ident, which
makes this function leak memory if the actual quoting took place, no? Is
that leak too small to worry about?

Cheers,
Jan

#7Peter Eisentraut
peter_e@gmx.net
In reply to: Jan Urbański (#6)
Re: pl/python quoting functions

On ons, 2011-02-23 at 11:26 +0100, Jan Urbański wrote:

One thing: you removed the conditional pfree from PLy_quote_ident,
which makes this function leak memory if the actual quoting took
place, no? Is that leak too small to worry about?

Many functions in PostgreSQL leak memory in this way. It's not worth
worrying about, and certainly not worth violating a called function's
API for it.