Add psycopg2.extensions.quote_ident.

This commit is contained in:
Oleksandr Shulgin 2015-10-13 17:29:55 +02:00
parent c73c1c5771
commit 9295bce154
5 changed files with 61 additions and 3 deletions

View File

@ -221,6 +221,19 @@ functionalities defined by the |DBAPI|_.
.. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION .. __: 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-objects:
SQL adaptation protocol objects SQL adaptation protocol objects

View File

@ -56,7 +56,7 @@ try:
except ImportError: except ImportError:
pass 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 string_types, binary_types, new_type, new_array_type, register_type
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column

View File

@ -165,6 +165,42 @@ exit:
return res; 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 **/ /** type registration **/
#define psyco_register_type_doc \ #define psyco_register_type_doc \
"register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \ "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}, METH_VARARGS|METH_KEYWORDS, psyco_parse_dsn_doc},
{"adapt", (PyCFunction)psyco_microprotocols_adapt, {"adapt", (PyCFunction)psyco_microprotocols_adapt,
METH_VARARGS, psyco_microprotocols_adapt_doc}, METH_VARARGS, psyco_microprotocols_adapt_doc},
{"quote_ident", (PyCFunction)psyco_quote_ident,
METH_VARARGS, psyco_quote_ident_doc},
{"register_type", (PyCFunction)psyco_register_type, {"register_type", (PyCFunction)psyco_register_type,
METH_VARARGS, psyco_register_type_doc}, METH_VARARGS, psyco_register_type_doc},

View File

@ -87,7 +87,7 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
return to; 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. * Allocate a new buffer on the Python heap containing the new string.
* 'len' is optional: if 0 the length is calculated. * '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 * 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 * 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 * char *
psycopg_escape_identifier_easy(const char *from, Py_ssize_t len) psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)

View File

@ -165,6 +165,13 @@ class TestQuotedString(ConnectingTestCase):
self.assertEqual(q.encoding, 'utf_8') 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(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)