Fixed reference leak with arguments referenced more than once in queries

Plus, some more care in objects life cycle, mostly in exceptions handling.

Closes ticket #81.
This commit is contained in:
Daniele Varrazzo 2011-12-11 02:52:06 +00:00
parent cdb19a2329
commit ad3a198919
3 changed files with 28 additions and 10 deletions

2
NEWS
View File

@ -18,6 +18,8 @@ What's new in psycopg 2.4.3
- Fixed interaction between RealDictCursor and named cursors - Fixed interaction between RealDictCursor and named cursors
(ticket #67). (ticket #67).
- Dropped limit on the columns length in COPY operations (ticket #68). - Dropped limit on the columns length in COPY operations (ticket #68).
- Fixed reference leak with arguments referenced more than once
in queries (ticket #81).
- Fixed typecasting of arrays containing consecutive backslashes. - Fixed typecasting of arrays containing consecutive backslashes.
- 'errorcodes' map updated to PostgreSQL 9.1. - 'errorcodes' map updated to PostgreSQL 9.1.

View File

@ -123,23 +123,29 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
for (d = c + 1; *d && *d != ')' && *d != '%'; d++); for (d = c + 1; *d && *d != ')' && *d != '%'; d++);
if (*d == ')') { if (*d == ')') {
key = Text_FromUTF8AndSize(c+1, (Py_ssize_t) (d-c-1)); if (!(key = Text_FromUTF8AndSize(c+1, (Py_ssize_t)(d-c-1)))) {
value = PyObject_GetItem(var, key); Py_XDECREF(n);
/* key has refcnt 1, value the original value + 1 */ return -1;
}
/* if value is NULL we did not find the key (or this is not a /* if value is NULL we did not find the key (or this is not a
dictionary): let python raise a KeyError */ dictionary): let python raise a KeyError */
if (value == NULL) { if (!(value = PyObject_GetItem(var, key))) {
Py_DECREF(key); /* destroy key */ Py_DECREF(key); /* destroy key */
Py_XDECREF(n); /* destroy n */ Py_XDECREF(n); /* destroy n */
return -1; return -1;
} }
/* key has refcnt 1, value the original value + 1 */
Dprintf("_mogrify: value refcnt: " Dprintf("_mogrify: value refcnt: "
FORMAT_CODE_PY_SSIZE_T " (+1)", Py_REFCNT(value)); FORMAT_CODE_PY_SSIZE_T " (+1)", Py_REFCNT(value));
if (n == NULL) { if (n == NULL) {
n = PyDict_New(); if (!(n = PyDict_New())) {
Py_DECREF(key);
Py_DECREF(value);
return -1;
}
} }
if (0 == PyDict_Contains(n, key)) { if (0 == PyDict_Contains(n, key)) {
@ -156,24 +162,22 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
} }
else { else {
t = microprotocol_getquoted(value, curs->conn); t = microprotocol_getquoted(value, curs->conn);
if (t != NULL) { if (t != NULL) {
PyDict_SetItem(n, key, t); PyDict_SetItem(n, key, t);
/* both key and t refcnt +1, key is at 2 now */ /* both key and t refcnt +1, key is at 2 now */
} }
else { else {
/* no adapter found, raise a BIG exception */ /* no adapter found, raise a BIG exception */
Py_XDECREF(value); Py_DECREF(key);
Py_DECREF(value);
Py_DECREF(n); Py_DECREF(n);
return -1; return -1;
} }
} }
Py_XDECREF(t); /* t dies here */ Py_XDECREF(t); /* t dies here */
/* after the DECREF value has the original refcnt plus 1
if it was added to the dictionary directly; good */
Py_XDECREF(value);
} }
Py_DECREF(value);
Py_DECREF(key); /* key has the original refcnt now */ Py_DECREF(key); /* key has the original refcnt now */
Dprintf("_mogrify: after value refcnt: " Dprintf("_mogrify: after value refcnt: "
FORMAT_CODE_PY_SSIZE_T, Py_REFCNT(value)); FORMAT_CODE_PY_SSIZE_T, Py_REFCNT(value));

View File

@ -97,6 +97,18 @@ class CursorTests(unittest.TestCase):
self.assertEqual(b('SELECT 10.3;'), self.assertEqual(b('SELECT 10.3;'),
cur.mogrify("SELECT %s;", (Decimal("10.3"),))) cur.mogrify("SELECT %s;", (Decimal("10.3"),)))
def test_mogrify_leak_on_multiple_reference(self):
# issue #81: reference leak when a parameter value is referenced
# more than once from a dict.
cur = self.conn.cursor()
i = lambda x: x
foo = i('foo') * 10
import sys
nref1 = sys.getrefcount(foo)
cur.mogrify("select %(foo)s, %(foo)s, %(foo)s", {'foo': foo})
nref2 = sys.getrefcount(foo)
self.assertEqual(nref1, nref2)
def test_bad_placeholder(self): def test_bad_placeholder(self):
cur = self.conn.cursor() cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError, self.assertRaises(psycopg2.ProgrammingError,