Fix for CONPY-106

Check error number instead of SQL code for determine exception type.
This commit is contained in:
Georg Richter
2020-08-14 14:44:05 +02:00
parent 588bc01cc0
commit e091edd549
2 changed files with 101 additions and 57 deletions

View File

@ -252,9 +252,9 @@ typedef struct {
unsigned long prefetch_rows;
unsigned long cursor_type;
int64_t affected_rows;
int64_t row_count;
uint32_t field_count;
uint64_t lastrowid;
int64_t row_count;
uint64_t lastrow_id;
unsigned long row_number;
enum enum_result_format result_format;
uint8_t is_prepared;
@ -319,7 +319,7 @@ int Mariadb_traverse(PyObject *self,
void
mariadb_throw_exception(void *handle,
PyObject *execption_type,
unsigned char is_statement,
int8_t is_statement,
const char *message,
...);

View File

@ -18,6 +18,7 @@
*************************************************************************************/
#include <mariadb_python.h>
#include <mysqld_error.h>
/* Exceptions */
PyObject *Mariadb_InterfaceError;
@ -37,77 +38,70 @@ struct st_error_map {
uint8_t type;
};
static PyObject *get_exception_type(const char *sqlstate)
static PyObject *get_exception_type(int error_number)
{
if (!sqlstate || strlen(sqlstate) != 5)
return NULL;
if (!strncmp(sqlstate, "21", 2) ||
!strncmp(sqlstate, "22", 2) ||
!strncmp(sqlstate, "02", 2))
/* This list might be incomplete, special error values which are
not handled yet will be returned as Internal or Operational errors.
error codes are defined in errmsg.h (client errors) and mysqld_error.h
(server errors) */
switch (error_number) {
/* InterfaceError */
case 0:
return Mariadb_InterfaceError;
/* DataError: Exception raised for errors that are due to problems with the processed
data like division by zero, numeric value out of range, etc */
case ER_DATA_TOO_LONG:
case ER_DATETIME_FUNCTION_OVERFLOW:
case ER_DIVISION_BY_ZERO:
case ER_NO_DEFAULT:
case ER_PRIMARY_CANT_HAVE_NULL:
case ER_WARN_DATA_OUT_OF_RANGE:
case WARN_DATA_TRUNCATED:
return Mariadb_DataError;
if (!strncmp(sqlstate, "07", 2) ||
!strncmp(sqlstate, "2B", 2) ||
!strncmp(sqlstate, "2D", 2) ||
!strncmp(sqlstate, "33", 2) ||
!strncmp(sqlstate, "HY", 2))
return Mariadb_DatabaseError;
if (!strncmp(sqlstate, "23", 2) ||
!strncmp(sqlstate, "XA", 2))
return Mariadb_IntegrityError;
if (!strncmp(sqlstate, "0A", 2))
return Mariadb_NotSupportedError;
if (!strncmp(sqlstate, "40", 2) ||
!strncmp(sqlstate, "44", 2))
return Mariadb_InternalError;
if (!strncmp(sqlstate, "0K", 2) ||
!strncmp(sqlstate, "08", 2) ||
!strncmp(sqlstate, "HZ", 2))
return Mariadb_OperationalError;
if (!strncmp(sqlstate, "24", 2) ||
!strncmp(sqlstate, "25", 2) ||
!strncmp(sqlstate, "26", 2) ||
!strncmp(sqlstate, "27", 2) ||
!strncmp(sqlstate, "28", 2) ||
!strncmp(sqlstate, "2A", 2) ||
!strncmp(sqlstate, "2C", 2) ||
!strncmp(sqlstate, "2F", 2) ||
!strncmp(sqlstate, "34", 2) ||
!strncmp(sqlstate, "35", 2) ||
!strncmp(sqlstate, "3C", 2) ||
!strncmp(sqlstate, "3D", 2) ||
!strncmp(sqlstate, "3F", 2) ||
!strncmp(sqlstate, "37", 2) ||
!strncmp(sqlstate, "42", 2) ||
!strncmp(sqlstate, "70", 2))
/* ProgrammingError: Exception raised for programming errors, e.g. table not found or
already exists, syntax error in the SQL statement, wrong number of parameters specified, etc. */
case CR_COMMANDS_OUT_OF_SYNC:
case ER_CANT_DO_THIS_DURING_AN_TRANSACTION:
case ER_DB_CREATE_EXISTS:
case ER_FIELD_SPECIFIED_TWICE:
case ER_INVALID_GROUP_FUNC_USE:
case ER_NO_SUCH_INDEX:
case ER_NO_SUCH_KEY_VALUE:
case ER_NO_SUCH_TABLE:
case ER_NO_SUCH_USER:
case ER_PARSE_ERROR:
case ER_SYNTAX_ERROR:
case ER_TABLE_MUST_HAVE_COLUMNS:
case ER_UNSUPPORTED_EXTENSION:
case ER_WRONG_DB_NAME:
case ER_WRONG_TABLE_NAME:
return Mariadb_ProgrammingError;
/* IntegrityError: Exception raised when the relational integrity of the database is affected,
e.g. a foreign key check fails */
case ER_CANNOT_ADD_FOREIGN:
case ER_DUP_ENTRY:
case ER_DUP_UNIQUE:
case ER_NO_DEFAULT_FOR_FIELD:
case ER_NO_REFERENCED_ROW:
case ER_NO_REFERENCED_ROW_2:
case ER_ROW_IS_REFERENCED:
case ER_ROW_IS_REFERENCED_2:
return Mariadb_IntegrityError;
default:
/* MariaDB Error */
if (error_number >= 1000)
return Mariadb_OperationalError;
/* same behavior as in MySQLdb: we return an InternalError, in case of system errors */
return Mariadb_InternalError;
}
return NULL;
}
/**
mariadb_throw_exception()
@brief raises an exception
@param handle[in] a connection or statement handle
@param exception_type[in] type of exception
@param is_statement[in] 1 is handle is a MYSQL_STMT handle
@param message[in] Error message. If message is NULL, the error
message will be retrieved from specified handle.
@param ... [in] message parameter
@return void
*/
void mariadb_throw_exception(void *handle,
PyObject *exception_type,
unsigned char is_statement,
void mariadb_exception_connection_gone(PyObject *exception_type,
int error_no,
const char *message,
...)
{
@ -117,19 +111,69 @@ void mariadb_throw_exception(void *handle,
PyObject *SqlState= 0;
PyObject *Exception= 0;
//if (!exception_type)
// exception_type= Mariadb_InterfaceError;
ErrorNo= PyLong_FromLong(CR_UNKNOWN_ERROR);
SqlState= PyUnicode_FromString("HY000");
va_start(ap, message);
ErrorMsg= PyUnicode_FromFormatV(message, ap);
va_end(ap);
if (!(Exception= PyObject_CallFunctionObjArgs(exception_type, ErrorMsg, NULL)))
{
PyErr_SetString(PyExc_RuntimeError,
"Failed to create exception");
return;
}
PyObject_SetAttr(Exception, PyUnicode_FromString("sqlstate"), SqlState);
PyObject_SetAttr(Exception, PyUnicode_FromString("errno"), ErrorNo);
PyObject_SetAttr(Exception, PyUnicode_FromString("errmsg"), ErrorMsg);
/* For MySQL Connector/Python compatibility */
PyObject_SetAttr(Exception, PyUnicode_FromString("msg"), ErrorMsg);
PyErr_SetObject(exception_type, Exception);
Py_XDECREF(ErrorMsg);
Py_XDECREF(ErrorNo);
Py_XDECREF(SqlState);
}
/**
mariadb_throw_exception()
@brief raises an exception
@param handle[in] a connection or statement handle
@param exception_type[in] type of exception
@param handle_type[in] -1 no handle (use error_no)
0 MYSQL
1 MYSQL_STMT
@param message[in] Error message. If message is NULL, the error
message will be retrieved from specified handle.
@param ... [in] message parameter
@return void
*/
void mariadb_throw_exception(void *handle,
PyObject *exception_type,
int8_t is_statement,
const char *message,
...)
{
va_list ap;
PyObject *ErrorMsg= 0;
PyObject *ErrorNo= 0;
PyObject *SqlState= 0;
PyObject *Exception= 0;
if (message)
{
ErrorNo= PyLong_FromLong(-1);
ErrorNo= PyLong_FromLong(CR_UNKNOWN_ERROR);
SqlState= PyUnicode_FromString("HY000");
va_start(ap, message);
ErrorMsg= PyUnicode_FromFormatV(message, ap);
va_end(ap);
} else
{
exception_type= get_exception_type(is_statement ? mysql_stmt_sqlstate((MYSQL_STMT*) handle) : mysql_sqlstate((MYSQL *)handle));
exception_type= get_exception_type(is_statement ? mysql_stmt_errno((MYSQL_STMT*) handle) : mysql_errno((MYSQL *)handle));
if (!exception_type)
exception_type= Mariadb_DatabaseError;