Skip to content

Commit b146568

Browse files
bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set. (GH-18942)
1 parent 684d2b9 commit b146568

File tree

5 files changed

+32
-14
lines changed

5 files changed

+32
-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
@@ -68,7 +68,7 @@ def CheckStatementReset(self):
6868
def CheckColumnNameWithSpaces(self):
6969
cur = self.con.cursor()
7070
cur.execute('select 1 as "foo bar [datetime]"')
71-
self.assertEqual(cur.description[0][0], "foo bar")
71+
self.assertEqual(cur.description[0][0], "foo bar [datetime]")
7272

7373
cur.execute('select 1 as "foo baz"')
7474
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
@@ -275,13 +275,13 @@ def CheckNone(self):
275275

276276
def CheckColName(self):
277277
self.cur.execute("insert into test(x) values (?)", ("xxx",))
278-
self.cur.execute('select x as "x [bar]" from test')
278+
self.cur.execute('select x as "x y [bar]" from test')
279279
val = self.cur.fetchone()[0]
280280
self.assertEqual(val, "<xxx>")
281281

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

286286
def CheckCaseInConverterName(self):
287287
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: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,22 +193,30 @@ pysqlite_build_row_cast_map(pysqlite_Cursor* self)
193193
}
194194

195195
static PyObject *
196-
_pysqlite_build_column_name(const char* colname)
196+
_pysqlite_build_column_name(pysqlite_Cursor *self, const char *colname)
197197
{
198198
const char* pos;
199+
Py_ssize_t len;
199200

200201
if (!colname) {
201202
Py_RETURN_NONE;
202203
}
203204

204-
for (pos = colname;; pos++) {
205-
if (*pos == 0 || *pos == '[') {
206-
if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) {
207-
pos--;
205+
if (self->connection->detect_types & PARSE_COLNAMES) {
206+
for (pos = colname; *pos; pos++) {
207+
if (*pos == '[') {
208+
if ((pos != colname) && (*(pos-1) == ' ')) {
209+
pos--;
210+
}
211+
break;
208212
}
209-
return PyUnicode_FromStringAndSize(colname, pos - colname);
210213
}
214+
len = pos - colname;
215+
}
216+
else {
217+
len = strlen(colname);
211218
}
219+
return PyUnicode_FromStringAndSize(colname, len);
212220
}
213221

214222
/*
@@ -370,6 +378,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
370378
PyObject* result;
371379
int numcols;
372380
PyObject* descriptor;
381+
PyObject* column_name;
373382
PyObject* second_argument = NULL;
374383
sqlite_int64 lastrowid;
375384

@@ -536,7 +545,13 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
536545
if (!descriptor) {
537546
goto error;
538547
}
539-
PyTuple_SetItem(descriptor, 0, _pysqlite_build_column_name(sqlite3_column_name(self->statement->st, i)));
548+
column_name = _pysqlite_build_column_name(self,
549+
sqlite3_column_name(self->statement->st, i));
550+
if (!column_name) {
551+
Py_DECREF(descriptor);
552+
goto error;
553+
}
554+
PyTuple_SetItem(descriptor, 0, column_name);
540555
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
541556
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
542557
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);

0 commit comments

Comments
 (0)