Skip to content

Commit fc662ac

Browse files
bpo-32788: Better error handling in sqlite3. (pythonGH-3723)
Propagate unexpected errors (like MemoryError and KeyboardInterrupt) to user.
1 parent dffccc6 commit fc662ac

File tree

7 files changed

+156
-124
lines changed

7 files changed

+156
-124
lines changed

Lib/sqlite3/test/types.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -102,24 +102,33 @@ def __conform__(self, protocol):
102102
def __str__(self):
103103
return "<%s>" % self.val
104104

105+
class BadConform:
106+
def __init__(self, exc):
107+
self.exc = exc
108+
def __conform__(self, protocol):
109+
raise self.exc
110+
105111
def setUp(self):
106112
self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
107113
self.cur = self.con.cursor()
108-
self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob, n1 number, n2 number(5))")
114+
self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob, n1 number, n2 number(5), bad bad)")
109115

110116
# override float, make them always return the same number
111117
sqlite.converters["FLOAT"] = lambda x: 47.2
112118

113119
# and implement two custom ones
114120
sqlite.converters["BOOL"] = lambda x: bool(int(x))
115121
sqlite.converters["FOO"] = DeclTypesTests.Foo
122+
sqlite.converters["BAD"] = DeclTypesTests.BadConform
116123
sqlite.converters["WRONG"] = lambda x: "WRONG"
117124
sqlite.converters["NUMBER"] = float
118125

119126
def tearDown(self):
120127
del sqlite.converters["FLOAT"]
121128
del sqlite.converters["BOOL"]
122129
del sqlite.converters["FOO"]
130+
del sqlite.converters["BAD"]
131+
del sqlite.converters["WRONG"]
123132
del sqlite.converters["NUMBER"]
124133
self.cur.close()
125134
self.con.close()
@@ -159,13 +168,13 @@ def CheckBool(self):
159168
self.cur.execute("insert into test(b) values (?)", (False,))
160169
self.cur.execute("select b from test")
161170
row = self.cur.fetchone()
162-
self.assertEqual(row[0], False)
171+
self.assertIs(row[0], False)
163172

164173
self.cur.execute("delete from test")
165174
self.cur.execute("insert into test(b) values (?)", (True,))
166175
self.cur.execute("select b from test")
167176
row = self.cur.fetchone()
168-
self.assertEqual(row[0], True)
177+
self.assertIs(row[0], True)
169178

170179
def CheckUnicode(self):
171180
# default
@@ -182,6 +191,19 @@ def CheckFoo(self):
182191
row = self.cur.fetchone()
183192
self.assertEqual(row[0], val)
184193

194+
def CheckErrorInConform(self):
195+
val = DeclTypesTests.BadConform(TypeError)
196+
with self.assertRaises(sqlite.InterfaceError):
197+
self.cur.execute("insert into test(bad) values (?)", (val,))
198+
with self.assertRaises(sqlite.InterfaceError):
199+
self.cur.execute("insert into test(bad) values (:val)", {"val": val})
200+
201+
val = DeclTypesTests.BadConform(KeyboardInterrupt)
202+
with self.assertRaises(KeyboardInterrupt):
203+
self.cur.execute("insert into test(bad) values (?)", (val,))
204+
with self.assertRaises(KeyboardInterrupt):
205+
self.cur.execute("insert into test(bad) values (:val)", {"val": val})
206+
185207
def CheckUnsupportedSeq(self):
186208
class Bar: pass
187209
val = Bar()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Errors other than :exc:`TypeError` raised in methods ``__adapt__()`` and
2+
``__conform__()`` in the :mod:`sqlite3` module are now propagated to the
3+
user.

Modules/_sqlite/cache.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args)
119119
pysqlite_Node* ptr;
120120
PyObject* data;
121121

122-
node = (pysqlite_Node*)PyDict_GetItem(self->mapping, key);
122+
node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
123123
if (node) {
124124
/* an entry for this key already exists in the cache */
125125

@@ -157,7 +157,11 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args)
157157
}
158158
ptr->prev = node;
159159
}
160-
} else {
160+
}
161+
else if (PyErr_Occurred()) {
162+
return NULL;
163+
}
164+
else {
161165
/* There is no entry for this key in the cache, yet. We'll insert a new
162166
* entry in the cache, and make space if necessary by throwing the
163167
* least used item out of the cache. */

Modules/_sqlite/connection.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,7 @@ pysqlite_connection_interrupt(pysqlite_Connection* self, PyObject* args)
14321432
static PyObject *
14331433
pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
14341434
{
1435+
_Py_IDENTIFIER(_iterdump);
14351436
PyObject* retval = NULL;
14361437
PyObject* module = NULL;
14371438
PyObject* module_dict;
@@ -1451,9 +1452,12 @@ pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
14511452
goto finally;
14521453
}
14531454

1454-
pyfn_iterdump = PyDict_GetItemString(module_dict, "_iterdump");
1455+
pyfn_iterdump = _PyDict_GetItemIdWithError(module_dict, &PyId__iterdump);
14551456
if (!pyfn_iterdump) {
1456-
PyErr_SetString(pysqlite_OperationalError, "Failed to obtain _iterdump() reference");
1457+
if (!PyErr_Occurred()) {
1458+
PyErr_SetString(pysqlite_OperationalError,
1459+
"Failed to obtain _iterdump() reference");
1460+
}
14571461
goto finally;
14581462
}
14591463

0 commit comments

Comments
 (0)