Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Mar 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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