Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[3.7] bpo-39652: Truncate the column name after '[' only if PARSE_COL…
…NAMES is set. (GH-18942). (GH-19104)

(cherry picked from commit b146568)
  • Loading branch information
serhiy-storchaka committed Mar 21, 2020
1 parent 6056b7b commit 39680fb
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 14 deletions.
7 changes: 4 additions & 3 deletions Doc/library/sqlite3.rst
Expand Up @@ -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])
Expand Down
2 changes: 1 addition & 1 deletion Lib/sqlite3/test/regression.py
Expand Up @@ -67,7 +67,7 @@ def CheckStatementReset(self):
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")
Expand Down
6 changes: 3 additions & 3 deletions Lib/sqlite3/test/types.py
Expand Up @@ -253,13 +253,13 @@ def CheckNone(self):

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]\"")
Expand Down
@@ -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.
30 changes: 23 additions & 7 deletions Modules/_sqlite/cursor.c
Expand Up @@ -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);
}

/*
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 39680fb

Please sign in to comment.