From 2f703c07a98c70ac369e77603abd98e681204421 Mon Sep 17 00:00:00 2001 From: Jan Stastny Date: Sat, 17 Dec 2016 13:28:45 +0100 Subject: [PATCH 1/4] Expose sql_type for columns in resultset #167 --- src/cursor.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cursor.h | 2 ++ 2 files changed, 65 insertions(+) diff --git a/src/cursor.cpp b/src/cursor.cpp index 8461762c..2987e653 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -430,6 +430,12 @@ static bool free_results(Cursor* self, int flags) self->description = Py_None; Py_INCREF(Py_None); } + if(self->coldescription != Py_None) + { + Py_DECREF(self->coldescription); + self->coldescription = Py_None; + Py_INCREF(Py_None); + } if (self->map_name_to_index) { @@ -471,11 +477,13 @@ static void closeimpl(Cursor* cur) Py_XDECREF(cur->pPreparedSQL); Py_XDECREF(cur->description); + Py_XDECREF(cur->coldescription); Py_XDECREF(cur->map_name_to_index); Py_XDECREF(cur->cnxn); cur->pPreparedSQL = 0; cur->description = 0; + cur->coldescription = 0; cur->map_name_to_index = 0; cur->cnxn = 0; } @@ -626,6 +634,48 @@ static bool PrepareResults(Cursor* cur, int cCols) return true; } +static bool expose_column_sql_types(Cursor* cur, int cCols) +{ + // Called after a SELECT has been executed to perform pre-fetch work. + // + // Goes through the earlier allocated ColumnInfo structures describing the returned data. + + I(cur->coldescription == Py_None); + + PyObject *colinfo=0, *coldesc=0, *isunsigned=0; + coldesc = PyTuple_New((Py_ssize_t)cCols); + int i; + for (i = 0; i < cCols; i++) + { + ColumnInfo *ci = &cur->colinfos[i]; + switch (ci->is_unsigned) + { + case SQL_TRUE: + isunsigned = Py_True; + break; + case SQL_FALSE: + isunsigned = Py_False; + break; + default: + isunsigned = Py_None; + break; + } + colinfo = Py_BuildValue("(iiO)", + (int)ci->sql_type, + (int)ci->column_size, + isunsigned + ); + PyTuple_SET_ITEM(coldesc, i, colinfo); + colinfo=0; + } + + Py_XDECREF(cur->coldescription); + cur->coldescription = coldesc; + coldesc = 0; + + return true; +} + static PyObject* execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first) { @@ -876,6 +926,9 @@ static PyObject* execute(Cursor* cur, PyObject* pSql, PyObject* params, bool ski if (!create_name_map(cur, cCols, lowercase())) return 0; + + if (!expose_column_sql_types(cur, cCols)) + return 0; } Py_INCREF(cur); @@ -2003,6 +2056,13 @@ static char description_doc[] = "the DB API and defined the pyodbc module: Date, Time, Timestamp, Binary,\n" \ "STRING, BINARY, NUMBER, and DATETIME."; +static char coldescription_doc[] = + "This read-only attribute decsribe columns returned by SQLDescribeCol.\n" + "Returns a tuple:\n" + "0 - sql_type\n" + "1 - column_width\n" + "2 - is_unsigned"; + static char arraysize_doc[] = "This read/write attribute specifies the number of rows to fetch at a time with\n" \ "fetchmany(). It defaults to 1 meaning to fetch a single row at a time."; @@ -2018,6 +2078,7 @@ static PyMemberDef Cursor_members[] = { {"rowcount", T_INT, offsetof(Cursor, rowcount), READONLY, rowcount_doc }, {"description", T_OBJECT_EX, offsetof(Cursor, description), READONLY, description_doc }, + {"coldescription", T_OBJECT_EX, offsetof(Cursor, coldescription),READONLY, coldescription_doc }, {"arraysize", T_INT, offsetof(Cursor, arraysize), 0, arraysize_doc }, {"connection", T_OBJECT_EX, offsetof(Cursor, cnxn), READONLY, connection_doc }, { 0 } @@ -2279,6 +2340,7 @@ Cursor_New(Connection* cnxn) cur->cnxn = cnxn; cur->hstmt = SQL_NULL_HANDLE; cur->description = Py_None; + cur->coldescription = Py_None; cur->pPreparedSQL = 0; cur->paramcount = 0; cur->paramtypes = 0; @@ -2290,6 +2352,7 @@ Cursor_New(Connection* cnxn) Py_INCREF(cnxn); Py_INCREF(cur->description); + Py_INCREF(cur->coldescription); SQLRETURN ret; Py_BEGIN_ALLOW_THREADS diff --git a/src/cursor.h b/src/cursor.h index 3418d333..9da6fc74 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -114,6 +114,8 @@ struct Cursor // The description tuple described in the DB API 2.0 specification. Set to None when there are no results. PyObject* description; + PyObject* coldescription; + int arraysize; // The Cursor.rowcount attribute from the DB API specification. From 5f0f8d7af85fd24d444c504084c26cbbe26f45ac Mon Sep 17 00:00:00 2001 From: celestialorb Date: Mon, 16 Sep 2019 13:51:50 +0000 Subject: [PATCH 2/4] Adding exposure of column description on `nextset` --- src/cursor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cursor.cpp b/src/cursor.cpp index 5015ec14..fe1e7c5f 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -1883,6 +1883,9 @@ static PyObject* Cursor_nextset(PyObject* self, PyObject* args) if (!create_name_map(cur, cCols, lowercase())) return 0; + + if (!expose_column_sql_types(cur, cCols)) + return 0; } SQLLEN cRows; From 9ad9ba8b56a02295e5adf48129972145e4658fe9 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Mon, 16 Sep 2019 13:52:25 +0000 Subject: [PATCH 3/4] Removing warning of using boolean value in switch. --- src/cursor.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/cursor.cpp b/src/cursor.cpp index fe1e7c5f..31e407dd 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -570,28 +570,16 @@ static bool expose_column_sql_types(Cursor* cur, int cCols) I(cur->coldescription == Py_None); - PyObject *colinfo=0, *coldesc=0, *isunsigned=0; + PyObject *colinfo=0, *coldesc=0; coldesc = PyTuple_New((Py_ssize_t)cCols); int i; for (i = 0; i < cCols; i++) { ColumnInfo *ci = &cur->colinfos[i]; - switch (ci->is_unsigned) - { - case SQL_TRUE: - isunsigned = Py_True; - break; - case SQL_FALSE: - isunsigned = Py_False; - break; - default: - isunsigned = Py_None; - break; - } colinfo = Py_BuildValue("(iiO)", (int)ci->sql_type, (int)ci->column_size, - isunsigned + (ci->is_unsigned ? Py_True : Py_False) ); PyTuple_SET_ITEM(coldesc, i, colinfo); colinfo=0; From 2f66861873455b3696002033f7c7e28f302c7820 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Mon, 16 Sep 2019 14:19:09 +0000 Subject: [PATCH 4/4] Fixing typo. --- src/cursor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cursor.cpp b/src/cursor.cpp index 31e407dd..3f705c3a 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -2129,7 +2129,7 @@ static char description_doc[] = "STRING, BINARY, NUMBER, and DATETIME."; static char coldescription_doc[] = - "This read-only attribute decsribe columns returned by SQLDescribeCol.\n" + "This read-only attribute describe columns returned by SQLDescribeCol.\n" "Returns a tuple:\n" "0 - sql_type\n" "1 - column_width\n"