mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-08-03 19:20:35 +00:00
CONPY-31: Implement callproc method
Input/Output or Output parameters have to be retrieved by .fetch methods, the .sp_outparams attribute indicates if the result set contains output parameters.
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Normalise line endings:
|
||||||
|
* text=auto
|
@ -32,6 +32,33 @@ The cursor class
|
|||||||
same connection will fail, unless the entire result set was read. For buffering
|
same connection will fail, unless the entire result set was read. For buffering
|
||||||
the entire result set an additional parameter *buffered=True* must be specified.
|
the entire result set an additional parameter *buffered=True* must be specified.
|
||||||
|
|
||||||
|
.. method:: callproc(procedure_name, args=())
|
||||||
|
|
||||||
|
Executes a stored procedure. The args sequence must contain an entry for
|
||||||
|
each parameter the procedure expects.
|
||||||
|
Input/Output or Output parameters have to be retrieved by .fetch methods,
|
||||||
|
the .sp_outparams attribute indicates if the result set contains output
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
>>>cursor.execute("CREATE PROCEDURE p1(IN i1 VAR CHAR(20), OUT o2 VARCHAR(40))"
|
||||||
|
"BEGIN"
|
||||||
|
" SELECT 'hello'"
|
||||||
|
" o2:= 'test'"
|
||||||
|
"END")
|
||||||
|
>>>cursor.callproc('p1', ('foo', 0))
|
||||||
|
>>> cursor.sp_outparams
|
||||||
|
False
|
||||||
|
>>> cursor.fetchone()
|
||||||
|
('hello',)
|
||||||
|
>>> cursor.nextset()
|
||||||
|
True
|
||||||
|
>>> cursor.sp_outparams
|
||||||
|
True
|
||||||
|
>>> cursor.fetchone()
|
||||||
|
('test',)
|
||||||
|
|
||||||
.. method:: executemany(statement, data)
|
.. method:: executemany(statement, data)
|
||||||
|
|
||||||
Exactly behaves like .execute() but accepts a list of tuples, where each
|
Exactly behaves like .execute() but accepts a list of tuples, where each
|
||||||
@ -147,6 +174,11 @@ The cursor class
|
|||||||
AUTO_INCREMENT attribute and LAST_INSERT_ID was not used, the returned
|
AUTO_INCREMENT attribute and LAST_INSERT_ID was not used, the returned
|
||||||
value will be zero
|
value will be zero
|
||||||
|
|
||||||
|
.. data:: sp_outparams
|
||||||
|
|
||||||
|
This read-only attribute undicates if the current result set contains inout
|
||||||
|
or out parameters from a previously executed stored procedure.
|
||||||
|
|
||||||
.. data:: rowcount
|
.. data:: rowcount
|
||||||
|
|
||||||
This read-only attribute specifies the number of rows that the last
|
This read-only attribute specifies the number of rows that the last
|
||||||
|
@ -10,7 +10,7 @@ Prerequisits
|
|||||||
|
|
||||||
- Python 3 (minimum supported version is 3.6)
|
- Python 3 (minimum supported version is 3.6)
|
||||||
- MariaDB Server 10.x or MySQL Server
|
- MariaDB Server 10.x or MySQL Server
|
||||||
- MariaDB Connector/C 3.1.3 or newer
|
- MariaDB Connector/C 3.1.5 or newer
|
||||||
|
|
||||||
:: _build-prerequisits:
|
:: _build-prerequisits:
|
||||||
|
|
||||||
@ -24,16 +24,27 @@ installed from source distribution package or github.
|
|||||||
- Python development files (Usually they are installed with package **python-dev**).
|
- Python development files (Usually they are installed with package **python-dev**).
|
||||||
- MariaDB Connector/C libraries and header files (Either from MariaDB server package or
|
- MariaDB Connector/C libraries and header files (Either from MariaDB server package or
|
||||||
from MariaDB Connector/C package).
|
from MariaDB Connector/C package).
|
||||||
|
- For Posix systems: TLS libraries, e.g. GnuTLS or OpenSSL (default)
|
||||||
|
|
||||||
|
|
||||||
Binary installation
|
Binary installation
|
||||||
-------------------
|
-------------------
|
||||||
MariaDB Connector/C is also available from PyPi as wheel packages for Linux, Windows and MacOS.
|
MariaDB Connector/C is also available from PyPi as wheel packages for Linux, Windows and MacOS.
|
||||||
|
These binary packages are not intended for production use, since there might be several limitations
|
||||||
|
and bottlenecks, e.g.:
|
||||||
|
|
||||||
|
- Binaries for Posix systems come with their own version of libraries which will be used regardless
|
||||||
|
of other libraries installed on the system. This might lead to unexpected results, e.g. when using
|
||||||
|
different OpenSSL libraries.
|
||||||
|
|
||||||
|
- Dynamic MariaDB plugins (e.g. authentication plugins) are not part of the package and must
|
||||||
|
be installed separetly by installing MariaDB Connector/C or MariaDB Server package.
|
||||||
|
|
||||||
Make sure you have an up to date version of pip and install it with
|
Make sure you have an up to date version of pip and install it with
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pip install mariadb
|
$ pip install mariadb-binary
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,6 +128,35 @@ PyDoc_STRVAR(
|
|||||||
"Fetches all rows of a pending result set and returns a list of tuples.\n"
|
"Fetches all rows of a pending result set and returns a list of tuples.\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PyDoc_STRVAR(
|
||||||
|
cursor_callproc__doc__,
|
||||||
|
"callproc(procedure_name, args=())\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Executes a stored procedure. The args sequence must contain an entry for\n"
|
||||||
|
"each parameter the procedure expects.\n"
|
||||||
|
"Input/Output or Output parameters have to be retrieved by .fetch methods,\n"
|
||||||
|
"the .sp_outparams attribute indicates if the result set contains output\n"
|
||||||
|
"parameters\n\n"
|
||||||
|
"Example:\n\n"
|
||||||
|
">>>cursor.execute(\"CREATE PROCEDURE p1(IN i1 VAR CHAR(20), OUT o2 VARCHAR(40))\"\n"
|
||||||
|
" \"BEGIN\"\n"
|
||||||
|
" \" SELECT 'hello'\"\n"
|
||||||
|
" \" o2:= 'test'\"\n"
|
||||||
|
" \"END\")\n"
|
||||||
|
">>>cursor.callproc('p1', ('foo', 0))\n"
|
||||||
|
">>> cursor.sp_outparams\n"
|
||||||
|
"False\n"
|
||||||
|
">>> cursor.fetchone()\n"
|
||||||
|
"('hello',)\n"
|
||||||
|
">>> cursor.nextset()\n"
|
||||||
|
"True\n"
|
||||||
|
">>> cursor.sp_outparams\n"
|
||||||
|
"True\n"
|
||||||
|
">>> cursor.fetchone()\n"
|
||||||
|
"('test',)"
|
||||||
|
);
|
||||||
|
|
||||||
PyDoc_STRVAR(
|
PyDoc_STRVAR(
|
||||||
cursor_fetchone__doc__,
|
cursor_fetchone__doc__,
|
||||||
"fetchone()\n"
|
"fetchone()\n"
|
||||||
@ -183,15 +212,6 @@ PyDoc_STRVAR(
|
|||||||
"Required by PEP-249. Does nothing in MariaDB Connector/Python"
|
"Required by PEP-249. Does nothing in MariaDB Connector/Python"
|
||||||
);
|
);
|
||||||
|
|
||||||
PyDoc_STRVAR(
|
|
||||||
cursor_callproc__doc__,
|
|
||||||
"callproc()\n"
|
|
||||||
"--\n"
|
|
||||||
"\n"
|
|
||||||
"Required by PEP-249. Does nothing in MariaDB Connector/Python,\n"
|
|
||||||
"use the execute method with syntax 'CALL {procedurename}' instead"
|
|
||||||
);
|
|
||||||
|
|
||||||
PyDoc_STRVAR(
|
PyDoc_STRVAR(
|
||||||
cursor_next__doc__,
|
cursor_next__doc__,
|
||||||
"next()\n"
|
"next()\n"
|
||||||
@ -238,3 +258,10 @@ PyDoc_STRVAR(
|
|||||||
"(read/write)\n\n"
|
"(read/write)\n\n"
|
||||||
"the number of rows to fetch"
|
"the number of rows to fetch"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PyDoc_STRVAR(
|
||||||
|
cursor_sp_outparam__doc__,
|
||||||
|
"(read)\n\n"
|
||||||
|
"Indicates if the current result set contains inout or out parameter\n"
|
||||||
|
"from a previous executed stored procedure."
|
||||||
|
);
|
||||||
|
2
setup.py
2
setup.py
@ -18,7 +18,7 @@ if os.name == "nt":
|
|||||||
cfg = get_config(options)
|
cfg = get_config(options)
|
||||||
|
|
||||||
setup(name='mariadb',
|
setup(name='mariadb',
|
||||||
version='0.9.4',
|
version='0.9.41',
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
classifiers = [
|
classifiers = [
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
|
@ -36,6 +36,8 @@ static PyObject *MrdbCursor_fetchmany(MrdbCursor *self,
|
|||||||
static PyObject *MrdbCursor_scroll(MrdbCursor *self,
|
static PyObject *MrdbCursor_scroll(MrdbCursor *self,
|
||||||
PyObject *args,
|
PyObject *args,
|
||||||
PyObject *kwargs);
|
PyObject *kwargs);
|
||||||
|
static PyObject *MrdbCursor_callproc(MrdbCursor *self,
|
||||||
|
PyObject *args);
|
||||||
static PyObject *MrdbCursor_fieldcount(MrdbCursor *self);
|
static PyObject *MrdbCursor_fieldcount(MrdbCursor *self);
|
||||||
void field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column);
|
void field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column);
|
||||||
void field_fetch_callback(void *data, unsigned int column, unsigned char **row);
|
void field_fetch_callback(void *data, unsigned int column, unsigned char **row);
|
||||||
@ -86,6 +88,7 @@ static PyObject *MrdbCursor_getbuffered(MrdbCursor *self);
|
|||||||
static int MrdbCursor_setbuffered(MrdbCursor *self, PyObject *arg);
|
static int MrdbCursor_setbuffered(MrdbCursor *self, PyObject *arg);
|
||||||
static PyObject *MrdbCursor_lastrowid(MrdbCursor *self);
|
static PyObject *MrdbCursor_lastrowid(MrdbCursor *self);
|
||||||
static PyObject *MrdbCursor_closed(MrdbCursor *self);
|
static PyObject *MrdbCursor_closed(MrdbCursor *self);
|
||||||
|
static PyObject *MrdbCursor_sp_outparams(MrdbCursor *self);
|
||||||
|
|
||||||
|
|
||||||
static PyGetSetDef MrdbCursor_sets[]=
|
static PyGetSetDef MrdbCursor_sets[]=
|
||||||
@ -102,12 +105,17 @@ static PyGetSetDef MrdbCursor_sets[]=
|
|||||||
cursor_closed__doc__, NULL},
|
cursor_closed__doc__, NULL},
|
||||||
{"buffered", (getter)MrdbCursor_getbuffered, (setter)MrdbCursor_setbuffered,
|
{"buffered", (getter)MrdbCursor_getbuffered, (setter)MrdbCursor_setbuffered,
|
||||||
cursor_buffered__doc__, NULL},
|
cursor_buffered__doc__, NULL},
|
||||||
|
{"sp_outparams", (getter)MrdbCursor_sp_outparams, NULL,
|
||||||
|
cursor_sp_outparam__doc__, NULL},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMethodDef MrdbCursor_Methods[] =
|
static PyMethodDef MrdbCursor_Methods[] =
|
||||||
{
|
{
|
||||||
/* PEP-249 methods */
|
/* PEP-249 methods */
|
||||||
|
{"callproc", (PyCFunction)MrdbCursor_callproc,
|
||||||
|
METH_VARARGS,
|
||||||
|
cursor_callproc__doc__},
|
||||||
{"close", (PyCFunction)MrdbCursor_close,
|
{"close", (PyCFunction)MrdbCursor_close,
|
||||||
METH_NOARGS,
|
METH_NOARGS,
|
||||||
cursor_close__doc__},
|
cursor_close__doc__},
|
||||||
@ -353,6 +361,7 @@ static PyObject *Mariadb_no_operation(MrdbCursor *self,
|
|||||||
static
|
static
|
||||||
void MrdbCursor_clear(MrdbCursor *self)
|
void MrdbCursor_clear(MrdbCursor *self)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!self->is_text && self->stmt) {
|
if (!self->is_text && self->stmt) {
|
||||||
uint32_t val= 0;
|
uint32_t val= 0;
|
||||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, 0);
|
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, 0);
|
||||||
@ -1347,3 +1356,58 @@ static PyObject *MrdbCursor_closed(MrdbCursor *self)
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ MrdbCursor_inoutparam */
|
||||||
|
static PyObject *MrdbCursor_sp_outparams(MrdbCursor *self)
|
||||||
|
{
|
||||||
|
if (!self->is_closed && self->stmt &&
|
||||||
|
self->stmt->mysql &&
|
||||||
|
(self->stmt->mysql->server_status & SERVER_PS_OUT_PARAMS))
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *MrdbCursor_callproc(MrdbCursor *self, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *sp;
|
||||||
|
Py_ssize_t sp_len;
|
||||||
|
PyObject *data= NULL;
|
||||||
|
uint32_t i, param_count= 0;
|
||||||
|
char *stmt= NULL;
|
||||||
|
size_t stmt_len= 0;
|
||||||
|
PyObject *new_args= NULL;
|
||||||
|
PyObject *rc= NULL;
|
||||||
|
|
||||||
|
MARIADB_CHECK_STMT(((MrdbCursor *)self));
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s#|O!", &sp, &sp_len,
|
||||||
|
&PyTuple_Type, &data))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
param_count= PyTuple_Size(data);
|
||||||
|
|
||||||
|
stmt_len= sp_len + 5 + 3 + param_count * 2 + 1;
|
||||||
|
if (!(stmt= (char *)PyMem_RawCalloc(1, stmt_len)))
|
||||||
|
goto end;
|
||||||
|
sprintf(stmt, "CALL %s(", sp);
|
||||||
|
for (i=0; i < param_count; i++)
|
||||||
|
{
|
||||||
|
if (i)
|
||||||
|
strcat(stmt, ",");
|
||||||
|
strcat(stmt, "?");
|
||||||
|
}
|
||||||
|
strcat(stmt, ")");
|
||||||
|
|
||||||
|
new_args= PyTuple_New(2);
|
||||||
|
PyTuple_SetItem(new_args, 0, PyUnicode_FromString(stmt));
|
||||||
|
PyTuple_SetItem(new_args, 1, data);
|
||||||
|
|
||||||
|
rc= MrdbCursor_execute(self, new_args, NULL);
|
||||||
|
Py_DECREF(new_args);
|
||||||
|
end:
|
||||||
|
if (stmt)
|
||||||
|
PyMem_RawFree(stmt);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -472,8 +472,8 @@ class TestCursor(unittest.TestCase):
|
|||||||
cursor.execute("SELECT LAST_INSERT_ID()")
|
cursor.execute("SELECT LAST_INSERT_ID()")
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
self.assertEqual(row[0], 1)
|
self.assertEqual(row[0], 1)
|
||||||
# del cursor
|
del cursor
|
||||||
# cursor= self.connection.cursor()
|
cursor= self.connection.cursor()
|
||||||
vals = [(3, "bar"), (4, "this")]
|
vals = [(3, "bar"), (4, "this")]
|
||||||
cursor.executemany("INSERT INTO test_conpy_15 VALUES (?,?)", vals)
|
cursor.executemany("INSERT INTO test_conpy_15 VALUES (?,?)", vals)
|
||||||
self.assertEqual(cursor.lastrowid, 4)
|
self.assertEqual(cursor.lastrowid, 4)
|
||||||
@ -596,7 +596,50 @@ class TestCursor(unittest.TestCase):
|
|||||||
self.assertEqual(row[0], 'foo')
|
self.assertEqual(row[0], 'foo')
|
||||||
del cursor, con
|
del cursor, con
|
||||||
|
|
||||||
|
def test_sp1(self):
|
||||||
|
con= create_connection()
|
||||||
|
cursor= con.cursor()
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p1")
|
||||||
|
cursor.execute("CREATE PROCEDURE p1( )\nBEGIN\n SELECT 1;\nEND")
|
||||||
|
cursor.callproc("p1")
|
||||||
|
row= cursor.fetchone()
|
||||||
|
self.assertEqual(row[0], 1)
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p1")
|
||||||
|
|
||||||
|
def test_sp2(self):
|
||||||
|
con= create_connection()
|
||||||
|
cursor= con.cursor()
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p2")
|
||||||
|
cursor.execute("CREATE PROCEDURE p2(IN s1 VARCHAR(20), IN s2 VARCHAR(20), OUT o1 VARCHAR(40) )\nBEGIN\n SET o1:=CONCAT(s1,s2);\nEND")
|
||||||
|
cursor.callproc("p2", ("foo", "bar", 1))
|
||||||
|
self.assertEqual(cursor.sp_outparams, True)
|
||||||
|
row= cursor.fetchone()
|
||||||
|
self.assertEqual(row[0], "foobar")
|
||||||
|
cursor.nextset()
|
||||||
|
del cursor
|
||||||
|
cursor=con.cursor()
|
||||||
|
cursor.execute("CALL p2(?,?,?)", ("foo", "bar", 0))
|
||||||
|
self.assertEqual(cursor.sp_outparams, True)
|
||||||
|
row= cursor.fetchone()
|
||||||
|
self.assertEqual(row[0], "foobar")
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p2")
|
||||||
|
del cursor, con
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def test_sp3(self):
|
||||||
|
con= create_connection()
|
||||||
|
cursor= con.cursor()
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p3")
|
||||||
|
cursor.execute("CREATE PROCEDURE p3(IN s1 VARCHAR(20), IN s2 VARCHAR(20), OUT o1 VARCHAR(40) )\nBEGIN\n SELECT '1';SET o1:=CONCAT(s1,s2);\nEND")
|
||||||
|
cursor.callproc("p3", ("foo", "bar", 1))
|
||||||
|
self.assertEqual(cursor.sp_outparams, False)
|
||||||
|
row= cursor.fetchone()
|
||||||
|
self.assertEqual(row[0], "1")
|
||||||
|
cursor.nextset()
|
||||||
|
self.assertEqual(cursor.sp_outparams, True)
|
||||||
|
row= cursor.fetchone()
|
||||||
|
self.assertEqual(row[0], "foobar")
|
||||||
|
cursor.execute("DROP PROCEDURE IF EXISTS p3")
|
||||||
|
del cursor, con
|
||||||
|
|
||||||
|
f __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user