Fixed handling large Oid values

Oid is defined as unsigned 32. On some Python implementations (probably
the ones where maxint = 2 ** 31) this can cause int overflow for large
values (see #961). On my 64 box it doesn't seem the case.

Oid handling was sloppy here and there (messages, casts...): trying to
use uint everywhere, and added a couple of helper macros to treat Oid
consistently.

Close #961.
This commit is contained in:
Daniele Varrazzo 2019-09-04 12:27:16 +01:00
parent 4d10f1235f
commit 80df0553a6
7 changed files with 25 additions and 13 deletions

6
NEWS
View File

@ -4,8 +4,10 @@ Current release
What's new in psycopg 2.8.4
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Don't swallow keyboard interrupts on connect when a password is specified
in the connection string (:ticket:`#898`).
- Don't swallow keyboard interrupts on connect when a password is specified
in the connection string (:ticket:`#898`).
- Fixed int overflow for large values in `~psycopg2.extensions.Column.table_oid`
and `~psycopg2.extensions.Column.type_code` (:ticket:`961).
What's new in psycopg 2.8.3

View File

@ -968,7 +968,7 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
Dprintf("psyco_conn_lobject: new lobject for connection at %p", self);
Dprintf("psyco_conn_lobject: parameters: oid = %u, mode = %s",
oid, smode);
Dprintf("psyco_conn_lobject: parameters: new_oid = %d, new_file = %s",
Dprintf("psyco_conn_lobject: parameters: new_oid = %u, new_file = %s",
new_oid, new_file);
if (new_file)

View File

@ -1827,7 +1827,7 @@ static struct PyMemberDef cursorObject_members[] = {
"Number of records ``iter(cur)`` must fetch per network roundtrip."},
{"description", T_OBJECT, OFFSETOF(description), READONLY,
"Cursor description as defined in DBAPI-2.0."},
{"lastrowid", T_LONG, OFFSETOF(lastoid), READONLY,
{"lastrowid", T_OID, OFFSETOF(lastoid), READONLY,
"The ``oid`` of the last row inserted by the cursor."},
/* DBAPI-2.0 extensions */
{"rownumber", T_LONG, OFFSETOF(row), READONLY,

View File

@ -176,7 +176,7 @@ lobject_open(lobjectObject *self, connectionObject *conn,
self->oid = lo_creat(self->conn->pgconn, INV_READ | INV_WRITE);
}
Dprintf("lobject_open: large object created with oid = %d",
Dprintf("lobject_open: large object created with oid = %u",
self->oid);
if (self->oid == InvalidOid) {

View File

@ -327,7 +327,7 @@ static struct PyMethodDef lobjectObject_methods[] = {
/* object member list */
static struct PyMemberDef lobjectObject_members[] = {
{"oid", T_UINT, offsetof(lobjectObject, oid), READONLY,
{"oid", T_OID, offsetof(lobjectObject, oid), READONLY,
"The backend OID associated to this lobject."},
{"mode", T_STRING, offsetof(lobjectObject, smode), READONLY,
"Open mode."},
@ -368,7 +368,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
Dprintf("lobject_setup: good lobject object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, self, Py_REFCNT(self));
Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd);
Dprintf("lobject_setup: oid = %u, fd = %d", self->oid, self->fd);
return 0;
}

View File

@ -995,9 +995,9 @@ _get_cast(cursorObject *curs, PGresult *pgres, int i)
PyObject *rv = NULL;
Oid ftype = PQftype(pgres, i);
if (!(type = PyInt_FromLong(ftype))) { goto exit; }
if (!(type = PyLong_FromOid(ftype))) { goto exit; }
Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype);
Dprintf("_pq_fetch_tuples: looking for cast %u:", ftype);
if (!(cast = curs_get_cast(curs, type))) { goto exit; }
/* else if we got binary tuples and if we got a field that
@ -1006,11 +1006,11 @@ _get_cast(cursorObject *curs, PGresult *pgres, int i)
*/
if (cast == psyco_default_binary_cast && PQbinaryTuples(pgres)) {
Dprintf("_pq_fetch_tuples: Binary cursor and "
"binary field: %i using default cast", ftype);
"binary field: %u using default cast", ftype);
cast = psyco_default_cast;
}
Dprintf("_pq_fetch_tuples: using cast at %p for type %d", cast, ftype);
Dprintf("_pq_fetch_tuples: using cast at %p for type %u", cast, ftype);
/* success */
Py_INCREF(cast);
@ -1041,7 +1041,7 @@ _make_column(connectionObject *conn, PGresult *pgres, int i)
/* fill the type and name fields */
{
PyObject *tmp;
if (!(tmp = PyInt_FromLong(ftype))) {
if (!(tmp = PyLong_FromOid(ftype))) {
goto exit;
}
column->type_code = tmp;
@ -1099,7 +1099,7 @@ _make_column(connectionObject *conn, PGresult *pgres, int i)
/* table_oid, table_column */
if (ftable != InvalidOid) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftable))) { goto exit; }
if (!(tmp = PyLong_FromOid(ftable))) { goto exit; }
column->table_oid = tmp;
}

View File

@ -91,6 +91,11 @@ typedef unsigned long Py_uhash_t;
#define INIT_MODULE(m) init ## m
/* fix #961, but don't change all types to longs. Sure someone will complain. */
#define PyLong_FromOid(x) (((x) & 0x80000000) ? \
PyLong_FromUnsignedLong((unsigned long)(x)) : \
PyInt_FromLong((x)))
#endif /* PY_2 */
#if PY_3
@ -133,6 +138,11 @@ typedef unsigned long Py_uhash_t;
#define INIT_MODULE(m) PyInit_ ## m
#define PyLong_FromOid(x) (PyLong_FromUnsignedLong((unsigned long)(x)))
#endif /* PY_3 */
/* expose Oid attributes in Python C objects */
#define T_OID T_UINT
#endif /* !defined(PSYCOPG_PYTHON_H) */