summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-11 03:36:36 +0100
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-11 03:37:09 +0100
commitf99a8de6d04e109766ec4fc2ad276ff763e86cb1 (patch)
treeb78e38a6038b4fc18749337c08ceb1dca3a2d8da
parentb3b225a9da450ed11ffbe8a61ba28ca6b343af32 (diff)
downloadpsycopg2-description-extra-attrs.tar.gz
Added table_oid, table_column on cursor.description itemsdescription-extra-attrs
Close #661
-rw-r--r--psycopg/column.h4
-rw-r--r--psycopg/column_type.c30
-rw-r--r--psycopg/pqpath.c15
-rwxr-xr-xtests/test_cursor.py23
4 files changed, 66 insertions, 6 deletions
diff --git a/psycopg/column.h b/psycopg/column.h
index b551345..59e9d9c 100644
--- a/psycopg/column.h
+++ b/psycopg/column.h
@@ -39,6 +39,10 @@ typedef struct {
PyObject *scale;
PyObject *null_ok;
+ /* Extensions to the DBAPI */
+ PyObject *table_oid;
+ PyObject *table_column;
+
} columnObject;
#endif /* PSYCOPG_COLUMN_H */
diff --git a/psycopg/column_type.c b/psycopg/column_type.c
index f8476a4..8ee7d4c 100644
--- a/psycopg/column_type.c
+++ b/psycopg/column_type.c
@@ -62,7 +62,15 @@ static const char scale_doc[] =
"None for other types.";
static const char null_ok_doc[] =
- "Always none.\n\n";
+ "Always none.";
+
+static const char table_oid_doc[] =
+ "The OID of the table from which the column was fetched.\n\n"
+ "None if not available";
+
+static const char table_column_doc[] =
+ "The number (within its table) of the column making up the result\n\n"
+ "None if not available. Note that PostgreSQL column numbers start at 1";
static PyMemberDef column_members[] = {
@@ -73,6 +81,8 @@ static PyMemberDef column_members[] = {
{ "precision", T_OBJECT, offsetof(columnObject, precision), READONLY, (char *)precision_doc },
{ "scale", T_OBJECT, offsetof(columnObject, scale), READONLY, (char *)scale_doc },
{ "null_ok", T_OBJECT, offsetof(columnObject, null_ok), READONLY, (char *)null_ok_doc },
+ { "table_oid", T_OBJECT, offsetof(columnObject, table_oid), READONLY, (char *)table_oid_doc },
+ { "table_column", T_OBJECT, offsetof(columnObject, table_column), READONLY, (char *)table_column_doc },
{ NULL }
};
@@ -89,12 +99,12 @@ column_init(columnObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {
"name", "type_code", "display_size", "internal_size",
- "precision", "scale", "null_ok", NULL};
+ "precision", "scale", "null_ok", "table_oid", "table_column", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOO", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOOOO", kwlist,
&self->name, &self->type_code, &self->display_size,
&self->internal_size, &self->precision, &self->scale,
- &self->null_ok)) {
+ &self->null_ok, &self->table_oid, &self->table_column)) {
return -1;
}
@@ -112,6 +122,8 @@ column_dealloc(columnObject *self)
Py_CLEAR(self->precision);
Py_CLEAR(self->scale);
Py_CLEAR(self->null_ok);
+ Py_CLEAR(self->table_oid);
+ Py_CLEAR(self->table_column);
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -294,6 +306,16 @@ column_setstate(columnObject *self, PyObject *state)
self->null_ok = PyTuple_GET_ITEM(state, 6);
Py_INCREF(self->null_ok);
}
+ if (size > 7) {
+ Py_CLEAR(self->table_oid);
+ self->table_oid = PyTuple_GET_ITEM(state, 7);
+ Py_INCREF(self->table_oid);
+ }
+ if (size > 8) {
+ Py_CLEAR(self->table_column);
+ self->table_column = PyTuple_GET_ITEM(state, 8);
+ Py_INCREF(self->table_column);
+ }
exit:
rv = Py_None;
diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c
index e318802..2893739 100644
--- a/psycopg/pqpath.c
+++ b/psycopg/pqpath.c
@@ -1207,6 +1207,8 @@ _pq_fetch_tuples(cursorObject *curs)
Oid ftype = PQftype(curs->pgres, i);
int fsize = PQfsize(curs->pgres, i);
int fmod = PQfmod(curs->pgres, i);
+ Oid ftable = PQftable(curs->pgres, i);
+ int ftablecol = PQftablecol(curs->pgres, i);
columnObject *column = NULL;
PyObject *type = NULL;
@@ -1299,7 +1301,18 @@ _pq_fetch_tuples(cursorObject *curs)
column->scale = tmp;
}
- /* 6/ FIXME: null_ok??? */
+ /* table_oid, table_column */
+ if (ftable != InvalidOid) {
+ PyObject *tmp;
+ if (!(tmp = PyInt_FromLong((long)ftable))) { goto err_for; }
+ column->table_oid = tmp;
+ }
+
+ if (ftablecol > 0) {
+ PyObject *tmp;
+ if (!(tmp = PyInt_FromLong((long)ftablecol))) { goto err_for; }
+ column->table_column = tmp;
+ }
PyTuple_SET_ITEM(description, i, (PyObject *)column);
column = NULL;
diff --git a/tests/test_cursor.py b/tests/test_cursor.py
index 37110db..d048f3e 100755
--- a/tests/test_cursor.py
+++ b/tests/test_cursor.py
@@ -377,7 +377,7 @@ class CursorTests(ConnectingTestCase):
for i, rec in enumerate(curs):
self.assertEqual(i + 1, curs.rownumber)
- def test_namedtuple_description(self):
+ def test_description_attribs(self):
curs = self.conn.cursor()
curs.execute("""select
3.14::decimal(10,2) as pi,
@@ -412,6 +412,27 @@ class CursorTests(ConnectingTestCase):
self.assertEqual(c.precision, None)
self.assertEqual(c.scale, None)
+ def test_description_extra_attribs(self):
+ curs = self.conn.cursor()
+ curs.execute("""
+ create table testcol (
+ pi decimal(10,2),
+ hi text)
+ """)
+ curs.execute("select oid from pg_class where relname = %s", ('testcol',))
+ oid = curs.fetchone()[0]
+
+ curs.execute("insert into testcol values (3.14, 'hello')")
+ curs.execute("select hi, pi, 42 from testcol")
+ self.assertEqual(curs.description[0].table_oid, oid)
+ self.assertEqual(curs.description[0].table_column, 2)
+
+ self.assertEqual(curs.description[1].table_oid, oid)
+ self.assertEqual(curs.description[1].table_column, 1)
+
+ self.assertEqual(curs.description[2].table_oid, None)
+ self.assertEqual(curs.description[2].table_column, None)
+
def test_pickle_description(self):
curs = self.conn.cursor()
curs.execute('SELECT 1 AS foo')