/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version. Alternatively, you may use the original license
reproduced below.
Copyright 1999 by Comstar.net, Inc., Atlanta, GA, US.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Comstar.net, Inc.
or COMSTAR not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
COMSTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
EVENT SHALL COMSTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "mysql.h"
#include "mysqld_error.h"
#if MYSQL_VERSION_ID >= 80000
// https://github.com/mysql/mysql-server/commit/eb821c023cedc029ca0b06456dfae365106bee84
#define my_bool _Bool
#endif
#if ((MYSQL_VERSION_ID >= 50555 && MYSQL_VERSION_ID <= 50599) || \
(MYSQL_VERSION_ID >= 50636 && MYSQL_VERSION_ID <= 50699) || \
(MYSQL_VERSION_ID >= 50711 && MYSQL_VERSION_ID <= 50799) || \
(MYSQL_VERSION_ID >= 80000)) && \
!defined(MARIADB_BASE_VERSION) && !defined(MARIADB_VERSION_ID)
#define HAVE_ENUM_MYSQL_OPT_SSL_MODE
#endif
#define PY_SSIZE_T_CLEAN 1
#include "Python.h"
#if PY_MAJOR_VERSION == 2
#error "Python 2 is not supported"
#endif
#include "bytesobject.h"
#include "structmember.h"
#include "errmsg.h"
#define MyAlloc(s,t) (s *) t.tp_alloc(&t,0)
#define MyFree(o) Py_TYPE(o)->tp_free((PyObject*)o)
static PyObject *_mysql_MySQLError;
static PyObject *_mysql_Warning;
static PyObject *_mysql_Error;
static PyObject *_mysql_DatabaseError;
static PyObject *_mysql_InterfaceError;
static PyObject *_mysql_DataError;
static PyObject *_mysql_OperationalError;
static PyObject *_mysql_IntegrityError;
static PyObject *_mysql_InternalError;
static PyObject *_mysql_ProgrammingError;
static PyObject *_mysql_NotSupportedError;
typedef struct {
PyObject_HEAD
MYSQL connection;
int open;
PyObject *converter;
} _mysql_ConnectionObject;
#define check_connection(c) \
if (!(c->open)) { \
return _mysql_Exception(c); \
};
#define result_connection(r) ((_mysql_ConnectionObject *)r->conn)
#define check_result_connection(r) check_connection(result_connection(r))
extern PyTypeObject _mysql_ConnectionObject_Type;
typedef struct {
PyObject_HEAD
PyObject *conn;
MYSQL_RES *result;
int nfields;
int use;
char has_next;
PyObject *converter;
const char *encoding;
} _mysql_ResultObject;
extern PyTypeObject _mysql_ResultObject_Type;
PyObject *
_mysql_Exception(_mysql_ConnectionObject *c)
{
PyObject *t, *e;
int merr;
if (!(t = PyTuple_New(2))) return NULL;
if (!(c->open)) {
/* GH-270: When connection is closed, accessing the c->connection
* object may cause SEGV.
*/
merr = CR_SERVER_GONE_ERROR;
}
else {
merr = mysql_errno(&(c->connection));
}
switch (merr) {
case 0:
e = _mysql_InterfaceError;
break;
case CR_COMMANDS_OUT_OF_SYNC:
case ER_DB_CREATE_EXISTS:
case ER_SYNTAX_ERROR:
case ER_PARSE_ERROR:
case ER_NO_SUCH_TABLE:
case ER_WRONG_DB_NAME:
case ER_WRONG_TABLE_NAME:
case ER_FIELD_SPECIFIED_TWICE:
case ER_INVALID_GROUP_FUNC_USE:
case ER_UNSUPPORTED_EXTENSION:
case ER_TABLE_MUST_HAVE_COLUMNS:
#ifdef ER_CANT_DO_THIS_DURING_AN_TRANSACTION
case ER_CANT_DO_THIS_DURING_AN_TRANSACTION:
#endif
e = _mysql_ProgrammingError;
break;
#ifdef WARN_DATA_TRUNCATED
case WARN_DATA_TRUNCATED:
#ifdef WARN_NULL_TO_NOTNULL
case WARN_NULL_TO_NOTNULL:
#endif
#ifdef ER_WARN_DATA_OUT_OF_RANGE
case ER_WARN_DATA_OUT_OF_RANGE:
#endif
#ifdef ER_NO_DEFAULT
case ER_NO_DEFAULT:
#endif
#ifdef ER_PRIMARY_CANT_HAVE_NULL
case ER_PRIMARY_CANT_HAVE_NULL:
#endif
#ifdef ER_DATA_TOO_LONG
case ER_DATA_TOO_LONG:
#endif
#ifdef ER_DATETIME_FUNCTION_OVERFLOW
case ER_DATETIME_FUNCTION_OVERFLOW:
#endif
e = _mysql_DataError;
break;
#endif
case ER_DUP_ENTRY:
#ifdef ER_DUP_UNIQUE
case ER_DUP_UNIQUE:
#endif
#ifdef ER_NO_REFERENCED_ROW
case ER_NO_REFERENCED_ROW:
#endif
#ifdef ER_NO_REFERENCED_ROW_2
case ER_NO_REFERENCED_ROW_2:
#endif
#ifdef ER_ROW_IS_REFERENCED
case ER_ROW_IS_REFERENCED:
#endif
#ifdef ER_ROW_IS_REFERENCED_2
case ER_ROW_IS_REFERENCED_2:
#endif
#ifdef ER_CANNOT_ADD_FOREIGN
case ER_CANNOT_ADD_FOREIGN:
#endif
#ifdef ER_NO_DEFAULT_FOR_FIELD
case ER_NO_DEFAULT_FOR_FIELD:
#endif
case ER_BAD_NULL_ERROR:
e = _mysql_IntegrityError;
break;
#ifdef ER_WARNING_NOT_COMPLETE_ROLLBACK
case ER_WARNING_NOT_COMPLETE_ROLLBACK:
#endif
#ifdef ER_NOT_SUPPORTED_YET
case ER_NOT_SUPPORTED_YET:
#endif
#ifdef ER_FEATURE_DISABLED
case ER_FEATURE_DISABLED:
#endif
#ifdef ER_UNKNOWN_STORAGE_ENGINE
case ER_UNKNOWN_STORAGE_ENGINE:
#endif
e = _mysql_NotSupportedError;
break;
default:
if (merr < 1000)
e = _mysql_InternalError;
else
e = _mysql_OperationalError;
break;
}
PyTuple_SET_ITEM(t, 0, PyLong_FromLong((long)merr));
PyTuple_SET_ITEM(t, 1, PyUnicode_FromString(mysql_error(&(c->connection))));
PyErr_SetObject(e, t);
Py_DECREF(t);
return NULL;
}
static const char *utf8 = "utf8";
static const char*
_get_encoding(MYSQL *mysql)
{
MY_CHARSET_INFO cs;
mysql_get_character_set_info(mysql, &cs);
if (strncmp(utf8, cs.csname, 4) == 0) { // utf8, utf8mb3, utf8mb4
return utf8;
}
else if (strncmp("latin1", cs.csname, 6) == 0) {
return "cp1252";
}
else if (strncmp("koi8r", cs.csname, 5) == 0) {
return "koi8_r";
}
else if (strncmp("koi8u", cs.csname, 5) == 0) {
return "koi8_u";
}
return cs.csname;
}
static char _mysql_ResultObject__doc__[] =
"result(connection, use=0, converter={}) -- Result set from a query.\n\
\n\
Creating instances of this class directly is an excellent way to\n\
shoot yourself in the foot. If using _mysql.connection directly,\n\
use connection.store_result() or connection.use_result() instead.\n\
If using MySQLdb.Connection, this is done by the cursor class.\n\
Just forget you ever saw this. Forget... FOR-GET...";
static int
_mysql_ResultObject_Initialize(
_mysql_ResultObject *self,
PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"connection", "use", "converter", NULL};
MYSQL_RES *result;
_mysql_ConnectionObject *conn=NULL;
int use=0;
PyObject *conv=NULL;
int n, i;
MYSQL_FIELD *fields;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|iO", kwlist,
&_mysql_ConnectionObject_Type, &conn, &use, &conv))
return -1;
self->conn = (PyObject *) conn;
Py_INCREF(conn);
self->use = use;
Py_BEGIN_ALLOW_THREADS ;
if (use)
result = mysql_use_result(&(conn->connection));
else
result = mysql_store_result(&(conn->connection));
self->result = result;
self->has_next = (char)mysql_more