]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.7] bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set...
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 21 Mar 2020 14:33:44 +0000 (16:33 +0200)
committerGitHub <noreply@github.com>
Sat, 21 Mar 2020 14:33:44 +0000 (16:33 +0200)
(cherry picked from commit b146568dfcbcd7409c724f8917e4f77433dd56e4)

Doc/library/sqlite3.rst
Lib/sqlite3/test/regression.py
Lib/sqlite3/test/types.py
Misc/NEWS.d/next/Library/2020-03-11-23-08-25.bpo-39652.gbasrk.rst [new file with mode: 0644]
Modules/_sqlite/cursor.c

index e20b4b3db370eb3b042a8bf2d3b27b1d02ab3939..8609be2ca65a8dc684f657f0a2536624e54218bf 100644 (file)
@@ -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])
index 865bd88f74f10a4947f7338b328d8fb372a316d0..25c58f562d4c3fa17429233be6f8b93189ef6a70 100644 (file)
@@ -67,7 +67,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")
index 6bc8d71def0e31bf1e266578ad6126b96df8f5de..77811212d49cb0b930f3e26c8d928ae689de968e 100644 (file)
@@ -253,13 +253,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 [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 (file)
index 0000000..9b75ae9
--- /dev/null
@@ -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.
index e96f3888b4616327960f7bb2b500ccbd9db1c92c..6c98a99bd02573c668d09d6a755916d78f38362e 100644 (file)
@@ -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);
 }
 
 /*
@@ -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;
 
@@ -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);