mirror of
https://github.com/python/cpython.git
synced 2024-12-01 05:45:40 +08:00
gh-89022: Improve sqlite3 exceptions related to binding params and API misuse (#91572)
* Map SQLITE_MISUSE to sqlite3.InterfaceError SQLITE_MISUSE implies misuse of the SQLite C API, which, if it happens, is _not_ a user error; it is an sqlite3 extension module error. * Raise better errors when binding parameters fail. Instead of always raising InterfaceError, guessing what went wrong, raise accurate exceptions with more accurate error messages.
This commit is contained in:
parent
d716a0dfe2
commit
090819ec5f
@ -743,7 +743,7 @@ class CursorTests(unittest.TestCase):
|
||||
self.assertEqual(row[0], "Hu\x00go")
|
||||
|
||||
def test_execute_non_iterable(self):
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
with self.assertRaises(sqlite.ProgrammingError) as cm:
|
||||
self.cu.execute("insert into test(id) values (?)", 42)
|
||||
self.assertEqual(str(cm.exception), 'parameters are of unsupported type')
|
||||
|
||||
|
@ -255,9 +255,9 @@ class DeclTypesTests(unittest.TestCase):
|
||||
|
||||
def test_error_in_conform(self):
|
||||
val = DeclTypesTests.BadConform(TypeError)
|
||||
with self.assertRaises(sqlite.InterfaceError):
|
||||
with self.assertRaises(sqlite.ProgrammingError):
|
||||
self.cur.execute("insert into test(bad) values (?)", (val,))
|
||||
with self.assertRaises(sqlite.InterfaceError):
|
||||
with self.assertRaises(sqlite.ProgrammingError):
|
||||
self.cur.execute("insert into test(bad) values (:val)", {"val": val})
|
||||
|
||||
val = DeclTypesTests.BadConform(KeyboardInterrupt)
|
||||
@ -269,13 +269,13 @@ class DeclTypesTests(unittest.TestCase):
|
||||
def test_unsupported_seq(self):
|
||||
class Bar: pass
|
||||
val = Bar()
|
||||
with self.assertRaises(sqlite.InterfaceError):
|
||||
with self.assertRaises(sqlite.ProgrammingError):
|
||||
self.cur.execute("insert into test(f) values (?)", (val,))
|
||||
|
||||
def test_unsupported_dict(self):
|
||||
class Bar: pass
|
||||
val = Bar()
|
||||
with self.assertRaises(sqlite.InterfaceError):
|
||||
with self.assertRaises(sqlite.ProgrammingError):
|
||||
self.cur.execute("insert into test(f) values (:val)", {"val": val})
|
||||
|
||||
def test_blob(self):
|
||||
|
@ -0,0 +1,4 @@
|
||||
In :mod:`sqlite3`, ``SQLITE_MISUSE`` result codes are now mapped to
|
||||
:exc:`~sqlite3.InterfaceError` instead of :exc:`~sqlite3.ProgrammingError`.
|
||||
Also, more accurate exceptions are raised when binding parameters fail.
|
||||
Patch by Erlend E. Aasland.
|
@ -527,7 +527,8 @@ stmt_step(sqlite3_stmt *statement)
|
||||
}
|
||||
|
||||
static int
|
||||
bind_param(pysqlite_Statement *self, int pos, PyObject *parameter)
|
||||
bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
|
||||
PyObject *parameter)
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
const char *string;
|
||||
@ -603,6 +604,9 @@ bind_param(pysqlite_Statement *self, int pos, PyObject *parameter)
|
||||
break;
|
||||
}
|
||||
case TYPE_UNKNOWN:
|
||||
PyErr_Format(state->ProgrammingError,
|
||||
"Error binding parameter %d: type '%s' is not supported",
|
||||
pos, Py_TYPE(parameter)->tp_name);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
@ -688,15 +692,15 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
|
||||
}
|
||||
}
|
||||
|
||||
rc = bind_param(self, i + 1, adapted);
|
||||
rc = bind_param(state, self, i + 1, adapted);
|
||||
Py_DECREF(adapted);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_Format(state->InterfaceError,
|
||||
"Error binding parameter %d - "
|
||||
"probably unsupported type.", i);
|
||||
}
|
||||
PyObject *exc, *val, *tb;
|
||||
PyErr_Fetch(&exc, &val, &tb);
|
||||
sqlite3 *db = sqlite3_db_handle(self->st);
|
||||
_pysqlite_seterror(state, db);
|
||||
_PyErr_ChainExceptions(exc, val, tb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -748,20 +752,21 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
|
||||
}
|
||||
}
|
||||
|
||||
rc = bind_param(self, i, adapted);
|
||||
rc = bind_param(state, self, i, adapted);
|
||||
Py_DECREF(adapted);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_Format(state->InterfaceError,
|
||||
"Error binding parameter :%s - "
|
||||
"probably unsupported type.", binding_name);
|
||||
}
|
||||
PyObject *exc, *val, *tb;
|
||||
PyErr_Fetch(&exc, &val, &tb);
|
||||
sqlite3 *db = sqlite3_db_handle(self->st);
|
||||
_pysqlite_seterror(state, db);
|
||||
_PyErr_ChainExceptions(exc, val, tb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type");
|
||||
PyErr_SetString(state->ProgrammingError,
|
||||
"parameters are of unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,6 @@ get_exception_class(pysqlite_state *state, int errorcode)
|
||||
case SQLITE_MISMATCH:
|
||||
return state->IntegrityError;
|
||||
case SQLITE_MISUSE:
|
||||
return state->ProgrammingError;
|
||||
case SQLITE_RANGE:
|
||||
return state->InterfaceError;
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user