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

Miss Islington (bot) webhook-mailer at python.org
Sat Mar 21 10:32:30 EDT 2020


https://github.com/python/cpython/commit/687f5921a46cf95c2a648d8031f9e99cdcc3e6b7
commit: 687f5921a46cf95c2a648d8031f9e99cdcc3e6b7
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2020-03-21T07:32:25-07:00
summary:

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

(cherry picked from commit b146568dfcbcd7409c724f8917e4f77433dd56e4)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
A Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst
M Doc/library/sqlite3.rst
M Lib/sqlite3/test/regression.py
M Lib/sqlite3/test/types.py
M Modules/_sqlite/cursor.c

diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 67ea2b1d77663..314d3a58e2759 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 dd5aec50d1867..ce97655c66be8 100644
--- a/Lib/sqlite3/test/regression.py
+++ b/Lib/sqlite3/test/regression.py
@@ -68,7 +68,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")
diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py
index 19ecd07500fec..d26a9cb93f088 100644
--- a/Lib/sqlite3/test/types.py
+++ b/Lib/sqlite3/test/types.py
@@ -275,13 +275,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]\"")
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 0000000000000..9b75ae9df6cec
--- /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 01b9dc44cb5da..8cfa6e50e8222 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;
 
@@ -546,7 +555,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);



More information about the Python-checkins mailing list