mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
bpo-16379: expose SQLite error codes and error names in sqlite3 (GH-27786)
This commit is contained in:
parent
f62763d267
commit
86d8b46523
8 changed files with 264 additions and 32 deletions
|
|
@ -282,12 +282,79 @@ static PyMethodDef module_methods[] = {
|
|||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* SQLite API error codes */
|
||||
static const struct {
|
||||
const char *name;
|
||||
long value;
|
||||
} error_codes[] = {
|
||||
#define DECLARE_ERROR_CODE(code) {#code, code}
|
||||
// Primary result code list
|
||||
DECLARE_ERROR_CODE(SQLITE_ABORT),
|
||||
DECLARE_ERROR_CODE(SQLITE_AUTH),
|
||||
DECLARE_ERROR_CODE(SQLITE_BUSY),
|
||||
DECLARE_ERROR_CODE(SQLITE_CANTOPEN),
|
||||
DECLARE_ERROR_CODE(SQLITE_CONSTRAINT),
|
||||
DECLARE_ERROR_CODE(SQLITE_CORRUPT),
|
||||
DECLARE_ERROR_CODE(SQLITE_DONE),
|
||||
DECLARE_ERROR_CODE(SQLITE_EMPTY),
|
||||
DECLARE_ERROR_CODE(SQLITE_ERROR),
|
||||
DECLARE_ERROR_CODE(SQLITE_FORMAT),
|
||||
DECLARE_ERROR_CODE(SQLITE_FULL),
|
||||
DECLARE_ERROR_CODE(SQLITE_INTERNAL),
|
||||
DECLARE_ERROR_CODE(SQLITE_INTERRUPT),
|
||||
DECLARE_ERROR_CODE(SQLITE_IOERR),
|
||||
DECLARE_ERROR_CODE(SQLITE_LOCKED),
|
||||
DECLARE_ERROR_CODE(SQLITE_MISMATCH),
|
||||
DECLARE_ERROR_CODE(SQLITE_MISUSE),
|
||||
DECLARE_ERROR_CODE(SQLITE_NOLFS),
|
||||
DECLARE_ERROR_CODE(SQLITE_NOMEM),
|
||||
DECLARE_ERROR_CODE(SQLITE_NOTADB),
|
||||
DECLARE_ERROR_CODE(SQLITE_NOTFOUND),
|
||||
DECLARE_ERROR_CODE(SQLITE_OK),
|
||||
DECLARE_ERROR_CODE(SQLITE_PERM),
|
||||
DECLARE_ERROR_CODE(SQLITE_PROTOCOL),
|
||||
DECLARE_ERROR_CODE(SQLITE_READONLY),
|
||||
DECLARE_ERROR_CODE(SQLITE_ROW),
|
||||
DECLARE_ERROR_CODE(SQLITE_SCHEMA),
|
||||
DECLARE_ERROR_CODE(SQLITE_TOOBIG),
|
||||
#if SQLITE_VERSION_NUMBER >= 3007017
|
||||
DECLARE_ERROR_CODE(SQLITE_NOTICE),
|
||||
DECLARE_ERROR_CODE(SQLITE_WARNING),
|
||||
#endif
|
||||
#undef DECLARE_ERROR_CODE
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static int
|
||||
add_error_constants(PyObject *module)
|
||||
{
|
||||
for (int i = 0; error_codes[i].name != NULL; i++) {
|
||||
const char *name = error_codes[i].name;
|
||||
const long value = error_codes[i].value;
|
||||
if (PyModule_AddIntConstant(module, name, value) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *
|
||||
pysqlite_error_name(int rc)
|
||||
{
|
||||
for (int i = 0; error_codes[i].name != NULL; i++) {
|
||||
if (error_codes[i].value == rc) {
|
||||
return error_codes[i].name;
|
||||
}
|
||||
}
|
||||
// No error code matched.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int add_integer_constants(PyObject *module) {
|
||||
int ret = 0;
|
||||
|
||||
ret += PyModule_AddIntMacro(module, PARSE_DECLTYPES);
|
||||
ret += PyModule_AddIntMacro(module, PARSE_COLNAMES);
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_OK);
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_DENY);
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_IGNORE);
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_CREATE_INDEX);
|
||||
|
|
@ -325,7 +392,6 @@ static int add_integer_constants(PyObject *module) {
|
|||
#if SQLITE_VERSION_NUMBER >= 3008003
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_RECURSIVE);
|
||||
#endif
|
||||
ret += PyModule_AddIntMacro(module, SQLITE_DONE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +472,11 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
|
|||
ADD_EXCEPTION(module, state, DataError, state->DatabaseError);
|
||||
ADD_EXCEPTION(module, state, NotSupportedError, state->DatabaseError);
|
||||
|
||||
/* Set error constants */
|
||||
if (add_error_constants(module) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set integer constants */
|
||||
if (add_integer_constants(module) < 0) {
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ pysqlite_get_state_by_type(PyTypeObject *Py_UNUSED(tp))
|
|||
return &pysqlite_global_state;
|
||||
}
|
||||
|
||||
extern const char *pysqlite_error_name(int rc);
|
||||
|
||||
#define PARSE_DECLTYPES 1
|
||||
#define PARSE_COLNAMES 2
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -36,27 +36,19 @@ pysqlite_step(sqlite3_stmt *statement)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the SQLite error code and sets the appropriate DB-API exception.
|
||||
* Returns the error code (0 means no error occurred).
|
||||
*/
|
||||
int
|
||||
_pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
|
||||
// Returns non-NULL if a new exception should be raised
|
||||
static PyObject *
|
||||
get_exception_class(pysqlite_state *state, int errorcode)
|
||||
{
|
||||
int errorcode = sqlite3_errcode(db);
|
||||
|
||||
switch (errorcode)
|
||||
{
|
||||
switch (errorcode) {
|
||||
case SQLITE_OK:
|
||||
PyErr_Clear();
|
||||
break;
|
||||
return NULL;
|
||||
case SQLITE_INTERNAL:
|
||||
case SQLITE_NOTFOUND:
|
||||
PyErr_SetString(state->InternalError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->InternalError;
|
||||
case SQLITE_NOMEM:
|
||||
(void)PyErr_NoMemory();
|
||||
break;
|
||||
return PyErr_NoMemory();
|
||||
case SQLITE_ERROR:
|
||||
case SQLITE_PERM:
|
||||
case SQLITE_ABORT:
|
||||
|
|
@ -70,26 +62,85 @@ _pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
|
|||
case SQLITE_PROTOCOL:
|
||||
case SQLITE_EMPTY:
|
||||
case SQLITE_SCHEMA:
|
||||
PyErr_SetString(state->OperationalError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->OperationalError;
|
||||
case SQLITE_CORRUPT:
|
||||
PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->DatabaseError;
|
||||
case SQLITE_TOOBIG:
|
||||
PyErr_SetString(state->DataError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->DataError;
|
||||
case SQLITE_CONSTRAINT:
|
||||
case SQLITE_MISMATCH:
|
||||
PyErr_SetString(state->IntegrityError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->IntegrityError;
|
||||
case SQLITE_MISUSE:
|
||||
PyErr_SetString(state->ProgrammingError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->ProgrammingError;
|
||||
default:
|
||||
PyErr_SetString(state->DatabaseError, sqlite3_errmsg(db));
|
||||
break;
|
||||
return state->DatabaseError;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
raise_exception(PyObject *type, int errcode, const char *errmsg)
|
||||
{
|
||||
PyObject *exc = NULL;
|
||||
PyObject *args[] = { PyUnicode_FromString(errmsg), };
|
||||
if (args[0] == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
exc = PyObject_Vectorcall(type, args, 1, NULL);
|
||||
Py_DECREF(args[0]);
|
||||
if (exc == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
PyObject *code = PyLong_FromLong(errcode);
|
||||
if (code == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
int rc = PyObject_SetAttrString(exc, "sqlite_errorcode", code);
|
||||
Py_DECREF(code);
|
||||
if (rc < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
const char *error_name = pysqlite_error_name(errcode);
|
||||
PyObject *name;
|
||||
if (error_name) {
|
||||
name = PyUnicode_FromString(error_name);
|
||||
}
|
||||
else {
|
||||
name = PyUnicode_InternFromString("unknown");
|
||||
}
|
||||
if (name == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
rc = PyObject_SetAttrString(exc, "sqlite_errorname", name);
|
||||
Py_DECREF(name);
|
||||
if (rc < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
PyErr_SetObject(type, exc);
|
||||
|
||||
exit:
|
||||
Py_XDECREF(exc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the SQLite error code and sets the appropriate DB-API exception.
|
||||
* Returns the error code (0 means no error occurred).
|
||||
*/
|
||||
int
|
||||
_pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
|
||||
{
|
||||
int errorcode = sqlite3_errcode(db);
|
||||
PyObject *exc_class = get_exception_class(state, errorcode);
|
||||
if (exc_class == NULL) {
|
||||
// No new exception need be raised; just pass the error code
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
/* Create and set the exception. */
|
||||
const char *errmsg = sqlite3_errmsg(db);
|
||||
raise_exception(exc_class, errorcode, errmsg);
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue