diff --git a/NEWS b/NEWS index f31a1f9a..01cee926 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ What's new in psycopg 2.9.11 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Add support for Python 3.14. +- Avoid a segfault passing more arguments than placeholders if Python is built + with assertions enabled (:ticket:`#1791`). - `~psycopg2.errorcodes` map and `~psycopg2.errors` classes updated to PostgreSQL 18. - Drop support for Python 3.8. diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index 0584b390..37001a8b 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -1047,7 +1047,7 @@ static cursorObject * _conn_get_async_cursor(connectionObject *self) { PyObject *py_curs; - if (!(py_curs = PyWeakref_GetObject(self->async_cursor))) { + if (!(py_curs = psyco_weakref_get_object(self->async_cursor))) { PyErr_SetString(PyExc_SystemError, "got null dereferencing cursor weakref"); goto error; diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c index efdeefcc..e17e7172 100644 --- a/psycopg/cursor_type.c +++ b/psycopg/cursor_type.c @@ -342,7 +342,7 @@ _psyco_curs_merge_query_args(cursorObject *self, if (PyObject_HasAttrString(arg, "args")) { PyObject *args = PyObject_GetAttrString(arg, "args"); PyObject *str = PySequence_GetItem(args, 0); - const char *s = Bytes_AS_STRING(str); + const char *s = PyUnicode_AsUTF8(str); Dprintf("curs_execute: -> %s", s); @@ -779,7 +779,7 @@ curs_fetchone(cursorObject *self, PyObject *dummy) successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor - && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) + && psyco_weakref_get_object(self->conn->async_cursor) == (PyObject*)self) CLEARPGRES(self->pgres); return res; @@ -826,7 +826,7 @@ curs_next_named(cursorObject *self) successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor - && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) + && psyco_weakref_get_object(self->conn->async_cursor) == (PyObject*)self) CLEARPGRES(self->pgres); return res; @@ -911,7 +911,7 @@ curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor - && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) + && psyco_weakref_get_object(self->conn->async_cursor) == (PyObject*)self) CLEARPGRES(self->pgres); /* success */ @@ -980,7 +980,7 @@ curs_fetchall(cursorObject *self, PyObject *dummy) successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor - && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) + && psyco_weakref_get_object(self->conn->async_cursor) == (PyObject*)self) CLEARPGRES(self->pgres); /* success */ diff --git a/psycopg/utils.c b/psycopg/utils.c index 93f0636c..46070133 100644 --- a/psycopg/utils.c +++ b/psycopg/utils.c @@ -441,3 +441,36 @@ psyco_get_decimal_type(void) return decimalType; } + +/* Return the object referred by the weak ref as a borrowed pointer. + * + * Reproduce the semantics of PyWeakref_GetObject(), which was deprecated in + * 3.13. + * + * I know that it would have been better to reproduce the semantics of + * PyWeakref_GetRef(), thank you for the suggestion. However this opens a can + * of worms in cursor_type.c. Why so? Glad you ask: because there are many + * places in that function where we don't check the return value. That stuff is + * convoluted and async: I think in case of failure it would fail of internal + * error, but it's not been reported doing so. + */ +BORROWED PyObject * +psyco_weakref_get_object(PyObject *ref) +{ +#if PY_VERSION_HEX >= 0x030d0000 + PyObject *obj = NULL; + int rv; + + if ((rv = PyWeakref_GetRef(ref, &obj)) > 0) { + Py_DECREF(obj); /* make it weak */ + } + else if (rv == 0) { /* dead ref */ + obj = Py_None; + } + /* else it's an error */ + + return obj; +#else + return PyWeakref_GetObject(ref); +#endif +} diff --git a/psycopg/utils.h b/psycopg/utils.h index 5223d3a5..5850291e 100644 --- a/psycopg/utils.h +++ b/psycopg/utils.h @@ -59,6 +59,8 @@ HIDDEN RAISES BORROWED PyObject *psyco_set_error( HIDDEN PyObject *psyco_get_decimal_type(void); +HIDDEN BORROWED PyObject *psyco_weakref_get_object(PyObject *); + HIDDEN PyObject *Bytes_Format(PyObject *format, PyObject *args); diff --git a/tests/test_cursor.py b/tests/test_cursor.py index 5de989ed..deaa99e1 100755 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -139,6 +139,11 @@ class CursorTests(ConnectingTestCase): self.assertRaises(psycopg2.ProgrammingError, cur.mogrify, "select %(foo, %(bar)", {'foo': 1, 'bar': 2}) + def test_bad_params_number(self): + cur = self.conn.cursor() + self.assertRaises(IndexError, cur.execute, "select %s, %s", [1]) + self.assertRaises(TypeError, cur.execute, "select %s", [1, 2]) + def test_cast(self): curs = self.conn.cursor()