I got some SQL function
CREATE OR REPLACE FUNCTION tools.update_company(IN company_id integer, OUT value integer)
RETURNS integer AS
$BODY$
BEGIN
select * into value from function_making_int(company_id)
END;$BODY$
and from Psycopg2 (its inside Django if that matters) I do
c = connection.cursor()
c.callproc('tools.update_company', [1, ])
but function returns exactly the same input sequence as I gave, ignoring results and OUT parameter. Change to IN OUT and passing some foo value changes nothing. When called within database SQL function works as expected
Prerequisites. The current psycopg2 implementation supports: Python versions from 3.6 to 3.11. PostgreSQL server versions from 7.4 to 15.
Establish the connection to the PostgreSQL database from python by passing the connection string to psycopg2. connect() function. Then call psycopg2 callproc() function, which takes in two parameters, the name of the stored procedure and the parameters of the stored procedure.
Thread and process safetyThe Psycopg module and the connection objects are thread-safe: many threads can access the same database either using separate sessions and creating a connection per thread or using the same connection and creating separate cursors. In DB API 2.0 parlance, Psycopg is level 2 thread safe.
Psycopg2 is a DB API 2.0 compliant PostgreSQL driver that is actively developed. It is designed for multi-threaded applications and manages its own connection pool.
Well I did little research and I've checked psycopg2 code - current implementation of this function just do
select * from function_name(params)
return params
so it does not modify anything in any way.
taken from pyscopg2 source code, the C implementation of callproc using the C-api give the following code:
/* callproc method - execute a stored procedure */
#define psyco_curs_callproc_doc \
"callproc(procname, parameters=None) -- Execute stored procedure."
static PyObject *
psyco_curs_callproc(cursorObject *self, PyObject *args)
{
const char *procname = NULL;
char *sql = NULL;
Py_ssize_t procname_len, i, nparameters = 0, sl = 0;
PyObject *parameters = Py_None;
PyObject *operation = NULL;
PyObject *res = NULL;
if (!PyArg_ParseTuple(args, "s#|O",
&procname, &procname_len, ¶meters
))
{ goto exit; }
EXC_IF_CURS_CLOSED(self);
EXC_IF_ASYNC_IN_PROGRESS(self, callproc);
EXC_IF_TPC_PREPARED(self->conn, callproc);
if (self->name != NULL) {
psyco_set_error(ProgrammingError, self,
"can't call .callproc() on named cursors");
goto exit;
}
if (parameters != Py_None) {
if (-1 == (nparameters = PyObject_Length(parameters))) { goto exit; }
}
/* allocate some memory, build the SQL and create a PyString from it */
sl = procname_len + 17 + nparameters*3 - (nparameters ? 1 : 0);
sql = (char*)PyMem_Malloc(sl);
if (sql == NULL) {
PyErr_NoMemory();
goto exit;
}
sprintf(sql, "SELECT * FROM %s(", procname);
for(i=0; i<nparameters; i++) {
strcat(sql, "%s,");
}
sql[sl-2] = ')';
sql[sl-1] = '\0';
if (!(operation = Bytes_FromString(sql))) { goto exit; }
if (0 <= _psyco_curs_execute(self, operation, parameters,
self->conn->async, 0)) {
Py_INCREF(parameters);
res = parameters;
}
exit:
Py_XDECREF(operation);
PyMem_Free((void*)sql);
return res;
}
you can notice that there is no a local modification on the passed arguments to "callproc" but the result is returned:
c = connection.cursor()
res = c.callproc('tools.update_company', [1, ])
and yes, actually the code produce a "SELECT" SQL on the ground to perfom the action.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With