mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-26 10:53:44 +03:00
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:
parent
cdb19a2329
commit
ad3a198919
2
NEWS
2
NEWS
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user