summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2020-03-21 13:53:28 (GMT)
committerGitHub <noreply@github.com>2020-03-21 13:53:28 (GMT)
commitb146568dfcbcd7409c724f8917e4f77433dd56e4 (patch)
tree716efcf61f1bef4887ca6c6db39ea0f8d49b8c57
parent684d2b9a071fa8e54749e0eec3c16aafcd642ed4 (diff)
downloadcpython-b146568dfcbcd7409c724f8917e4f77433dd56e4.zip
cpython-b146568dfcbcd7409c724f8917e4f77433dd56e4.tar.gz
cpython-b146568dfcbcd7409c724f8917e4f77433dd56e4.tar.bz2
bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set. (GH-18942)
-rw-r--r--Doc/library/sqlite3.rst7
-rw-r--r--Lib/sqlite3/test/regression.py2
-rw-r--r--Lib/sqlite3/test/types.py6
-rw-r--r--Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst2
-rw-r--r--Modules/_sqlite/cursor.c29
5 files changed, 32 insertions, 14 deletions
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 67ea2b1..314d3a5 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -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])
diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py
index c714116..cbd46d4 100644
--- a/Lib/sqlite3/test/regression.py
+++ b/Lib/sqlite3/test/regression.py
@@ -68,7 +68,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")
diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py
index 19ecd07..d26a9cb 100644
--- a/Lib/sqlite3/test/types.py
+++ b/Lib/sqlite3/test/types.py
@@ -275,13 +275,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]\"")
diff --git a/Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst b/Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst
new file mode 100644
index 0000000..9b75ae9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst
@@ -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.
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index ab276db..5cfb4b9 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -193,22 +193,30 @@ pysqlite_build_row_cast_map(pysqlite_Cursor* self)
}
static PyObject *
-_pysqlite_build_column_name(const char* colname)
+_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);
}
/*
@@ -370,6 +378,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
PyObject* result;
int numcols;
PyObject* descriptor;
+ PyObject* column_name;
PyObject* second_argument = NULL;
sqlite_int64 lastrowid;
@@ -536,7 +545,13 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
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);