Skip to content

Commit 39680fb

Browse files
[3.7] bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set. (GH-18942). (GH-19104)
(cherry picked from commit b146568)
1 parent 6056b7b commit 39680fb

File tree

5 files changed

+33
-14
lines changed

5 files changed

+33
-14
lines changed

Doc/library/sqlite3.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,10 @@ Module functions and constants
165165
that 'mytype' is the type of the column. It will try to find an entry of
166166
'mytype' in the converters dictionary and then use the converter function found
167167
there to return the value. The column name found in :attr:`Cursor.description`
168-
is only the first word of the column name, i. e. if you use something like
169-
``'as "x [datetime]"'`` in your SQL, then we will parse out everything until the
170-
first blank for the column name: the column name would simply be "x".
168+
does not include the type, i. e. if you use something like
169+
``'as "Expiration date [datetime]"'`` in your SQL, then we will parse out
170+
everything until the first ``'['`` for the column name and strip
171+
the preceeding space: the column name would simply be "Expiration date".
171172

172173

173174
.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])

Lib/sqlite3/test/regression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def CheckStatementReset(self):
6767
def CheckColumnNameWithSpaces(self):
6868
cur = self.con.cursor()
6969
cur.execute('select 1 as "foo bar [datetime]"')
70-
self.assertEqual(cur.description[0][0], "foo bar")
70+
self.assertEqual(cur.description[0][0], "foo bar [datetime]")
7171

7272
cur.execute('select 1 as "foo baz"')
7373
self.assertEqual(cur.description[0][0], "foo baz")

Lib/sqlite3/test/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,13 @@ def CheckNone(self):
253253

254254
def CheckColName(self):
255255
self.cur.execute("insert into test(x) values (?)", ("xxx",))
256-
self.cur.execute('select x as "x [bar]" from test')
256+
self.cur.execute('select x as "x y [bar]" from test')
257257
val = self.cur.fetchone()[0]
258258
self.assertEqual(val, "<xxx>")
259259

260260
# Check if the stripping of colnames works. Everything after the first
261-
# whitespace should be stripped.
262-
self.assertEqual(self.cur.description[0][0], "x")
261+
# '[' (and the preceeding space) should be stripped.
262+
self.assertEqual(self.cur.description[0][0], "x y")
263263

264264
def CheckCaseInConverterName(self):
265265
self.cur.execute("select 'other' as \"x [b1b1]\"")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The column name found in ``sqlite3.Cursor.description`` is now truncated on
2+
the first '[' only if the PARSE_COLNAMES option is set.

Modules/_sqlite/cursor.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,22 +198,31 @@ int pysqlite_build_row_cast_map(pysqlite_Cursor* self)
198198
return 0;
199199
}
200200

201-
PyObject* _pysqlite_build_column_name(const char* colname)
201+
static PyObject *
202+
_pysqlite_build_column_name(pysqlite_Cursor *self, const char *colname)
202203
{
203204
const char* pos;
205+
Py_ssize_t len;
204206

205207
if (!colname) {
206208
Py_RETURN_NONE;
207209
}
208210

209-
for (pos = colname;; pos++) {
210-
if (*pos == 0 || *pos == '[') {
211-
if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) {
212-
pos--;
211+
if (self->connection->detect_types & PARSE_COLNAMES) {
212+
for (pos = colname; *pos; pos++) {
213+
if (*pos == '[') {
214+
if ((pos != colname) && (*(pos-1) == ' ')) {
215+
pos--;
216+
}
217+
break;
213218
}
214-
return PyUnicode_FromStringAndSize(colname, pos - colname);
215219
}
220+
len = pos - colname;
221+
}
222+
else {
223+
len = strlen(colname);
216224
}
225+
return PyUnicode_FromStringAndSize(colname, len);
217226
}
218227

219228
/*
@@ -389,6 +398,7 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
389398
PyObject* result;
390399
int numcols;
391400
PyObject* descriptor;
401+
PyObject* column_name;
392402
PyObject* second_argument = NULL;
393403
sqlite_int64 lastrowid;
394404

@@ -569,7 +579,13 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
569579
if (!descriptor) {
570580
goto error;
571581
}
572-
PyTuple_SetItem(descriptor, 0, _pysqlite_build_column_name(sqlite3_column_name(self->statement->st, i)));
582+
column_name = _pysqlite_build_column_name(self,
583+
sqlite3_column_name(self->statement->st, i));
584+
if (!column_name) {
585+
Py_DECREF(descriptor);
586+
goto error;
587+
}
588+
PyTuple_SetItem(descriptor, 0, column_name);
573589
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
574590
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
575591
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);

0 commit comments

Comments
 (0)