Skip to content

Commit 1c51c7d

Browse files
committed
Add PL/Python functions for quoting strings
Add functions plpy.quote_ident, plpy.quote_literal, plpy.quote_nullable, which wrap the equivalent SQL functions. To be able to propagate char * constness properly, make the argument of quote_literal_cstr() const char *. This also makes it more consistent with quote_identifier(). Jan Urbański, reviewed by Hitoshi Harada, some refinements by Peter Eisentraut
1 parent 3e6b305 commit 1c51c7d

File tree

8 files changed

+179
-7
lines changed

8 files changed

+179
-7
lines changed

doc/src/sgml/plpython.sgml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,23 @@ $$ LANGUAGE plpythonu;
997997
<xref linkend="guc-client-min-messages"> configuration
998998
variables. See <xref linkend="runtime-config"> for more information.
999999
</para>
1000+
1001+
<para>
1002+
Another set of utility functions are
1003+
<literal>plpy.quote_literal(<replaceable>string</>)</literal>,
1004+
<literal>plpy.quote_nullable(<replaceable>string</>)</literal>, and
1005+
<literal>plpy.quote_ident(<replaceable>string</>)</literal>. They
1006+
are equivalent to the built-in quoting functions described in <xref
1007+
linkend="functions-string">. They are useful when constructing
1008+
ad-hoc queries. A PL/Python equivalent of dynamic SQL from <xref
1009+
linkend="plpgsql-quote-literal-example"> would be:
1010+
<programlisting>
1011+
plpy.execute("UPDATE tbl SET %s = %s where key = %s" % (
1012+
plpy.quote_ident(colname),
1013+
plpy.quote_nullable(newvalue),
1014+
plpy.quote_literal(keyvalue)))
1015+
</programlisting>
1016+
</para>
10001017
</sect1>
10011018

10021019
<sect1 id="plpython-envar">

src/backend/utils/adt/quote.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ quote_ident(PG_FUNCTION_ARGS)
4343
* uses this for before thinking you know better.
4444
*/
4545
static size_t
46-
quote_literal_internal(char *dst, char *src, size_t len)
46+
quote_literal_internal(char *dst, const char *src, size_t len)
4747
{
48-
char *s;
48+
const char *s;
4949
char *savedst = dst;
5050

5151
for (s = src; s < src + len; s++)
@@ -99,7 +99,7 @@ quote_literal(PG_FUNCTION_ARGS)
9999
* returns a properly quoted literal
100100
*/
101101
char *
102-
quote_literal_cstr(char *rawstr)
102+
quote_literal_cstr(const char *rawstr)
103103
{
104104
char *result;
105105
int len;

src/include/utils/builtins.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ extern int32 type_maximum_size(Oid type_oid, int32 typemod);
988988
/* quote.c */
989989
extern Datum quote_ident(PG_FUNCTION_ARGS);
990990
extern Datum quote_literal(PG_FUNCTION_ARGS);
991-
extern char *quote_literal_cstr(char *rawstr);
991+
extern char *quote_literal_cstr(const char *rawstr);
992992
extern Datum quote_nullable(PG_FUNCTION_ARGS);
993993

994994
/* guc.c */

src/pl/plpython/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ REGRESS = \
7979
plpython_types \
8080
plpython_error \
8181
plpython_unicode \
82+
plpython_quote \
8283
plpython_drop
8384
# where to find psql for running the tests
8485
PSQLDIR = $(bindir)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
-- test quoting functions
2+
CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
3+
if how == "literal":
4+
return plpy.quote_literal(t)
5+
elif how == "nullable":
6+
return plpy.quote_nullable(t)
7+
elif how == "ident":
8+
return plpy.quote_ident(t)
9+
else:
10+
raise plpy.Error("unrecognized quote type %s" % how)
11+
$$ LANGUAGE plpythonu;
12+
SELECT quote(t, 'literal') FROM (VALUES
13+
('abc'),
14+
('a''bc'),
15+
('''abc'''),
16+
(''),
17+
(''''),
18+
('xyzv')) AS v(t);
19+
quote
20+
-----------
21+
'abc'
22+
'a''bc'
23+
'''abc'''
24+
''
25+
''''
26+
'xyzv'
27+
(6 rows)
28+
29+
SELECT quote(t, 'nullable') FROM (VALUES
30+
('abc'),
31+
('a''bc'),
32+
('''abc'''),
33+
(''),
34+
(''''),
35+
(NULL)) AS v(t);
36+
quote
37+
-----------
38+
'abc'
39+
'a''bc'
40+
'''abc'''
41+
''
42+
''''
43+
NULL
44+
(6 rows)
45+
46+
SELECT quote(t, 'ident') FROM (VALUES
47+
('abc'),
48+
('a b c'),
49+
('a " ''abc''')) AS v(t);
50+
quote
51+
--------------
52+
abc
53+
"a b c"
54+
"a "" 'abc'"
55+
(3 rows)
56+

src/pl/plpython/expected/plpython_test.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ contents.sort()
4343
return ", ".join(contents)
4444
$$ LANGUAGE plpythonu;
4545
select module_contents();
46-
module_contents
47-
-------------------------------------------------------------------------------------------
48-
Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, warning
46+
module_contents
47+
---------------------------------------------------------------------------------------------------------------------------------------
48+
Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, warning
4949
(1 row)
5050

5151
CREATE FUNCTION elog_test() RETURNS void

src/pl/plpython/plpython.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2637,6 +2637,10 @@ static PyObject *PLy_spi_execute_query(char *query, long limit);
26372637
static PyObject *PLy_spi_execute_plan(PyObject *, PyObject *, long);
26382638
static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int);
26392639

2640+
static PyObject *PLy_quote_literal(PyObject *self, PyObject *args);
2641+
static PyObject *PLy_quote_nullable(PyObject *self, PyObject *args);
2642+
static PyObject *PLy_quote_ident(PyObject *self, PyObject *args);
2643+
26402644

26412645
static PyMethodDef PLy_plan_methods[] = {
26422646
{"status", PLy_plan_status, METH_VARARGS, NULL},
@@ -2751,6 +2755,13 @@ static PyMethodDef PLy_methods[] = {
27512755
*/
27522756
{"execute", PLy_spi_execute, METH_VARARGS, NULL},
27532757

2758+
/*
2759+
* escaping strings
2760+
*/
2761+
{"quote_literal", PLy_quote_literal, METH_VARARGS, NULL},
2762+
{"quote_nullable", PLy_quote_nullable, METH_VARARGS, NULL},
2763+
{"quote_ident", PLy_quote_ident, METH_VARARGS, NULL},
2764+
27542765
{NULL, NULL, 0, NULL}
27552766
};
27562767

@@ -3688,6 +3699,60 @@ PLy_output(volatile int level, PyObject *self, PyObject *args)
36883699
}
36893700

36903701

3702+
static PyObject *
3703+
PLy_quote_literal(PyObject *self, PyObject *args)
3704+
{
3705+
const char *str;
3706+
char *quoted;
3707+
PyObject *ret;
3708+
3709+
if (!PyArg_ParseTuple(args, "s", &str))
3710+
return NULL;
3711+
3712+
quoted = quote_literal_cstr(str);
3713+
ret = PyString_FromString(quoted);
3714+
pfree(quoted);
3715+
3716+
return ret;
3717+
}
3718+
3719+
static PyObject *
3720+
PLy_quote_nullable(PyObject *self, PyObject *args)
3721+
{
3722+
const char *str;
3723+
char *quoted;
3724+
PyObject *ret;
3725+
3726+
if (!PyArg_ParseTuple(args, "z", &str))
3727+
return NULL;
3728+
3729+
if (str == NULL)
3730+
return PyString_FromString("NULL");
3731+
3732+
quoted = quote_literal_cstr(str);
3733+
ret = PyString_FromString(quoted);
3734+
pfree(quoted);
3735+
3736+
return ret;
3737+
}
3738+
3739+
static PyObject *
3740+
PLy_quote_ident(PyObject *self, PyObject *args)
3741+
{
3742+
const char *str;
3743+
const char *quoted;
3744+
PyObject *ret;
3745+
3746+
if (!PyArg_ParseTuple(args, "s", &str))
3747+
return NULL;
3748+
3749+
quoted = quote_identifier(str);
3750+
ret = PyString_FromString(quoted);
3751+
3752+
return ret;
3753+
}
3754+
3755+
36913756
/*
36923757
* Get the name of the last procedure called by the backend (the
36933758
* innermost, if a plpython procedure call calls the backend and the
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-- test quoting functions
2+
3+
CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
4+
if how == "literal":
5+
return plpy.quote_literal(t)
6+
elif how == "nullable":
7+
return plpy.quote_nullable(t)
8+
elif how == "ident":
9+
return plpy.quote_ident(t)
10+
else:
11+
raise plpy.Error("unrecognized quote type %s" % how)
12+
$$ LANGUAGE plpythonu;
13+
14+
SELECT quote(t, 'literal') FROM (VALUES
15+
('abc'),
16+
('a''bc'),
17+
('''abc'''),
18+
(''),
19+
(''''),
20+
('xyzv')) AS v(t);
21+
22+
SELECT quote(t, 'nullable') FROM (VALUES
23+
('abc'),
24+
('a''bc'),
25+
('''abc'''),
26+
(''),
27+
(''''),
28+
(NULL)) AS v(t);
29+
30+
SELECT quote(t, 'ident') FROM (VALUES
31+
('abc'),
32+
('a b c'),
33+
('a " ''abc''')) AS v(t);

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