summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/extensions.rst13
-rw-r--r--lib/extensions.py2
-rw-r--r--psycopg/psycopgmodule.c38
-rw-r--r--psycopg/utils.c4
-rwxr-xr-xtests/test_quote.py7
5 files changed, 61 insertions, 3 deletions
diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst
index 4db76b0..d96cca4 100644
--- a/doc/src/extensions.rst
+++ b/doc/src/extensions.rst
@@ -221,6 +221,19 @@ functionalities defined by the |DBAPI|_.
.. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
+.. function:: quote_ident(str, scope)
+
+ Return quoted identifier according to PostgreSQL quoting rules.
+
+ The *scope* must be a `connection` or a `cursor`, the underlying
+ connection encoding is used for any necessary character conversion.
+
+ Requires libpq >= 9.0.
+
+ .. seealso:: libpq docs for `PQescapeIdentifier()`__
+
+ .. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
+
.. _sql-adaptation-objects:
SQL adaptation protocol objects
diff --git a/lib/extensions.py b/lib/extensions.py
index d10e8ac..b40e28b 100644
--- a/lib/extensions.py
+++ b/lib/extensions.py
@@ -56,7 +56,7 @@ try:
except ImportError:
pass
-from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version, parse_dsn
+from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version, parse_dsn, quote_ident
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index c77dce5..9906b7b 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -165,6 +165,42 @@ exit:
return res;
}
+
+#define psyco_quote_ident_doc "quote_ident(str, conn_or_curs) -> str"
+
+static PyObject *
+psyco_quote_ident(PyObject *self, PyObject *args)
+{
+ const char *str = NULL;
+ char *quoted;
+ PyObject *obj, *result;
+ connectionObject *conn;
+
+ if (!PyArg_ParseTuple(args, "sO", &str, &obj)) return NULL;
+
+ if (PyObject_TypeCheck(obj, &cursorType)) {
+ conn = ((cursorObject*)obj)->conn;
+ }
+ else if (PyObject_TypeCheck(obj, &connectionType)) {
+ conn = (connectionObject*)obj;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "argument 2 must be a connection or a cursor");
+ return NULL;
+ }
+
+ quoted = PQescapeIdentifier(conn->pgconn, str, strlen(str));
+ if (!quoted) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ result = conn_text_from_chars(conn, quoted);
+ PQfreemem(quoted);
+
+ return result;
+}
+
/** type registration **/
#define psyco_register_type_doc \
"register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \
@@ -768,6 +804,8 @@ static PyMethodDef psycopgMethods[] = {
METH_VARARGS|METH_KEYWORDS, psyco_parse_dsn_doc},
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
METH_VARARGS, psyco_microprotocols_adapt_doc},
+ {"quote_ident", (PyCFunction)psyco_quote_ident,
+ METH_VARARGS, psyco_quote_ident_doc},
{"register_type", (PyCFunction)psyco_register_type,
METH_VARARGS, psyco_register_type_doc},
diff --git a/psycopg/utils.c b/psycopg/utils.c
index 836f612..ec8e47c 100644
--- a/psycopg/utils.c
+++ b/psycopg/utils.c
@@ -87,7 +87,7 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
return to;
}
-/* Escape a string to build a valid PostgreSQL identifier
+/* Escape a string to build a valid PostgreSQL identifier.
*
* Allocate a new buffer on the Python heap containing the new string.
* 'len' is optional: if 0 the length is calculated.
@@ -96,7 +96,7 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
*
* WARNING: this function is not so safe to allow untrusted input: it does no
* check for multibyte chars. Such a function should be built on
- * PQescapeIndentifier, which is only available from PostgreSQL 9.0.
+ * PQescapeIdentifier, which is only available from PostgreSQL 9.0.
*/
char *
psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
diff --git a/tests/test_quote.py b/tests/test_quote.py
index e7b3c31..a24ab6d 100755
--- a/tests/test_quote.py
+++ b/tests/test_quote.py
@@ -165,6 +165,13 @@ class TestQuotedString(ConnectingTestCase):
self.assertEqual(q.encoding, 'utf_8')
+class TestQuotedIdentifier(ConnectingTestCase):
+ def test_identifier(self):
+ from psycopg2.extensions import quote_ident
+ self.assertEqual(quote_ident('blah-blah', self.conn), '"blah-blah"')
+ self.assertEqual(quote_ident('quote"inside', self.conn), '"quote""inside"')
+
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)