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

(cherry picked from commit b146568dfc)
This commit is contained in:
Serhiy Storchaka 2020-03-21 16:33:44 +02:00 committed by GitHub
parent 6056b7b84f
commit 39680fb704
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 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
'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`
is only the first word of the column name, i. e. if you use something like
``'as "x [datetime]"'`` in your SQL, then we will parse out everything until the
first blank for the column name: the column name would simply be "x".
does not include the type, i. e. if you use something like
``'as "Expiration date [datetime]"'`` in your SQL, then we will parse out
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])

View File

@ -67,7 +67,7 @@ class RegressionTests(unittest.TestCase):
def CheckColumnNameWithSpaces(self):
cur = self.con.cursor()
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"')
self.assertEqual(cur.description[0][0], "foo baz")

View File

@ -253,13 +253,13 @@ class ColNamesTests(unittest.TestCase):
def CheckColName(self):
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]
self.assertEqual(val, "<xxx>")
# Check if the stripping of colnames works. Everything after the first
# whitespace should be stripped.
self.assertEqual(self.cur.description[0][0], "x")
# '[' (and the preceeding space) should be stripped.
self.assertEqual(self.cur.description[0][0], "x y")
def CheckCaseInConverterName(self):
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

@ -198,22 +198,31 @@ int pysqlite_build_row_cast_map(pysqlite_Cursor* self)
return 0;
}
PyObject* _pysqlite_build_column_name(const char* colname)
static PyObject *
_pysqlite_build_column_name(pysqlite_Cursor *self, const char *colname)
{
const char* pos;
Py_ssize_t len;
if (!colname) {
Py_RETURN_NONE;
}
for (pos = colname;; pos++) {
if (*pos == 0 || *pos == '[') {
if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) {
pos--;
if (self->connection->detect_types & PARSE_COLNAMES) {
for (pos = colname; *pos; pos++) {
if (*pos == '[') {
if ((pos != colname) && (*(pos-1) == ' ')) {
pos--;
}
break;
}
return PyUnicode_FromStringAndSize(colname, pos - colname);
}
len = pos - colname;
}
else {
len = strlen(colname);
}
return PyUnicode_FromStringAndSize(colname, len);
}
/*
@ -389,6 +398,7 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
PyObject* result;
int numcols;
PyObject* descriptor;
PyObject* column_name;
PyObject* second_argument = NULL;
sqlite_int64 lastrowid;
@ -569,7 +579,13 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
if (!descriptor) {
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, 2, Py_None);
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);