From 5a366c3b8bf1e8afb2d48a02b498f2eaab812c49 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gerhard=20H=C3=A4ring?= Date: Sun, 4 May 2008 13:15:12 +0000 Subject: [PATCH] Applied sqliterow-richcmp.diff patch from Thomas Heller in Issue2152. The sqlite3.Row type is now correctly hashable. --- Lib/sqlite3/test/factory.py | 20 ++++++++++++++++++++ Modules/_sqlite/row.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py index 9469d79c2e..ff41a9d2ab 100644 --- a/Lib/sqlite3/test/factory.py +++ b/Lib/sqlite3/test/factory.py @@ -131,6 +131,26 @@ class RowFactoryTests(unittest.TestCase): self.failUnlessEqual(d["a"], row["a"]) self.failUnlessEqual(d["b"], row["b"]) + def CheckSqliteRowHashCmp(self): + """Checks if the row object compares and hashes correctly""" + self.con.row_factory = sqlite.Row + row_1 = self.con.execute("select 1 as a, 2 as b").fetchone() + row_2 = self.con.execute("select 1 as a, 2 as b").fetchone() + row_3 = self.con.execute("select 1 as a, 3 as b").fetchone() + + self.failUnless(row_1 == row_1) + self.failUnless(row_1 == row_2) + self.failUnless(row_2 != row_3) + + self.failIf(row_1 != row_1) + self.failIf(row_1 != row_2) + self.failIf(row_2 == row_3) + + self.failUnlessEqual(row_1, row_2) + self.failUnlessEqual(hash(row_1), hash(row_2)) + self.failIfEqual(row_1, row_3) + self.failIfEqual(hash(row_1), hash(row_3)) + def tearDown(self): self.con.close() diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 8f155d92a4..34192673a6 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -169,6 +169,30 @@ static PyObject* pysqlite_iter(pysqlite_Row* self) return PyObject_GetIter(self->data); } +static long pysqlite_row_hash(pysqlite_Row *self) +{ + return PyObject_Hash(self->description) ^ PyObject_Hash(self->data); +} + +static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid) +{ + if (opid != Py_EQ && opid != Py_NE) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (PyType_IsSubtype(Py_TYPE(_other), &pysqlite_RowType)) { + pysqlite_Row *other = (pysqlite_Row *)_other; + PyObject *res = PyObject_RichCompare(self->description, other->description, opid); + if (opid == Py_EQ && res == Py_True + || opid == Py_NE && res == Py_False) { + Py_DECREF(res); + return PyObject_RichCompare(self->data, other->data, opid); + } + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + PyMappingMethods pysqlite_row_as_mapping = { /* mp_length */ (lenfunc)pysqlite_row_length, /* mp_subscript */ (binaryfunc)pysqlite_row_subscript, @@ -196,7 +220,7 @@ PyTypeObject pysqlite_RowType = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)pysqlite_row_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ @@ -206,7 +230,7 @@ PyTypeObject pysqlite_RowType = { 0, /* tp_doc */ (traverseproc)0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + (richcmpfunc)pysqlite_row_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)pysqlite_iter, /* tp_iter */ 0, /* tp_iternext */ -- 2.50.1