Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f669581

Browse files
authoredSep 17, 2019
bpo-38185: Fixed case-insensitive string comparison in sqlite3.Row indexing. (pythonGH-16190)
1 parent dfd34a9 commit f669581

File tree

3 files changed

+50
-38
lines changed

3 files changed

+50
-38
lines changed
 

‎Lib/sqlite3/test/factory.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,14 @@ def CheckCustomFactory(self):
9898

9999
def CheckSqliteRowIndex(self):
100100
self.con.row_factory = sqlite.Row
101-
row = self.con.execute("select 1 as a, 2 as b").fetchone()
101+
row = self.con.execute("select 1 as a_1, 2 as b").fetchone()
102102
self.assertIsInstance(row, sqlite.Row)
103103

104-
col1, col2 = row["a"], row["b"]
105-
self.assertEqual(col1, 1, "by name: wrong result for column 'a'")
106-
self.assertEqual(col2, 2, "by name: wrong result for column 'a'")
104+
self.assertEqual(row["a_1"], 1, "by name: wrong result for column 'a_1'")
105+
self.assertEqual(row["b"], 2, "by name: wrong result for column 'b'")
107106

108-
col1, col2 = row["A"], row["B"]
109-
self.assertEqual(col1, 1, "by name: wrong result for column 'A'")
110-
self.assertEqual(col2, 2, "by name: wrong result for column 'B'")
107+
self.assertEqual(row["A_1"], 1, "by name: wrong result for column 'A_1'")
108+
self.assertEqual(row["B"], 2, "by name: wrong result for column 'B'")
111109

112110
self.assertEqual(row[0], 1, "by index: wrong result for column 0")
113111
self.assertEqual(row[1], 2, "by index: wrong result for column 1")
@@ -116,13 +114,26 @@ def CheckSqliteRowIndex(self):
116114

117115
with self.assertRaises(IndexError):
118116
row['c']
117+
with self.assertRaises(IndexError):
118+
row['a_\x11']
119+
with self.assertRaises(IndexError):
120+
row['a\x7f1']
119121
with self.assertRaises(IndexError):
120122
row[2]
121123
with self.assertRaises(IndexError):
122124
row[-3]
123125
with self.assertRaises(IndexError):
124126
row[2**1000]
125127

128+
def CheckSqliteRowIndexUnicode(self):
129+
self.con.row_factory = sqlite.Row
130+
row = self.con.execute("select 1 as \xff").fetchone()
131+
self.assertEqual(row["\xff"], 1)
132+
with self.assertRaises(IndexError):
133+
row['\u0178']
134+
with self.assertRaises(IndexError):
135+
row['\xdf']
136+
126137
def CheckSqliteRowSlice(self):
127138
# A sqlite.Row can be sliced like a list.
128139
self.con.row_factory = sqlite.Row
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed case-insensitive string comparison in :class:`sqlite3.Row` indexing.

‎Modules/_sqlite/row.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,38 @@ PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx)
7676
return item;
7777
}
7878

79+
static int
80+
equal_ignore_case(PyObject *left, PyObject *right)
81+
{
82+
int eq = PyObject_RichCompareBool(left, right, Py_EQ);
83+
if (eq) { /* equal or error */
84+
return eq;
85+
}
86+
if (!PyUnicode_Check(left) || !PyUnicode_Check(right)) {
87+
return 0;
88+
}
89+
if (!PyUnicode_IS_ASCII(left) || !PyUnicode_IS_ASCII(right)) {
90+
return 0;
91+
}
92+
93+
Py_ssize_t len = PyUnicode_GET_LENGTH(left);
94+
if (PyUnicode_GET_LENGTH(right) != len) {
95+
return 0;
96+
}
97+
const Py_UCS1 *p1 = PyUnicode_1BYTE_DATA(left);
98+
const Py_UCS1 *p2 = PyUnicode_1BYTE_DATA(right);
99+
for (; len; len--, p1++, p2++) {
100+
if (Py_TOLOWER(*p1) != Py_TOLOWER(*p2)) {
101+
return 0;
102+
}
103+
}
104+
return 1;
105+
}
106+
79107
PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
80108
{
81109
Py_ssize_t _idx;
82-
const char *key;
83110
Py_ssize_t nitems, i;
84-
const char *compare_key;
85-
86-
const char *p1;
87-
const char *p2;
88-
89111
PyObject* item;
90112

91113
if (PyLong_Check(idx)) {
@@ -98,44 +120,22 @@ PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
98120
Py_XINCREF(item);
99121
return item;
100122
} else if (PyUnicode_Check(idx)) {
101-
key = PyUnicode_AsUTF8(idx);
102-
if (key == NULL)
103-
return NULL;
104-
105123
nitems = PyTuple_Size(self->description);
106124

107125
for (i = 0; i < nitems; i++) {
108126
PyObject *obj;
109127
obj = PyTuple_GET_ITEM(self->description, i);
110128
obj = PyTuple_GET_ITEM(obj, 0);
111-
compare_key = PyUnicode_AsUTF8(obj);
112-
if (!compare_key) {
129+
int eq = equal_ignore_case(idx, obj);
130+
if (eq < 0) {
113131
return NULL;
114132
}
115-
116-
p1 = key;
117-
p2 = compare_key;
118-
119-
while (1) {
120-
if ((*p1 == (char)0) || (*p2 == (char)0)) {
121-
break;
122-
}
123-
124-
if ((*p1 | 0x20) != (*p2 | 0x20)) {
125-
break;
126-
}
127-
128-
p1++;
129-
p2++;
130-
}
131-
132-
if ((*p1 == (char)0) && (*p2 == (char)0)) {
133+
if (eq) {
133134
/* found item */
134135
item = PyTuple_GetItem(self->data, i);
135136
Py_INCREF(item);
136137
return item;
137138
}
138-
139139
}
140140

141141
PyErr_SetString(PyExc_IndexError, "No item with that key");

0 commit comments

Comments
 (0)