Unverified Kaydet (Commit) fc662ac3 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka Kaydeden (comit) GitHub

bpo-32788: Better error handling in sqlite3. (GH-3723)

Propagate unexpected errors (like MemoryError and KeyboardInterrupt) to user.
üst dffccc6b
...@@ -102,10 +102,16 @@ class DeclTypesTests(unittest.TestCase): ...@@ -102,10 +102,16 @@ class DeclTypesTests(unittest.TestCase):
def __str__(self): def __str__(self):
return "<%s>" % self.val return "<%s>" % self.val
class BadConform:
def __init__(self, exc):
self.exc = exc
def __conform__(self, protocol):
raise self.exc
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
self.cur = self.con.cursor() self.cur = self.con.cursor()
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))") 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)")
# override float, make them always return the same number # override float, make them always return the same number
sqlite.converters["FLOAT"] = lambda x: 47.2 sqlite.converters["FLOAT"] = lambda x: 47.2
...@@ -113,6 +119,7 @@ class DeclTypesTests(unittest.TestCase): ...@@ -113,6 +119,7 @@ class DeclTypesTests(unittest.TestCase):
# and implement two custom ones # and implement two custom ones
sqlite.converters["BOOL"] = lambda x: bool(int(x)) sqlite.converters["BOOL"] = lambda x: bool(int(x))
sqlite.converters["FOO"] = DeclTypesTests.Foo sqlite.converters["FOO"] = DeclTypesTests.Foo
sqlite.converters["BAD"] = DeclTypesTests.BadConform
sqlite.converters["WRONG"] = lambda x: "WRONG" sqlite.converters["WRONG"] = lambda x: "WRONG"
sqlite.converters["NUMBER"] = float sqlite.converters["NUMBER"] = float
...@@ -120,6 +127,8 @@ class DeclTypesTests(unittest.TestCase): ...@@ -120,6 +127,8 @@ class DeclTypesTests(unittest.TestCase):
del sqlite.converters["FLOAT"] del sqlite.converters["FLOAT"]
del sqlite.converters["BOOL"] del sqlite.converters["BOOL"]
del sqlite.converters["FOO"] del sqlite.converters["FOO"]
del sqlite.converters["BAD"]
del sqlite.converters["WRONG"]
del sqlite.converters["NUMBER"] del sqlite.converters["NUMBER"]
self.cur.close() self.cur.close()
self.con.close() self.con.close()
...@@ -159,13 +168,13 @@ class DeclTypesTests(unittest.TestCase): ...@@ -159,13 +168,13 @@ class DeclTypesTests(unittest.TestCase):
self.cur.execute("insert into test(b) values (?)", (False,)) self.cur.execute("insert into test(b) values (?)", (False,))
self.cur.execute("select b from test") self.cur.execute("select b from test")
row = self.cur.fetchone() row = self.cur.fetchone()
self.assertEqual(row[0], False) self.assertIs(row[0], False)
self.cur.execute("delete from test") self.cur.execute("delete from test")
self.cur.execute("insert into test(b) values (?)", (True,)) self.cur.execute("insert into test(b) values (?)", (True,))
self.cur.execute("select b from test") self.cur.execute("select b from test")
row = self.cur.fetchone() row = self.cur.fetchone()
self.assertEqual(row[0], True) self.assertIs(row[0], True)
def CheckUnicode(self): def CheckUnicode(self):
# default # default
...@@ -182,6 +191,19 @@ class DeclTypesTests(unittest.TestCase): ...@@ -182,6 +191,19 @@ class DeclTypesTests(unittest.TestCase):
row = self.cur.fetchone() row = self.cur.fetchone()
self.assertEqual(row[0], val) self.assertEqual(row[0], val)
def CheckErrorInConform(self):
val = DeclTypesTests.BadConform(TypeError)
with self.assertRaises(sqlite.InterfaceError):
self.cur.execute("insert into test(bad) values (?)", (val,))
with self.assertRaises(sqlite.InterfaceError):
self.cur.execute("insert into test(bad) values (:val)", {"val": val})
val = DeclTypesTests.BadConform(KeyboardInterrupt)
with self.assertRaises(KeyboardInterrupt):
self.cur.execute("insert into test(bad) values (?)", (val,))
with self.assertRaises(KeyboardInterrupt):
self.cur.execute("insert into test(bad) values (:val)", {"val": val})
def CheckUnsupportedSeq(self): def CheckUnsupportedSeq(self):
class Bar: pass class Bar: pass
val = Bar() val = Bar()
......
Errors other than :exc:`TypeError` raised in methods ``__adapt__()`` and
``__conform__()`` in the :mod:`sqlite3` module are now propagated to the
user.
...@@ -119,7 +119,7 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args) ...@@ -119,7 +119,7 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args)
pysqlite_Node* ptr; pysqlite_Node* ptr;
PyObject* data; PyObject* data;
node = (pysqlite_Node*)PyDict_GetItem(self->mapping, key); node = (pysqlite_Node*)PyDict_GetItemWithError(self->mapping, key);
if (node) { if (node) {
/* an entry for this key already exists in the cache */ /* an entry for this key already exists in the cache */
...@@ -157,7 +157,11 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args) ...@@ -157,7 +157,11 @@ PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args)
} }
ptr->prev = node; ptr->prev = node;
} }
} else { }
else if (PyErr_Occurred()) {
return NULL;
}
else {
/* There is no entry for this key in the cache, yet. We'll insert a new /* There is no entry for this key in the cache, yet. We'll insert a new
* entry in the cache, and make space if necessary by throwing the * entry in the cache, and make space if necessary by throwing the
* least used item out of the cache. */ * least used item out of the cache. */
......
...@@ -1432,6 +1432,7 @@ finally: ...@@ -1432,6 +1432,7 @@ finally:
static PyObject * static PyObject *
pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args) pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
{ {
_Py_IDENTIFIER(_iterdump);
PyObject* retval = NULL; PyObject* retval = NULL;
PyObject* module = NULL; PyObject* module = NULL;
PyObject* module_dict; PyObject* module_dict;
...@@ -1451,9 +1452,12 @@ pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args) ...@@ -1451,9 +1452,12 @@ pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
goto finally; goto finally;
} }
pyfn_iterdump = PyDict_GetItemString(module_dict, "_iterdump"); pyfn_iterdump = _PyDict_GetItemIdWithError(module_dict, &PyId__iterdump);
if (!pyfn_iterdump) { if (!pyfn_iterdump) {
PyErr_SetString(pysqlite_OperationalError, "Failed to obtain _iterdump() reference"); if (!PyErr_Occurred()) {
PyErr_SetString(pysqlite_OperationalError,
"Failed to obtain _iterdump() reference");
}
goto finally; goto finally;
} }
......
This diff is collapsed.
...@@ -75,7 +75,9 @@ pysqlite_microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) ...@@ -75,7 +75,9 @@ pysqlite_microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
PyObject * PyObject *
pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
{ {
PyObject *adapter, *key; _Py_IDENTIFIER(__adapt__);
_Py_IDENTIFIER(__conform__);
PyObject *adapter, *key, *adapted;
/* we don't check for exact type conformance as specified in PEP 246 /* we don't check for exact type conformance as specified in PEP 246
because the pysqlite_PrepareProtocolType type is abstract and there is no because the pysqlite_PrepareProtocolType type is abstract and there is no
...@@ -86,48 +88,60 @@ pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) ...@@ -86,48 +88,60 @@ pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
if (!key) { if (!key) {
return NULL; return NULL;
} }
adapter = PyDict_GetItem(psyco_adapters, key); adapter = PyDict_GetItemWithError(psyco_adapters, key);
Py_DECREF(key); Py_DECREF(key);
if (adapter) { if (adapter) {
PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); Py_INCREF(adapter);
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
Py_DECREF(adapter);
return adapted; return adapted;
} }
if (PyErr_Occurred()) {
return NULL;
}
/* try to have the protocol adapt this object*/ /* try to have the protocol adapt this object */
if (PyObject_HasAttrString(proto, "__adapt__")) { if (_PyObject_LookupAttrId(proto, &PyId___adapt__, &adapter) < 0) {
_Py_IDENTIFIER(__adapt__); return NULL;
PyObject *adapted = _PyObject_CallMethodId(proto, &PyId___adapt__, "O", obj); }
if (adapter) {
if (adapted) { adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
if (adapted != Py_None) { Py_DECREF(adapter);
return adapted;
} else {
Py_DECREF(adapted);
}
}
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) if (adapted == Py_None) {
return NULL; Py_DECREF(adapted);
}
else if (adapted || !PyErr_ExceptionMatches(PyExc_TypeError)) {
return adapted;
}
else {
PyErr_Clear();
}
} }
/* and finally try to have the object adapt itself */ /* and finally try to have the object adapt itself */
if (PyObject_HasAttrString(obj, "__conform__")) { if (_PyObject_LookupAttrId(obj, &PyId___conform__, &adapter) < 0) {
_Py_IDENTIFIER(__conform__); return NULL;
PyObject *adapted = _PyObject_CallMethodId(obj, &PyId___conform__,"O", proto); }
if (adapter) {
if (adapted) { adapted = PyObject_CallFunctionObjArgs(adapter, proto, NULL);
if (adapted != Py_None) { Py_DECREF(adapter);
return adapted;
} else {
Py_DECREF(adapted);
}
}
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) { if (adapted == Py_None) {
return NULL; Py_DECREF(adapted);
}
else if (adapted || !PyErr_ExceptionMatches(PyExc_TypeError)) {
return adapted;
}
else {
PyErr_Clear();
} }
} }
if (alt) {
Py_INCREF(alt);
return alt;
}
/* else set the right exception and return NULL */ /* else set the right exception and return NULL */
PyErr_SetString(pysqlite_ProgrammingError, "can't adapt"); PyErr_SetString(pysqlite_ProgrammingError, "can't adapt");
return NULL; return NULL;
......
...@@ -250,12 +250,10 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para ...@@ -250,12 +250,10 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para
if (!_need_adapt(current_param)) { if (!_need_adapt(current_param)) {
adapted = current_param; adapted = current_param;
} else { } else {
adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, NULL); adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, current_param);
if (adapted) { Py_DECREF(current_param);
Py_DECREF(current_param); if (!adapted) {
} else { return;
PyErr_Clear();
adapted = current_param;
} }
} }
...@@ -272,6 +270,7 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para ...@@ -272,6 +270,7 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para
} else if (PyDict_Check(parameters)) { } else if (PyDict_Check(parameters)) {
/* parameters passed as dictionary */ /* parameters passed as dictionary */
for (i = 1; i <= num_params_needed; i++) { for (i = 1; i <= num_params_needed; i++) {
PyObject *binding_name_obj;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
binding_name = sqlite3_bind_parameter_name(self->st, i); binding_name = sqlite3_bind_parameter_name(self->st, i);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
...@@ -281,26 +280,31 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para ...@@ -281,26 +280,31 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para
} }
binding_name++; /* skip first char (the colon) */ binding_name++; /* skip first char (the colon) */
binding_name_obj = PyUnicode_FromString(binding_name);
if (!binding_name_obj) {
return;
}
if (PyDict_CheckExact(parameters)) { if (PyDict_CheckExact(parameters)) {
current_param = PyDict_GetItemString(parameters, binding_name); current_param = PyDict_GetItemWithError(parameters, binding_name_obj);
Py_XINCREF(current_param); Py_XINCREF(current_param);
} else { } else {
current_param = PyMapping_GetItemString(parameters, binding_name); current_param = PyObject_GetItem(parameters, binding_name_obj);
} }
Py_DECREF(binding_name_obj);
if (!current_param) { if (!current_param) {
PyErr_Format(pysqlite_ProgrammingError, "You did not supply a value for binding %d.", i); if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_LookupError)) {
PyErr_Format(pysqlite_ProgrammingError, "You did not supply a value for binding %d.", i);
}
return; return;
} }
if (!_need_adapt(current_param)) { if (!_need_adapt(current_param)) {
adapted = current_param; adapted = current_param;
} else { } else {
adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, NULL); adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, current_param);
if (adapted) { Py_DECREF(current_param);
Py_DECREF(current_param); if (!adapted) {
} else { return;
PyErr_Clear();
adapted = current_param;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment