Skip to content

Commit 1713f20

Browse files
author
gerhard.haering
committed
Applied sqliterow-richcmp.diff patch from Thomas Heller in Issue2152. The
sqlite3.Row type is now correctly hashable. git-svn-id: http://svn.python.org/projects/python/trunk@62701 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent f7bee60 commit 1713f20

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

Lib/sqlite3/test/factory.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,26 @@ def CheckSqliteRowAsDict(self):
131131
self.failUnlessEqual(d["a"], row["a"])
132132
self.failUnlessEqual(d["b"], row["b"])
133133

134+
def CheckSqliteRowHashCmp(self):
135+
"""Checks if the row object compares and hashes correctly"""
136+
self.con.row_factory = sqlite.Row
137+
row_1 = self.con.execute("select 1 as a, 2 as b").fetchone()
138+
row_2 = self.con.execute("select 1 as a, 2 as b").fetchone()
139+
row_3 = self.con.execute("select 1 as a, 3 as b").fetchone()
140+
141+
self.failUnless(row_1 == row_1)
142+
self.failUnless(row_1 == row_2)
143+
self.failUnless(row_2 != row_3)
144+
145+
self.failIf(row_1 != row_1)
146+
self.failIf(row_1 != row_2)
147+
self.failIf(row_2 == row_3)
148+
149+
self.failUnlessEqual(row_1, row_2)
150+
self.failUnlessEqual(hash(row_1), hash(row_2))
151+
self.failIfEqual(row_1, row_3)
152+
self.failIfEqual(hash(row_1), hash(row_3))
153+
134154
def tearDown(self):
135155
self.con.close()
136156

Modules/_sqlite/row.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,30 @@ static PyObject* pysqlite_iter(pysqlite_Row* self)
169169
return PyObject_GetIter(self->data);
170170
}
171171

172+
static long pysqlite_row_hash(pysqlite_Row *self)
173+
{
174+
return PyObject_Hash(self->description) ^ PyObject_Hash(self->data);
175+
}
176+
177+
static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid)
178+
{
179+
if (opid != Py_EQ && opid != Py_NE) {
180+
Py_INCREF(Py_NotImplemented);
181+
return Py_NotImplemented;
182+
}
183+
if (PyType_IsSubtype(Py_TYPE(_other), &pysqlite_RowType)) {
184+
pysqlite_Row *other = (pysqlite_Row *)_other;
185+
PyObject *res = PyObject_RichCompare(self->description, other->description, opid);
186+
if (opid == Py_EQ && res == Py_True
187+
|| opid == Py_NE && res == Py_False) {
188+
Py_DECREF(res);
189+
return PyObject_RichCompare(self->data, other->data, opid);
190+
}
191+
}
192+
Py_INCREF(Py_NotImplemented);
193+
return Py_NotImplemented;
194+
}
195+
172196
PyMappingMethods pysqlite_row_as_mapping = {
173197
/* mp_length */ (lenfunc)pysqlite_row_length,
174198
/* mp_subscript */ (binaryfunc)pysqlite_row_subscript,
@@ -196,7 +220,7 @@ PyTypeObject pysqlite_RowType = {
196220
0, /* tp_as_number */
197221
0, /* tp_as_sequence */
198222
0, /* tp_as_mapping */
199-
0, /* tp_hash */
223+
(hashfunc)pysqlite_row_hash, /* tp_hash */
200224
0, /* tp_call */
201225
0, /* tp_str */
202226
0, /* tp_getattro */
@@ -206,7 +230,7 @@ PyTypeObject pysqlite_RowType = {
206230
0, /* tp_doc */
207231
(traverseproc)0, /* tp_traverse */
208232
0, /* tp_clear */
209-
0, /* tp_richcompare */
233+
(richcmpfunc)pysqlite_row_richcompare, /* tp_richcompare */
210234
0, /* tp_weaklistoffset */
211235
(getiterfunc)pysqlite_iter, /* tp_iter */
212236
0, /* tp_iternext */

0 commit comments

Comments
 (0)