bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set. (GH-18942)

This commit is contained in:
Serhiy Storchaka 2020-03-21 15:53:28 +02:00 committed by GitHub
parent 684d2b9a07
commit b146568dfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 14 deletions

View File

@ -165,9 +165,10 @@ Module functions and constants
that 'mytype' is the type of the column. It will try to find an entry of that 'mytype' is the type of the column. It will try to find an entry of
'mytype' in the converters dictionary and then use the converter function found 'mytype' in the converters dictionary and then use the converter function found
there to return the value. The column name found in :attr:`Cursor.description` there to return the value. The column name found in :attr:`Cursor.description`
is only the first word of the column name, i. e. if you use something like does not include the type, i. e. if you use something like
``'as "x [datetime]"'`` in your SQL, then we will parse out everything until the ``'as "Expiration date [datetime]"'`` in your SQL, then we will parse out
first blank for the column name: the column name would simply be "x". everything until the first ``'['`` for the column name and strip
the preceeding space: the column name would simply be "Expiration date".
.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri]) .. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])

View File

@ -68,7 +68,7 @@ class RegressionTests(unittest.TestCase):
def CheckColumnNameWithSpaces(self): def CheckColumnNameWithSpaces(self):
cur = self.con.cursor() cur = self.con.cursor()
cur.execute('select 1 as "foo bar [datetime]"') cur.execute('select 1 as "foo bar [datetime]"')
self.assertEqual(cur.description[0][0], "foo bar") self.assertEqual(cur.description[0][0], "foo bar [datetime]")
cur.execute('select 1 as "foo baz"') cur.execute('select 1 as "foo baz"')
self.assertEqual(cur.description[0][0], "foo baz") self.assertEqual(cur.description[0][0], "foo baz")

View File

@ -275,13 +275,13 @@ class ColNamesTests(unittest.TestCase):
def CheckColName(self): def CheckColName(self):
self.cur.execute("insert into test(x) values (?)", ("xxx",)) self.cur.execute("insert into test(x) values (?)", ("xxx",))
self.cur.execute('select x as "x [bar]" from test') self.cur.execute('select x as "x y [bar]" from test')
val = self.cur.fetchone()[0] val = self.cur.fetchone()[0]
self.assertEqual(val, "<xxx>") self.assertEqual(val, "<xxx>")
# Check if the stripping of colnames works. Everything after the first # Check if the stripping of colnames works. Everything after the first
# whitespace should be stripped. # '[' (and the preceeding space) should be stripped.
self.assertEqual(self.cur.description[0][0], "x") self.assertEqual(self.cur.description[0][0], "x y")
def CheckCaseInConverterName(self): def CheckCaseInConverterName(self):
self.cur.execute("select 'other' as \"x [b1b1]\"") self.cur.execute("select 'other' as \"x [b1b1]\"")

View File

@ -0,0 +1,2 @@
The column name found in ``sqlite3.Cursor.description`` is now truncated on
the first '[' only if the PARSE_COLNAMES option is set.

View File

@ -193,22 +193,30 @@ pysqlite_build_row_cast_map(pysqlite_Cursor* self)
} }
static PyObject * static PyObject *
_pysqlite_build_column_name(const char* colname) _pysqlite_build_column_name(pysqlite_Cursor *self, const char *colname)
{ {
const char* pos; const char* pos;
Py_ssize_t len;
if (!colname) { if (!colname) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
for (pos = colname;; pos++) { if (self->connection->detect_types & PARSE_COLNAMES) {
if (*pos == 0 || *pos == '[') { for (pos = colname; *pos; pos++) {
if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) { if (*pos == '[') {
if ((pos != colname) && (*(pos-1) == ' ')) {
pos--; pos--;
} }
return PyUnicode_FromStringAndSize(colname, pos - colname); break;
} }
} }
len = pos - colname;
}
else {
len = strlen(colname);
}
return PyUnicode_FromStringAndSize(colname, len);
} }
/* /*
@ -370,6 +378,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
PyObject* result; PyObject* result;
int numcols; int numcols;
PyObject* descriptor; PyObject* descriptor;
PyObject* column_name;
PyObject* second_argument = NULL; PyObject* second_argument = NULL;
sqlite_int64 lastrowid; sqlite_int64 lastrowid;
@ -536,7 +545,13 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
if (!descriptor) { if (!descriptor) {
goto error; goto error;
} }
PyTuple_SetItem(descriptor, 0, _pysqlite_build_column_name(sqlite3_column_name(self->statement->st, i))); column_name = _pysqlite_build_column_name(self,
sqlite3_column_name(self->statement->st, i));
if (!column_name) {
Py_DECREF(descriptor);
goto error;
}
PyTuple_SetItem(descriptor, 0, column_name);
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None); Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None); Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None); Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);