Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Psycopg2 callproc and sql parameters

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

like image 699
M4ks Avatar asked Jul 08 '10 10:07

M4ks


People also ask

Does psycopg2 need PostgreSQL?

Prerequisites. The current psycopg2 implementation supports: Python versions from 3.6 to 3.11. PostgreSQL server versions from 7.4 to 15.

How do you call a stored procedure in PostgreSQL Python?

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.

Is psycopg2 connection thread safe?

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.

Is psycopg2 a database driver?

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.


2 Answers

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.

like image 97
M4ks Avatar answered Oct 24 '22 02:10

M4ks


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, &parameters
       ))
    { 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.

like image 22
Narcisse Doudieu Siewe Avatar answered Oct 24 '22 04:10

Narcisse Doudieu Siewe