/***************************************************************************** Copyright (C) 2018-2020 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 or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ****************************************************************************/ #include #include static void MrdbCursor_dealloc(MrdbCursor *self); static PyObject * MrdbCursor_close(MrdbCursor *self); static PyObject * MrdbCursor_nextset(MrdbCursor *self); static PyObject * MrdbCursor_execute_binary(MrdbCursor *self); static PyObject * MrdbCursor_InitResultSet(MrdbCursor *self); static PyObject * MrdbCursor_execute_text(MrdbCursor *self, PyObject *args); static PyObject * MrdbCursor_parse(MrdbCursor *self, PyObject *args); static PyObject * MrdbCursor_description(MrdbCursor *self); static PyObject * MrdbCursor_fetchone(MrdbCursor *self); static PyObject * MrdbCursor_seek(MrdbCursor *self, PyObject *args); static PyObject * MrdbCursor_execute_bulk(MrdbCursor *self); void field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column); static PyObject * MrdbCursor_readresponse(MrdbCursor *self); PyObject *MrdbCursor_clear_result(MrdbCursor *self); void field_fetch_callback(void *data, unsigned int column, unsigned char **row); static PyObject *mariadb_get_sequence_or_tuple(MrdbCursor *self); static PyObject * MrdbCursor_iter(PyObject *self); static PyObject * MrdbCursor_iternext(PyObject *self); /* todo: write more documentation, this is just a placeholder */ static char mariadb_cursor_documentation[] = "Returns a MariaDB cursor object"; #define CURSOR_SET_STATEMENT(a,s,l)\ MARIADB_FREE_MEM((a)->statement);\ (a)->statement= PyMem_RawMalloc((l)+ 1);\ strncpy((a)->statement, (s), (l));\ (a)->statement_len= (unsigned long)(l);\ (a)->statement[(l)]= 0; #define CURSOR_FIELD_COUNT(a)\ ((a)->parseinfo.is_text ? mysql_field_count((a)->connection->mysql) : (a)->stmt ? mysql_stmt_field_count((a)->stmt) : 0) #define CURSOR_WARNING_COUNT(a)\ (((a)->parseinfo.is_text) ? (long)mysql_warning_count((a)->connection->mysql) : ((a)->stmt) ? (long)mysql_stmt_warning_count((a)->stmt) : 0L) #define CURSOR_AFFECTED_ROWS(a)\ (int64_t)((a)->parseinfo.is_text ? mysql_affected_rows((a)->connection->mysql) : (a)->stmt ? mysql_stmt_affected_rows((a)->stmt) : 0) #define CURSOR_INSERT_ID(a)\ ((a)->parseinfo.is_text ? mysql_insert_id((a)->connection->mysql) : (a)->stmt ? mysql_stmt_insert_id((a)->stmt) : 0) #define CURSOR_NUM_ROWS(a)\ ((a)->parseinfo.is_text ? mysql_num_rows((a)->result) : (a)->stmt ? mysql_stmt_num_rows((a)->stmt) : 0) static char *mariadb_named_tuple_name= "mariadb.Row"; static char *mariadb_named_tuple_desc= "Named tupled row"; static PyObject *Mariadb_row_count(MrdbCursor *self); static PyObject *Mariadb_row_number(MrdbCursor *self); static PyObject *MrdbCursor_warnings(MrdbCursor *self); static PyObject *MrdbCursor_closed(MrdbCursor *self); static PyGetSetDef MrdbCursor_sets[]= { {"description", (getter)MrdbCursor_description, NULL, cursor_description__doc__, NULL}, {"rowcount", (getter)Mariadb_row_count, NULL, NULL, NULL}, {"warnings", (getter)MrdbCursor_warnings, NULL, cursor_warnings__doc__, NULL}, {"closed", (getter)MrdbCursor_closed, NULL, cursor_closed__doc__, NULL}, {"rownumber", (getter)Mariadb_row_number, NULL, cursor_rownumber__doc__, NULL}, {NULL} }; static PyMethodDef MrdbCursor_Methods[] = { /* PEP-249 methods */ {"close", (PyCFunction)MrdbCursor_close, METH_NOARGS, cursor_close__doc__}, {"fetchone", (PyCFunction)MrdbCursor_fetchone, METH_NOARGS, cursor_fetchone__doc__,}, {"_nextset", (PyCFunction)MrdbCursor_nextset, METH_NOARGS, cursor_nextset__doc__}, {"next", (PyCFunction)MrdbCursor_fetchone, METH_NOARGS, cursor_next__doc__}, /* internal helper functions */ {"_seek", (PyCFunction)MrdbCursor_seek, METH_VARARGS, NULL}, {"_initresult", (PyCFunction)MrdbCursor_InitResultSet, METH_NOARGS, NULL}, {"_parse", (PyCFunction)MrdbCursor_parse, METH_VARARGS, NULL}, {"_readresponse", (PyCFunction)MrdbCursor_readresponse, METH_NOARGS, NULL}, {"_execute_text", (PyCFunction)MrdbCursor_execute_text, METH_VARARGS, NULL}, {"_execute_binary", (PyCFunction)MrdbCursor_execute_binary, METH_NOARGS, NULL}, {"_execute_bulk", (PyCFunction)MrdbCursor_execute_bulk, METH_NOARGS, NULL}, {"_initresult", (PyCFunction)MrdbCursor_InitResultSet, METH_NOARGS, NULL}, {"_readresponse", (PyCFunction)MrdbCursor_readresponse, METH_NOARGS, NULL}, {"_clear_result", (PyCFunction)MrdbCursor_clear_result, METH_NOARGS, NULL}, {NULL} /* always last */ }; static struct PyMemberDef MrdbCursor_Members[] = { {"statement", T_STRING, offsetof(MrdbCursor, parseinfo.statement), READONLY, cursor_statement__doc__}, {"_paramstyle", T_UINT, offsetof(MrdbCursor, parseinfo.paramstyle), READONLY, MISSING_DOC}, {"_reprepare", T_UINT, offsetof(MrdbCursor, reprepare), 0, MISSING_DOC}, {"_command", T_BYTE, offsetof(MrdbCursor, parseinfo.command), 0, MISSING_DOC}, {"_text", T_BOOL, offsetof(MrdbCursor, parseinfo.is_text), 0, MISSING_DOC}, {"_paramlist", T_OBJECT, offsetof(MrdbCursor, parseinfo.paramlist), READONLY, MISSING_DOC}, {"_keys", T_OBJECT, offsetof(MrdbCursor, parseinfo.keys), READONLY, MISSING_DOC}, {"paramcount", T_UINT, offsetof(MrdbCursor, parseinfo.paramcount), READONLY, cursor_paramcount__doc__}, {"_data", T_OBJECT, offsetof(MrdbCursor, data), 0, MISSING_DOC}, {"_cursor_type", T_ULONG, offsetof(MrdbCursor, cursor_type), 0, MISSING_DOC}, {"buffered", T_BOOL, offsetof(MrdbCursor, is_buffered), 0, cursor_buffered__doc__}, {"arraysize", T_LONG, offsetof(MrdbCursor, row_array_size), 0, cursor_arraysize__doc__}, {"field_count", T_UINT, offsetof(MrdbCursor, field_count), READONLY, cursor_field_count__doc__}, {"affected_rows", T_ULONGLONG, offsetof(MrdbCursor, affected_rows), READONLY, "Number of affected rows"}, {"_rownumber", T_ULONGLONG, offsetof(MrdbCursor, row_number), 0, NULL}, {"insert_id", T_UINT, offsetof(MrdbCursor, lastrow_id), READONLY, "returns the ID generated by a query on a table with a column " \ "having the AUTO_INCREMENT attribute or the value for the last "\ "usage of LAST_INSERT_ID()"}, {NULL} }; /* {{{ MrdbCursor_initialize Cursor initialization Optional keywprds: named_tuple (Boolean): return rows as named tuple instead of tuple prefetch_size: Prefetch size for readonly cursors cursor_type: Type of cursor: CURSOR_TYPE_READONLY or CURSOR_TYPE_NONE (default) buffered: buffered or unbuffered result sets */ static int MrdbCursor_initialize(MrdbCursor *self, PyObject *args, PyObject *kwargs) { char *key_words[]= {"", "prefetch_size", "cursor_type", "prepared", "binary", NULL}; PyObject *connection; unsigned long cursor_type= 0, prefetch_rows= 0; uint8_t is_prepared= 0; uint8_t is_binary= 0; if (!self) return -1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|kkii", key_words, &MrdbConnection_Type, &connection, &prefetch_rows, &cursor_type, &is_prepared, &is_binary)) return -1; if (!((MrdbConnection *)connection)->mysql) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Connection isn't valid anymore"); return -1; } if (self->cursor_type != CURSOR_TYPE_READ_ONLY && self->cursor_type != CURSOR_TYPE_NO_CURSOR) { mariadb_throw_exception(NULL, Mariadb_DataError, 0, "Invalid value %ld for cursor_type", cursor_type); return -1; } self->connection= (MrdbConnection *)connection; self->is_prepared= is_prepared; self->parseinfo.is_text= 0; self->stmt= NULL; self->prefetch_rows= prefetch_rows; self->row_array_size= 1; return 0; } /* }}} */ static int MrdbCursor_traverse( MrdbCursor *self, visitproc visit, void *arg) { return 0; } static PyObject *MrdbCursor_repr(MrdbCursor *self) { char cobj_repr[384]; if (!self->closed) snprintf(cobj_repr, 384, "", self); else snprintf(cobj_repr, 384, "", self); return PyUnicode_FromString(cobj_repr); } PyTypeObject MrdbCursor_Type = { PyVarObject_HEAD_INIT(NULL, 0) "mariadb.cursor", sizeof(MrdbCursor), 0, (destructor)MrdbCursor_dealloc, /* tp_dealloc */ 0, /*tp_print*/ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* PyAsyncMethods * */ (reprfunc)MrdbCursor_repr, /* tp_repr */ /* Method suites for standard classes */ 0, /* (PyNumberMethods *) tp_as_number */ 0, /* (PySequenceMethods *) tp_as_sequence */ 0, /* (PyMappingMethods *) tp_as_mapping */ /* More standard operations (here for binary compatibility) */ 0, /* (hashfunc) tp_hash */ 0, /* (ternaryfunc) tp_call */ 0, /* (reprfunc) tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ /* Functions to access object as input/output buffer */ 0, /* (PyBufferProcs *) tp_as_buffer */ /* (tp_flags) Flags to define presence of optional/expanded features */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, mariadb_cursor_documentation, /* tp_doc Documentation string */ /* call function for all accessible objects */ (traverseproc)MrdbCursor_traverse,/* tp_traverse */ /* delete references to contained objects */ 0, /* tp_clear */ /* rich comparisons */ 0, /* (richcmpfunc) tp_richcompare */ /* weak reference enabler */ 0, /* (long) tp_weaklistoffset */ /* Iterators */ (getiterfunc)MrdbCursor_iter, (iternextfunc)MrdbCursor_iternext, /* Attribute descriptor and subclassing stuff */ (struct PyMethodDef *)MrdbCursor_Methods, /* tp_methods */ (struct PyMemberDef *)MrdbCursor_Members, /* tp_members */ MrdbCursor_sets, 0, /* (struct _typeobject *) tp_base; */ 0, /* (PyObject *) tp_dict */ 0, /* (descrgetfunc) tp_descr_get */ 0, /* (descrsetfunc) tp_descr_set */ 0, /* (long) tp_dictoffset */ (initproc)MrdbCursor_initialize, /* tp_init */ PyType_GenericAlloc, //NULL, /* tp_alloc */ PyType_GenericNew, //NULL, /* tp_new */ NULL, /* tp_free Low-level free-memory routine */ 0, /* (PyObject *) tp_bases */ 0, /* (PyObject *) tp_mro method resolution order */ 0, /* (PyObject *) tp_defined */ }; void MrdbCursor_clearparseinfo(MrdbParseInfo *parseinfo) { if (parseinfo->statement) MARIADB_FREE_MEM(parseinfo->statement); Py_XDECREF(parseinfo->keys); if (parseinfo->paramlist) Py_XDECREF(parseinfo->paramlist); memset(parseinfo, 0, sizeof(MrdbParseInfo)); } /* {{{ MrdbCursor_clear_result(MrdbCursor *self) clear pending result sets */ PyObject *MrdbCursor_clear_result(MrdbCursor *self) { if (!self->parseinfo.is_text && self->stmt) { /* free current result */ if (mysql_stmt_field_count(self->stmt)) mysql_stmt_free_result(self->stmt); /* check if there are more pending result sets */ while (mysql_stmt_next_result(self->stmt) == 0) { if (mysql_stmt_field_count(self->stmt)) mysql_stmt_free_result(self->stmt); } } else if (self->parseinfo.is_text) { /* free current result */ if (self->result) { mysql_free_result(self->result); } /* clear pending result sets */ if (self->connection->mysql) { do { MYSQL_RES *res; if ((res= mysql_use_result(self->connection->mysql))) mysql_free_result(res); } while (!mysql_next_result(self->connection->mysql)); } } /* CONPY-52: Avoid possible double free */ self->result= NULL; Py_RETURN_NONE; } static void MrdbCursor_FreeValues(MrdbCursor *self) { uint32_t i; if (!self->value) return; for (i= 0; i < self->parseinfo.paramcount; i++) if (self->value[i].free_me) MARIADB_FREE_MEM(self->value[i].buffer); MARIADB_FREE_MEM(self->value); } /* {{{ MrdbCursor_clear Resets statement attributes and frees associated memory */ static void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt) { /* clear pending result sets */ MrdbCursor_clear_result(self); if (!self->parseinfo.is_text && self->stmt) { if (new_stmt) { mysql_stmt_close(self->stmt); self->stmt= mysql_stmt_init(self->connection->mysql); } else { uint32_t val= 0; mysql_stmt_reset(self->stmt); /* we need to unset array size only */ mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &val); } } self->fetched= 0; if (self->sequence_fields) { MARIADB_FREE_MEM(self->sequence_fields); } self->fields= NULL; self->row_count= 0; self->affected_rows= 0; MrdbCursor_FreeValues(self); MrdbCursor_clearparseinfo(&self->parseinfo); MARIADB_FREE_MEM(self->values); MARIADB_FREE_MEM(self->bind); MARIADB_FREE_MEM(self->statement); MARIADB_FREE_MEM(self->value); MARIADB_FREE_MEM(self->params); } /* }}} */ static void ma_set_result_column_value(MrdbCursor *self, PyObject *row, uint32_t column) { switch (self->result_format) { case RESULT_NAMED_TUPLE: PyStructSequence_SET_ITEM(row, column, self->values[column]); break; case RESULT_DICTIONARY: PyDict_SetItemString(row, self->fields[column].name, self->values[column]); Py_DECREF(self->values[column]); /* CONPY-119 */ break; default: PyTuple_SET_ITEM(row, column, (self)->values[column]); } } /* {{{ ma_cursor_close closes the statement handle of current cursor. After call to cursor_close the cursor can't be reused anymore */ static void ma_cursor_close(MrdbCursor *self) { if (!self->closed) { MrdbCursor_clear_result(self); if (!self->parseinfo.is_text && self->stmt) { /* Todo: check if all the cursor stuff is deleted (when using prepared statements this should be handled in mysql_stmt_close) */ Py_BEGIN_ALLOW_THREADS; mysql_stmt_close(self->stmt); Py_END_ALLOW_THREADS; self->stmt= NULL; } MrdbCursor_clear(self, 0); if (!self->parseinfo.is_text && self->stmt) { mysql_stmt_close(self->stmt); self->stmt= NULL; } MrdbCursor_clearparseinfo(&self->parseinfo); self->closed= 1; } } static PyObject * MrdbCursor_close(MrdbCursor *self) { ma_cursor_close(self); Py_RETURN_NONE; } /* }}} */ /*{{{ MrDBCursor_dealloc */ void MrdbCursor_dealloc(MrdbCursor *self) { if (self->connection && self->connection->mysql) ma_cursor_close(self); Py_TYPE(self)->tp_free((PyObject*)self); } /* }}} */ static int Mrdb_GetFieldInfo(MrdbCursor *self) { self->row_number= 0; if (self->field_count) { if (self->parseinfo.is_text) { self->result= (self->is_buffered) ? mysql_store_result(self->connection->mysql) : mysql_use_result(self->connection->mysql); if (!self->result) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); return 1; } } else if (self->is_buffered) { if (mysql_stmt_store_result(self->stmt)) { mariadb_throw_exception(self->stmt, NULL, 1, NULL); return 1; } } self->affected_rows= CURSOR_AFFECTED_ROWS(self); self->fields= (self->parseinfo.is_text) ? mysql_fetch_fields(self->result) : mariadb_stmt_fetch_fields(self->stmt); if (self->result_format == RESULT_NAMED_TUPLE) { unsigned int i; PyStructSequence_Desc sequence_desc; if (!(self->sequence_fields= (PyStructSequence_Field *) PyMem_RawCalloc(self->field_count + 1, sizeof(PyStructSequence_Field)))) return 1; sequence_desc.name= mariadb_named_tuple_name; sequence_desc.doc= mariadb_named_tuple_desc; sequence_desc.fields= self->sequence_fields; sequence_desc.n_in_sequence= self->field_count; for (i=0; i < self->field_count; i++) { self->sequence_fields[i].name= self->fields[i].name; } self->sequence_type= PyStructSequence_NewType(&sequence_desc); #if PY_VERSION_HEX < 0x03070000 self->sequence_type->tp_flags|= Py_TPFLAGS_HEAPTYPE; #endif } } return 0; } PyObject *MrdbCursor_InitResultSet(MrdbCursor *self) { MARIADB_FREE_MEM(self->sequence_fields); MARIADB_FREE_MEM(self->values); if (self->result) { mysql_free_result(self->result); self->result= NULL; } if (Mrdb_GetFieldInfo(self)) return NULL; if (!(self->values= (PyObject**)PyMem_RawCalloc(self->field_count, sizeof(PyObject *)))) return NULL; if (!self->parseinfo.is_text) mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_RESULT, field_fetch_callback); if (self->field_count) { self->row_count= CURSOR_NUM_ROWS(self); } else { self->row_count= CURSOR_AFFECTED_ROWS(self); } self->lastrow_id= CURSOR_INSERT_ID(self); Py_RETURN_NONE; } static int Mrdb_execute_direct(MrdbCursor *self, const char *statement, size_t statement_len) { int rc; Py_BEGIN_ALLOW_THREADS; long ext_caps; mariadb_get_infov(self->connection->mysql, MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, &ext_caps); /* clear pending result sets */ MrdbCursor_clear_result(self); /* if stmt is already prepared */ if (!self->reprepare) { rc= mysql_stmt_execute(self->stmt); goto end; } /* execute_direct was implemented together with bulk operations, so we need to check if MARIADB_CLIENT_STMT_BULK_OPERATIONS is set in extended server capabilities */ if (!(ext_caps & (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))) { if (!(rc= mysql_stmt_prepare(self->stmt, statement, (unsigned long)statement_len))) { rc= mysql_stmt_execute(self->stmt); } } else { rc= mariadb_stmt_execute_direct(self->stmt, statement, statement_len); } end: Py_END_ALLOW_THREADS; return rc; } /* {{{ MrdbCursor_description PEP-249 description method() Please note that the returned tuple contains eight (instead of seven items, since we need the field flag */ static PyObject *MrdbCursor_description(MrdbCursor *self) { PyObject *obj= NULL; unsigned int field_count= self->field_count; if (PyErr_Occurred()) return NULL; if (self->fields && field_count) { uint32_t i; if (!(obj= PyTuple_New(field_count))) return NULL; for (i=0; i < field_count; i++) { uint32_t precision= 0; uint32_t decimals= 0; MY_CHARSET_INFO cs; unsigned long display_length; long packed_len= 0; PyObject *desc; enum enum_extended_field_type ext_type= mariadb_extended_field_type(&self->fields[i]); display_length= self->fields[i].max_length > self->fields[i].length ? self->fields[i].max_length : self->fields[i].length; mysql_get_character_set_info(self->connection->mysql, &cs); if (cs.mbmaxlen > 1) { packed_len= display_length; display_length/= cs.mbmaxlen; } else { packed_len= mysql_ps_fetch_functions[self->fields[i].type].pack_len; } if (self->fields[i].decimals) { if (self->fields[i].decimals < 31) { decimals= self->fields[i].decimals; precision= self->fields[i].length; display_length= precision + 1; } } if (ext_type == EXT_TYPE_JSON) self->fields[i].type= MYSQL_TYPE_JSON; if (!(desc= Py_BuildValue("(sIIiIIOIsss)", self->fields[i].name, self->fields[i].type, display_length, packed_len >= 0 ? packed_len : -1, precision, decimals, PyBool_FromLong(!IS_NOT_NULL(self->fields[i].flags)), self->fields[i].flags, self->fields[i].table, self->fields[i].org_name, self->fields[i].org_table))) { Py_XDECREF(obj); mariadb_throw_exception(NULL, Mariadb_OperationalError, 0, "Can't build descriptor record"); return NULL; } PyTuple_SetItem(obj, i, desc); } return obj; } Py_RETURN_NONE; } /* }}} */ static int MrdbCursor_fetchinternal(MrdbCursor *self) { unsigned int field_count= self->field_count; MYSQL_ROW row; int rc; unsigned int i; self->fetched= 1; if (!self->parseinfo.is_text) { rc= mysql_stmt_fetch(self->stmt); if (rc == MYSQL_NO_DATA) return 1; return 0; } if (!(row= mysql_fetch_row(self->result))) { return 1; } for (i= 0; i < field_count; i++) { field_fetch_fromtext(self, row[i], i); } return 0; } static PyObject * MrdbCursor_fetchone(MrdbCursor *self) { PyObject *row; uint32_t i; unsigned int field_count= self->field_count; if (self->cursor_type == CURSOR_TYPE_READ_ONLY) MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } if (!field_count) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Cursor doesn't have a result set"); return NULL; } if (MrdbCursor_fetchinternal(self)) { Py_RETURN_NONE; } self->row_number++; if (!(row= mariadb_get_sequence_or_tuple(self))) { return NULL; } for (i= 0; i < field_count; i++) { ma_set_result_column_value(self, row, i); } return row; } static PyObject *MrdbCursor_seek(MrdbCursor *self, PyObject *args) { uint64_t new_position; if (!PyArg_ParseTuple(args, "K", &new_position)) { return NULL; } Py_BEGIN_ALLOW_THREADS; if (self->parseinfo.is_text) mysql_data_seek(self->result, new_position); else mysql_stmt_data_seek(self->stmt, new_position); Py_END_ALLOW_THREADS; Py_RETURN_NONE; } static PyObject * mariadb_get_sequence_or_tuple(MrdbCursor *self) { switch (self->result_format) { case RESULT_NAMED_TUPLE: return PyStructSequence_New(self->sequence_type); case RESULT_DICTIONARY: return PyDict_New(); default: return PyTuple_New(self->field_count); } } static PyObject * MrdbCursor_nextset(MrdbCursor *self) { int rc; MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } Py_BEGIN_ALLOW_THREADS; if (!self->parseinfo.is_text) rc= mysql_stmt_next_result(self->stmt); else { if (self->result) { mysql_free_result(self->result); self->result= NULL; } rc= mysql_next_result(self->connection->mysql); } Py_END_ALLOW_THREADS; if (rc) { Py_RETURN_NONE; } if ((self->field_count= CURSOR_FIELD_COUNT(self))) { if (!MrdbCursor_InitResultSet(self)) { return NULL; } } else { self->fields= 0; } Py_RETURN_TRUE; } static PyObject * Mariadb_row_count(MrdbCursor *self) { if (!self->parseinfo.statement) return PyLong_FromLongLong(-1); if (self->field_count) return PyLong_FromLongLong(CURSOR_NUM_ROWS(self)); return PyLong_FromLongLong(CURSOR_AFFECTED_ROWS(self)); } static PyObject * Mariadb_row_number(MrdbCursor *self) { if (!self->field_count) { Py_RETURN_NONE; } return PyLong_FromLongLong(self->row_number); } static PyObject * MrdbCursor_warnings(MrdbCursor *self) { MARIADB_CHECK_STMT(self); return PyLong_FromLong((long)CURSOR_WARNING_COUNT(self)); } /* iterator protocol */ static PyObject * MrdbCursor_iter(PyObject *self) { MARIADB_CHECK_STMT(((MrdbCursor *)self)); Py_INCREF(self); return self; } static PyObject * MrdbCursor_iternext(PyObject *self) { PyObject *res; res= MrdbCursor_fetchone((MrdbCursor *)self); if (res && res == Py_None) { Py_DECREF(res); res= NULL; } return res; } static PyObject *MrdbCursor_closed(MrdbCursor *self) { if (self->closed || self->connection->mysql == NULL) Py_RETURN_TRUE; Py_RETURN_FALSE; } static PyObject * MrdbCursor_parse(MrdbCursor *self, PyObject *args) { const char *statement= NULL; Py_ssize_t statement_len= 0; MrdbParser *parser= NULL; char errmsg[128]; uint32_t old_paramcount= 0; if (self->parseinfo.statement) { old_paramcount= self->parseinfo.paramcount; MrdbCursor_clearparseinfo(&self->parseinfo); } if (!PyArg_ParseTuple(args, "s#|Ob", &statement, &statement_len)) { return NULL; } if (!(parser= MrdbParser_init(self->connection->mysql, statement, statement_len))) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Can't initialize parser."); return NULL; } if (MrdbParser_parse(parser, 0, errmsg, 128)) { MrdbParser_end(parser); PyErr_SetString(Mariadb_ProgrammingError, errmsg); return NULL; } /* cleanup and save some parser stuff */ if (parser->param_count && parser->param_count != old_paramcount) { MARIADB_FREE_MEM(self->params); MrdbCursor_FreeValues(self); MARIADB_FREE_MEM(self->values); MARIADB_FREE_MEM(self->bind); } self->parseinfo.paramcount= parser->param_count; self->parseinfo.paramstyle= parser->paramstyle; if (self->parseinfo.statement) PyMem_RawFree(self->parseinfo.statement); self->parseinfo.statement= PyMem_RawCalloc(parser->statement.length + 1, 1); memcpy(self->parseinfo.statement, parser->statement.str, parser->statement.length); self->parseinfo.statement_len= parser->statement.length; self->parseinfo.paramlist= parser->param_list; parser->param_list= NULL; self->parseinfo.is_text= (parser->command == SQL_NONE || parser->command == SQL_OTHER); self->parseinfo.command= parser->command; if (parser->paramstyle == PYFORMAT && parser->keys) { PyObject *tmp= PyTuple_New(parser->param_count); for (uint32_t i= 0; i < parser->param_count; i++) { PyObject *key; key= PyUnicode_FromString(parser->keys[i].str); PyTuple_SetItem(tmp, i, key); } self->parseinfo.keys= tmp; } MrdbParser_end(parser); Py_RETURN_NONE; } static PyObject * MrdbCursor_execute_binary(MrdbCursor *self) { int rc; unsigned char *buf= NULL; size_t buflen; MARIADB_CHECK_CONNECTION(self->connection, NULL); if (!self->stmt && !(self->stmt= mysql_stmt_init(self->connection->mysql))) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); goto error; } /* CONPY-164: reset array_size */ self->array_size= 0; mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &self->array_size); if (self->data && self->parseinfo.paramcount) { if (mariadb_check_execute_parameters(self, self->data)) goto error; /* Load values */ if (mariadb_param_update(self, self->params, 0)) goto error; } if (self->reprepare) { mysql_stmt_attr_set(self->stmt, STMT_ATTR_CURSOR_TYPE, &self->cursor_type); mysql_stmt_attr_set(self->stmt, STMT_ATTR_PREBIND_PARAMS, &self->parseinfo.paramcount); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, (void *)self); } if (self->parseinfo.paramcount) mysql_stmt_bind_param(self->stmt, self->params); if (!(buf= self->connection->mysql->methods->db_execute_generate_request(self->stmt, &buflen, 1))) goto error; if ((rc= Mrdb_execute_direct(self, self->parseinfo.statement, self->parseinfo.statement_len))) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); goto error; } self->field_count= mysql_stmt_field_count(self->stmt); Py_RETURN_NONE; error: return NULL; } static PyObject * MrdbCursor_execute_text(MrdbCursor *self, PyObject *args) { int rc; MYSQL *db; char *statement; size_t statement_len; MARIADB_CHECK_CONNECTION(self->connection, NULL); if (!PyArg_ParseTuple(args, "s#", &statement, &statement_len)) { return NULL; } db= self->connection->mysql; Py_BEGIN_ALLOW_THREADS; rc= mysql_send_query(db, statement, (long)statement_len); Py_END_ALLOW_THREADS; if (rc) { mariadb_throw_exception(db, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } static PyObject * MrdbCursor_readresponse(MrdbCursor *self) { int rc; MYSQL *db; MARIADB_CHECK_CONNECTION(self->connection, NULL); db= self->connection->mysql; if (self->parseinfo.is_text) { Py_BEGIN_ALLOW_THREADS; rc= db->methods->db_read_query_result(db); Py_END_ALLOW_THREADS; if (rc) { mariadb_throw_exception(db, NULL, 0, NULL); return NULL; } self->field_count= mysql_field_count(self->connection->mysql); } Py_RETURN_NONE; } static PyObject * MrdbCursor_execute_bulk(MrdbCursor *self) { int rc; unsigned char *buf= NULL; size_t buflen; MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } if (!self->data) { PyErr_SetString(PyExc_TypeError, "No data provided"); return NULL; } if (!self->stmt) { if (!(self->stmt= mysql_stmt_init(self->connection->mysql))) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); goto error; } } if (mariadb_check_bulk_parameters(self, self->data)) goto error; /* If the server doesn't support bulk execution (< 10.2.6), we need to call a fallback routine */ mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &self->array_size); mysql_stmt_attr_set(self->stmt, STMT_ATTR_PREBIND_PARAMS, &self->parseinfo.paramcount); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, (void *)self); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_PARAM, mariadb_param_update); mysql_stmt_bind_param(self->stmt, self->params); if (!(buf= self->connection->mysql->methods->db_execute_generate_request(self->stmt, &buflen, 1))) goto error; if ((rc= Mrdb_execute_direct(self, self->parseinfo.statement, self->parseinfo.statement_len))) { mariadb_throw_exception(self->stmt, NULL, 1, NULL); goto error; } if ((self->field_count= CURSOR_FIELD_COUNT(self))) { if (!MrdbCursor_InitResultSet(self)) { return NULL; } } else { self->affected_rows= CURSOR_AFFECTED_ROWS(self); self->lastrow_id= CURSOR_INSERT_ID(self); MARIADB_FREE_MEM(self->values); } Py_RETURN_NONE; error: MrdbCursor_clear(self, 0); return NULL; }