[Python-checkins] bpo-38185: Fixed case-insensitive string comparison in sqlite3.Row indexing. (GH-16190)

Miss Islington (bot) webhook-mailer at python.org
Tue Sep 17 02:39:15 EDT 2019


https://github.com/python/cpython/commit/d8d653c2d0d267eb1e44f6cdb916945029fec3b1
commit: d8d653c2d0d267eb1e44f6cdb916945029fec3b1
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-09-16T23:39:11-07:00
summary:

bpo-38185: Fixed case-insensitive string comparison in sqlite3.Row indexing. (GH-16190)

(cherry picked from commit f669581a9527afb0d2325f9845a86715c0ba365d)

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

files:
A Misc/NEWS.d/next/Library/2019-09-16-19-12-57.bpo-38185.zYWppY.rst
M Lib/sqlite3/test/factory.py
M Modules/_sqlite/row.c

diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py
index f103211ed9f1..95dd24bdfadc 100644
--- a/Lib/sqlite3/test/factory.py
+++ b/Lib/sqlite3/test/factory.py
@@ -98,16 +98,14 @@ def CheckCustomFactory(self):
 
     def CheckSqliteRowIndex(self):
         self.con.row_factory = sqlite.Row
-        row = self.con.execute("select 1 as a, 2 as b").fetchone()
+        row = self.con.execute("select 1 as a_1, 2 as b").fetchone()
         self.assertIsInstance(row, sqlite.Row)
 
-        col1, col2 = row["a"], row["b"]
-        self.assertEqual(col1, 1, "by name: wrong result for column 'a'")
-        self.assertEqual(col2, 2, "by name: wrong result for column 'a'")
+        self.assertEqual(row["a_1"], 1, "by name: wrong result for column 'a_1'")
+        self.assertEqual(row["b"], 2, "by name: wrong result for column 'b'")
 
-        col1, col2 = row["A"], row["B"]
-        self.assertEqual(col1, 1, "by name: wrong result for column 'A'")
-        self.assertEqual(col2, 2, "by name: wrong result for column 'B'")
+        self.assertEqual(row["A_1"], 1, "by name: wrong result for column 'A_1'")
+        self.assertEqual(row["B"], 2, "by name: wrong result for column 'B'")
 
         self.assertEqual(row[0], 1, "by index: wrong result for column 0")
         self.assertEqual(row[1], 2, "by index: wrong result for column 1")
@@ -116,6 +114,10 @@ def CheckSqliteRowIndex(self):
 
         with self.assertRaises(IndexError):
             row['c']
+        with self.assertRaises(IndexError):
+            row['a_\x11']
+        with self.assertRaises(IndexError):
+            row['a\x7f1']
         with self.assertRaises(IndexError):
             row[2]
         with self.assertRaises(IndexError):
@@ -123,6 +125,15 @@ def CheckSqliteRowIndex(self):
         with self.assertRaises(IndexError):
             row[2**1000]
 
+    def CheckSqliteRowIndexUnicode(self):
+        self.con.row_factory = sqlite.Row
+        row = self.con.execute("select 1 as \xff").fetchone()
+        self.assertEqual(row["\xff"], 1)
+        with self.assertRaises(IndexError):
+            row['\u0178']
+        with self.assertRaises(IndexError):
+            row['\xdf']
+
     def CheckSqliteRowSlice(self):
         # A sqlite.Row can be sliced like a list.
         self.con.row_factory = sqlite.Row
diff --git a/Misc/NEWS.d/next/Library/2019-09-16-19-12-57.bpo-38185.zYWppY.rst b/Misc/NEWS.d/next/Library/2019-09-16-19-12-57.bpo-38185.zYWppY.rst
new file mode 100644
index 000000000000..2260db68e8cf
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-09-16-19-12-57.bpo-38185.zYWppY.rst
@@ -0,0 +1 @@
+Fixed case-insensitive string comparison in :class:`sqlite3.Row` indexing.
diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c
index 758518a8ff80..4b47108278a0 100644
--- a/Modules/_sqlite/row.c
+++ b/Modules/_sqlite/row.c
@@ -76,16 +76,38 @@ PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx)
    return item;
 }
 
+static int
+equal_ignore_case(PyObject *left, PyObject *right)
+{
+    int eq = PyObject_RichCompareBool(left, right, Py_EQ);
+    if (eq) { /* equal or error */
+        return eq;
+    }
+    if (!PyUnicode_Check(left) || !PyUnicode_Check(right)) {
+        return 0;
+    }
+    if (!PyUnicode_IS_ASCII(left) || !PyUnicode_IS_ASCII(right)) {
+        return 0;
+    }
+
+    Py_ssize_t len = PyUnicode_GET_LENGTH(left);
+    if (PyUnicode_GET_LENGTH(right) != len) {
+        return 0;
+    }
+    const Py_UCS1 *p1 = PyUnicode_1BYTE_DATA(left);
+    const Py_UCS1 *p2 = PyUnicode_1BYTE_DATA(right);
+    for (; len; len--, p1++, p2++) {
+        if (Py_TOLOWER(*p1) != Py_TOLOWER(*p2)) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
 PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
 {
     Py_ssize_t _idx;
-    const char *key;
     Py_ssize_t nitems, i;
-    const char *compare_key;
-
-    const char *p1;
-    const char *p2;
-
     PyObject* item;
 
     if (PyLong_Check(idx)) {
@@ -98,44 +120,22 @@ PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
         Py_XINCREF(item);
         return item;
     } else if (PyUnicode_Check(idx)) {
-        key = PyUnicode_AsUTF8(idx);
-        if (key == NULL)
-            return NULL;
-
         nitems = PyTuple_Size(self->description);
 
         for (i = 0; i < nitems; i++) {
             PyObject *obj;
             obj = PyTuple_GET_ITEM(self->description, i);
             obj = PyTuple_GET_ITEM(obj, 0);
-            compare_key = PyUnicode_AsUTF8(obj);
-            if (!compare_key) {
+            int eq = equal_ignore_case(idx, obj);
+            if (eq < 0) {
                 return NULL;
             }
-
-            p1 = key;
-            p2 = compare_key;
-
-            while (1) {
-                if ((*p1 == (char)0) || (*p2 == (char)0)) {
-                    break;
-                }
-
-                if ((*p1 | 0x20) != (*p2 | 0x20)) {
-                    break;
-                }
-
-                p1++;
-                p2++;
-            }
-
-            if ((*p1 == (char)0) && (*p2 == (char)0)) {
+            if (eq) {
                 /* found item */
                 item = PyTuple_GetItem(self->data, i);
                 Py_INCREF(item);
                 return item;
             }
-
         }
 
         PyErr_SetString(PyExc_IndexError, "No item with that key");



More information about the Python-checkins mailing list