mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-07-27 13:01:19 +00:00

Instead of OperationalException and IntegrityException will be raised for the following eror types: ER_BAD_NULL_ERROR, ER_DATA_OUT_OF_RANGE, ER_CONSTRAINT_FAILED, ER_DUP_CONSTRAINT_NAME
221 lines
7.3 KiB
C
221 lines
7.3 KiB
C
/************************************************************************************
|
|
Copyright (C) 2018 Georg Richter and MariaDB Corporation AB
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
|
or write to the Free Software Foundation, Inc.,
|
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
|
*************************************************************************************/
|
|
|
|
#include <mariadb_python.h>
|
|
#include <mysqld_error.h>
|
|
|
|
/* Exceptions */
|
|
PyObject *Mariadb_InterfaceError;
|
|
PyObject *Mariadb_Error;
|
|
PyObject *Mariadb_DatabaseError;
|
|
PyObject *Mariadb_DataError;
|
|
PyObject *Mariadb_PoolError;
|
|
PyObject *Mariadb_OperationalError;
|
|
PyObject *Mariadb_IntegrityError;
|
|
PyObject *Mariadb_InternalError;
|
|
PyObject *Mariadb_ProgrammingError;
|
|
PyObject *Mariadb_NotSupportedError;
|
|
PyObject *Mariadb_Warning;
|
|
|
|
struct st_error_map {
|
|
char sqlstate[3];
|
|
uint8_t type;
|
|
};
|
|
|
|
static PyObject *get_exception_type(int error_number)
|
|
{
|
|
/* 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:
|
|
case CR_SERVER_LOST:
|
|
case CR_SERVER_GONE_ERROR:
|
|
case CR_SERVER_HANDSHAKE_ERR:
|
|
case CR_IPSOCK_ERROR:
|
|
case CR_COMMANDS_OUT_OF_SYNC:
|
|
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;
|
|
|
|
/* 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 ER_EMPTY_QUERY:
|
|
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:
|
|
case ER_BAD_DB_ERROR:
|
|
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:
|
|
case ER_XAER_OUTSIDE:
|
|
case ER_XAER_RMERR:
|
|
case ER_BAD_NULL_ERROR:
|
|
case ER_DATA_OUT_OF_RANGE:
|
|
case ER_CONSTRAINT_FAILED:
|
|
case ER_DUP_CONSTRAINT_NAME:
|
|
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;
|
|
}
|
|
|
|
void mariadb_exception_connection_gone(PyObject *exception_type,
|
|
int error_no,
|
|
const char *message,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
PyObject *ErrorMsg= 0;
|
|
PyObject *ErrorNo= 0;
|
|
PyObject *SqlState= 0;
|
|
PyObject *Exception= 0;
|
|
|
|
|
|
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(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_errno((MYSQL_STMT*) handle) : mysql_errno((MYSQL *)handle));
|
|
|
|
if (!exception_type)
|
|
exception_type= Mariadb_DatabaseError;
|
|
|
|
ErrorNo= PyLong_FromLong(is_statement ?
|
|
mysql_stmt_errno((MYSQL_STMT *)handle) : mysql_errno((MYSQL *)handle));
|
|
ErrorMsg= PyUnicode_FromString(is_statement ?
|
|
mysql_stmt_error((MYSQL_STMT *)handle) : mysql_error((MYSQL *)handle));
|
|
SqlState= PyUnicode_FromString(is_statement ?
|
|
mysql_stmt_sqlstate((MYSQL_STMT *)handle) : mysql_sqlstate((MYSQL *)handle));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|