Avoid encoding strdup in qstring adapter

Dropped encoding parameter in the constructor: it is used
nowhere and not documented. Use directly the connection
encoding if available, else the previous latin1 fallback.
This commit is contained in:
Daniele Varrazzo 2013-04-05 00:00:42 +01:00
parent 7a5a226b49
commit 736a78f3f6
3 changed files with 70 additions and 53 deletions

View File

@ -32,26 +32,32 @@
#include <string.h> #include <string.h>
static const char *default_encoding = "latin1";
/* qstring_quote - do the quote process on plain and unicode strings */ /* qstring_quote - do the quote process on plain and unicode strings */
BORROWED static PyObject * static PyObject *
qstring_quote(qstringObject *self) qstring_quote(qstringObject *self)
{ {
PyObject *str; PyObject *str = NULL;
char *s, *buffer; char *s, *buffer = NULL;
Py_ssize_t len, qlen; Py_ssize_t len, qlen;
const char *encoding = default_encoding;
PyObject *rv = NULL;
/* if the wrapped object is an unicode object we can encode it to match /* if the wrapped object is an unicode object we can encode it to match
self->encoding but if the encoding is not specified we don't know what conn->encoding but if the encoding is not specified we don't know what
to do and we raise an exception */ to do and we raise an exception */
if (self->conn) {
encoding = self->conn->codec;
}
Dprintf("qstring_quote: encoding to %s", self->encoding); Dprintf("qstring_quote: encoding to %s", encoding);
if (PyUnicode_Check(self->wrapped) && self->encoding) { if (PyUnicode_Check(self->wrapped) && encoding) {
str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL); str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
Dprintf("qstring_quote: got encoded object at %p", str); Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL; if (str == NULL) goto exit;
} }
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
@ -68,29 +74,28 @@ qstring_quote(qstringObject *self)
else { else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"can't quote non-string object (or missing encoding)"); "can't quote non-string object (or missing encoding)");
return NULL; goto exit;
} }
/* encode the string into buffer */ /* encode the string into buffer */
Bytes_AsStringAndSize(str, &s, &len); Bytes_AsStringAndSize(str, &s, &len);
if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) { if (!(buffer = psycopg_escape_string((PyObject *)self->conn, s, len, NULL, &qlen))) {
Py_DECREF(str); goto exit;
return NULL;
} }
if (qlen > (size_t) PY_SSIZE_T_MAX) { if (qlen > (size_t) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"PG buffer too large to fit in Python buffer."); "PG buffer too large to fit in Python buffer.");
PyMem_Free(buffer); goto exit;
Py_DECREF(str);
return NULL;
} }
self->buffer = Bytes_FromStringAndSize(buffer, qlen); rv = Bytes_FromStringAndSize(buffer, qlen);
PyMem_Free(buffer);
Py_DECREF(str);
return self->buffer; exit:
PyMem_Free(buffer);
Py_XDECREF(str);
return rv;
} }
/* qstring_str, qstring_getquoted - return result of quoting */ /* qstring_str, qstring_getquoted - return result of quoting */
@ -99,7 +104,7 @@ static PyObject *
qstring_getquoted(qstringObject *self, PyObject *args) qstring_getquoted(qstringObject *self, PyObject *args)
{ {
if (self->buffer == NULL) { if (self->buffer == NULL) {
qstring_quote(self); self->buffer = qstring_quote(self);
} }
Py_XINCREF(self->buffer); Py_XINCREF(self->buffer);
return self->buffer; return self->buffer;
@ -114,19 +119,11 @@ qstring_str(qstringObject *self)
static PyObject * static PyObject *
qstring_prepare(qstringObject *self, PyObject *args) qstring_prepare(qstringObject *self, PyObject *args)
{ {
PyObject *conn; connectionObject *conn;
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; return NULL;
/* we bother copying the encoding only if the wrapped string is unicode,
we don't need the encoding if that's not the case */
if (PyUnicode_Check(self->wrapped)) {
if (self->encoding) free(self->encoding);
self->encoding = strdup(((connectionObject *)conn)->codec);
Dprintf("qstring_prepare: set encoding to %s", self->encoding);
}
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
Py_INCREF(conn); Py_INCREF(conn);
self->conn = conn; self->conn = conn;
@ -151,6 +148,18 @@ qstring_conform(qstringObject *self, PyObject *args)
return res; return res;
} }
static PyObject *
qstring_get_encoding(qstringObject *self)
{
const char *encoding = default_encoding;
if (self->conn) {
encoding = self->conn->codec;
}
return Text_FromUTF8(encoding);
}
/** the QuotedString object **/ /** the QuotedString object **/
/* object member list */ /* object member list */
@ -158,7 +167,6 @@ qstring_conform(qstringObject *self, PyObject *args)
static struct PyMemberDef qstringObject_members[] = { static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY}, {"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY}, {"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY},
{"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY},
{NULL} {NULL}
}; };
@ -173,22 +181,24 @@ static PyMethodDef qstringObject_methods[] = {
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyGetSetDef qstringObject_getsets[] = {
{ "encoding",
(getter)qstring_get_encoding,
(setter)NULL,
"current encoding of the adapter" },
{NULL}
};
/* initialization and finalization methods */ /* initialization and finalization methods */
static int static int
qstring_setup(qstringObject *self, PyObject *str, const char *enc) qstring_setup(qstringObject *self, PyObject *str)
{ {
Dprintf("qstring_setup: init qstring object at %p, refcnt = " Dprintf("qstring_setup: init qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self) self, Py_REFCNT(self)
); );
self->buffer = NULL;
self->conn = NULL;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
Py_INCREF(str); Py_INCREF(str);
self->wrapped = str; self->wrapped = str;
@ -219,8 +229,6 @@ qstring_dealloc(PyObject* obj)
Py_CLEAR(self->buffer); Py_CLEAR(self->buffer);
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
if (self->encoding) free(self->encoding);
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = " Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj) obj, Py_REFCNT(obj)
@ -233,12 +241,11 @@ static int
qstring_init(PyObject *obj, PyObject *args, PyObject *kwds) qstring_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
PyObject *str; PyObject *str;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc)) if (!PyArg_ParseTuple(args, "O", &str))
return -1; return -1;
return qstring_setup((qstringObject *)obj, str, enc); return qstring_setup((qstringObject *)obj, str);
} }
static PyObject * static PyObject *
@ -257,7 +264,7 @@ qstring_repr(qstringObject *self)
/* object type */ /* object type */
#define qstringType_doc \ #define qstringType_doc \
"QuotedString(str, enc) -> new quoted object with 'enc' encoding" "QuotedString(str) -> new quoted object"
PyTypeObject qstringType = { PyTypeObject qstringType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
@ -288,7 +295,7 @@ PyTypeObject qstringType = {
0, /*tp_iternext*/ 0, /*tp_iternext*/
qstringObject_methods, /*tp_methods*/ qstringObject_methods, /*tp_methods*/
qstringObject_members, /*tp_members*/ qstringObject_members, /*tp_members*/
0, /*tp_getset*/ qstringObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
@ -306,10 +313,9 @@ PyObject *
psyco_QuotedString(PyObject *module, PyObject *args) psyco_QuotedString(PyObject *module, PyObject *args)
{ {
PyObject *str; PyObject *str;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc)) if (!PyArg_ParseTuple(args, "O", &str))
return NULL; return NULL;
return PyObject_CallFunction((PyObject *)&qstringType, "Os", str, enc); return PyObject_CallFunctionObjArgs((PyObject *)&qstringType, str, NULL);
} }

View File

@ -37,13 +37,8 @@ typedef struct {
PyObject *wrapped; PyObject *wrapped;
PyObject *buffer; PyObject *buffer;
/* NOTE: this used to be a PostgreSQL encoding: changed in 2.3.2 to be a
* Python codec name. I don't expect there has been any user for this
* object other than adapting str/unicode, so I don't expect client code
* broken for this reason. */
char *encoding;
PyObject *conn; connectionObject *conn;
} qstringObject; } qstringObject;
/* functions exported to psycopgmodule.c */ /* functions exported to psycopgmodule.c */

View File

@ -162,6 +162,22 @@ class QuotingTestCase(unittest.TestCase):
self.assert_(not self.conn.notices) self.assert_(not self.conn.notices)
class TestQuotedString(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
def test_encoding(self):
q = psycopg2.extensions.QuotedString('hi')
self.assertEqual(q.encoding, 'latin1')
self.conn.set_client_encoding('utf_8')
q.prepare(self.conn)
self.assertEqual(q.encoding, 'utf_8')
def test_suite(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)