summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-09-17 06:39:11 (GMT)
committerGitHub <noreply@github.com>2019-09-17 06:39:11 (GMT)
commitd8d653c2d0d267eb1e44f6cdb916945029fec3b1 (patch)
treefebf907476d093e00a7bf330b4e02319d4356e85
parent73ccc3322f7ad0d016478ef20237bccd0a314f0a (diff)
downloadcpython-d8d653c2d0d267eb1e44f6cdb916945029fec3b1.zip
cpython-d8d653c2d0d267eb1e44f6cdb916945029fec3b1.tar.gz
cpython-d8d653c2d0d267eb1e44f6cdb916945029fec3b1.tar.bz2
bpo-38185: Fixed case-insensitive string comparison in sqlite3.Row indexing. (GH-16190)
(cherry picked from commit f669581a9527afb0d2325f9845a86715c0ba365d) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
-rw-r--r--Lib/sqlite3/test/factory.py25
-rw-r--r--Misc/NEWS.d/next/Library/2019-09-16-19-12-57.bpo-38185.zYWppY.rst1
-rw-r--r--Modules/_sqlite/row.c62
3 files changed, 50 insertions, 38 deletions
diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py
index f103211..95dd24b 100644
--- a/Lib/sqlite3/test/factory.py
+++ b/Lib/sqlite3/test/factory.py
@@ -98,16 +98,14 @@ class RowFactoryTests(unittest.TestCase):
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")
@@ -117,12 +115,25 @@ class RowFactoryTests(unittest.TestCase):
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):
row[-3]
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 0000000..2260db6
--- /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 758518a..4b47108 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");