Обсуждение: Session state per transaction

Поиск
Список
Период
Сортировка

Session state per transaction

От
Tobias Oberstein
Дата:
Hi,

I am looking for a mechanism (usable from Psycopg2) that allows me
to call stored procedures while having a per-transaction state set.

I have a connection pool serving web sessions, and I want to set
the web session ID as per stored procedure call.

There is no 1:1 relation between Web sessions and database sessions,
so this needs to be on a per-call basis.

I came over the current_setting and set_config functions

http://www.postgresql.org/docs/9.2/static/functions-admin.html

and did some tests (see below).

However, I am not sure how to use that from Psycopg2.

Normally I call SPs simply via

cur.execute("SELECT mysp1(%s, %s)", [...])

on a connection set to autocommit = True;

Now I want to do the same but have

cur.execute("SELECT set_config('webmq.sessionid', %s, false)", [session_id])

"implicitly called before".

That is I want to avoid the double roundtrip ..

Any ideas?

Thanks,
Tobias




CREATE OR REPLACE FUNCTION hello (msg VARCHAR) RETURNS VARCHAR
AS
$$
BEGIN
   RETURN msg || ' [session ID = ' || current_setting('webmq.sessionid')
|| ']';
END;
$$
LANGUAGE 'plpgsql';


CREATE OR REPLACE FUNCTION tabfun1 () RETURNS SETOF VARCHAR
AS
$$
BEGIN
   RETURN NEXT 'Hello';
   RETURN NEXT ', world';
   RETURN NEXT '!';
   RETURN NEXT current_setting('webmq.sessionid');
   RETURN;
END;
$$
LANGUAGE 'plpgsql';


DO
$$
BEGIN
   PERFORM set_config('webmq.sessionid', 'Xnjkas23', false);
   RAISE NOTICE '%', hello('Hello, world!');
   PERFORM set_config('webmq.sessionid', 'abda6ads', false);
   RAISE NOTICE '%', hello('Hello, world!');
END
$$
LANGUAGE 'plpgsql';


SELECT set_config('webmq.sessionid', 'kjzgbad7', false), hello('Hello,
world!');

SELECT set_config('webmq.sessionid', 'mhzAD456', false), tabfun1()


Re: Session state per transaction

От
Daniele Varrazzo
Дата:
On Wed, Sep 26, 2012 at 5:56 PM, Tobias Oberstein
<tobias.oberstein@gmail.com> wrote:
> Hi,
>
> I am looking for a mechanism (usable from Psycopg2) that allows me
> to call stored procedures while having a per-transaction state set.
>
> I have a connection pool serving web sessions, and I want to set
> the web session ID as per stored procedure call.
>
> There is no 1:1 relation between Web sessions and database sessions,
> so this needs to be on a per-call basis.
>
> I came over the current_setting and set_config functions
>
> http://www.postgresql.org/docs/9.2/static/functions-admin.html
>
> and did some tests (see below).
>
> However, I am not sure how to use that from Psycopg2.
>
> Normally I call SPs simply via
>
> cur.execute("SELECT mysp1(%s, %s)", [...])
>
> on a connection set to autocommit = True;
>
> Now I want to do the same but have
>
> cur.execute("SELECT set_config('webmq.sessionid', %s, false)", [session_id])
>
> "implicitly called before".
>
> That is I want to avoid the double roundtrip ..
>
> Any ideas?

If the double roundtrip is the problem you can send the two queries
together, and take care in your code to send the first only once per
user request

cur.execute("""
    SELECT set_config('webmq.sessionid', %s, false);
    SELECT mysp1(%s, %s)""",
    [session_id, ...])

This is one of these things that would stop work moving to
PQexecParams: that's why I want to make sure to leave open the
possibility to keep on using PQexec even if we move to the *Params
functions.

-- Daniele


Re: Session state per transaction

От
Tobias Oberstein
Дата:
> If the double roundtrip is the problem you can send the two queries
> together, and take care in your code to send the first only once per
> user request
>
> cur.execute("""
>      SELECT set_config('webmq.sessionid', %s, false);
>      SELECT mysp1(%s, %s)""",
>      [session_id, ...])
>

Great! That's sufficient. I wasn't aware concatenating statements works
like that.

> This is one of these things that would stop work moving to
> PQexecParams: that's why I want to make sure to leave open the
> possibility to keep on using PQexec even if we move to the *Params
> functions.

Guess you refer to (which I here reproduce for others who might wonder):

"The primary advantage of PQexecParams over PQexec is that parameter
values can be separated from the command string, thus avoiding the need
for tedious and error-prone quoting and escaping.

Unlike PQexec, PQexecParams allows at most one SQL command in the given
string. (There can be semicolons in it, but not more than one nonempty
command.) This is a limitation of the underlying protocol, but has some
usefulness as an extra defense against SQL-injection attacks."

http://www.postgresql.org/docs/9.2/static/libpq-exec.html

===

Btw: does Psycopg currently use PQprepare/PQexecPrepared ?

Thanks for your help,
Tobias

>
> -- Daniele
>



Re: Session state per transaction

От
Daniele Varrazzo
Дата:
On Wed, Sep 26, 2012 at 8:53 PM, Tobias Oberstein
<tobias.oberstein@gmail.com> wrote:

>> This is one of these things that would stop work moving to
>> PQexecParams: that's why I want to make sure to leave open the
>> possibility to keep on using PQexec even if we move to the *Params
>> functions.
>
> Guess you refer to (which I here reproduce for others who might wonder):
>
> Unlike PQexec, PQexecParams allows at most one SQL command in the given
> string.
>
> http://www.postgresql.org/docs/9.2/static/libpq-exec.html

Correct.

> Btw: does Psycopg currently use PQprepare/PQexecPrepared ?

Not yet: note that these function are very similar in signature to
PQexecParams: supporting the latter would bring us closer to use
prepare statement.

Note 2: you can still prepare a statement in sql using the PREPARE
statement, and this can currently be used by psycopg.

But for both the methods, one question remains: how to use statement
preparation? prepare() at each execute() seems overkilling if you have
to execute a statement only once. An obvious place that would benefit
of preparation is executemany(): we could prepare the query once on
the query and execPrepared once for each item.

Still there is an important use case: in a connection, using one or
several cursors, the same query could be repeated over and over. So
what?

- should we have a prepare() method to be called to manually prepare a
query? How to refer to the prepared query? Should prepare return a
name/opaque object? Should it just intern the string and detect that
the same query is used by execute()?
- should we have a connection subclass preparing all the queries you
throw at it (until a certain limit after which start discarding the
older ones)?

So, I'd say once we know how we would use a prepare/execute feature we
can implement it. In the meanwhile the feature can be somewhat
prototyped by subclassing connection and cursor and mangling the
queries with a PREPARE statement, with which we can use the current
psycopg parameters adaptation.

-- Daniele


Re: Session state per transaction

От
Federico Di Gregorio
Дата:
On 27/09/2012 00:58, Daniele Varrazzo wrote:
[snip]
> - should we have a prepare() method to be called to manually prepare a
> query? How to refer to the prepared query? Should prepare return a
> name/opaque object? Should it just intern the string and detect that
> the same query is used by execute()?
> - should we have a connection subclass preparing all the queries you
> throw at it (until a certain limit after which start discarding the
> older ones)?

Preparing statements is useful only in a limited set of scenarios and we
should give the user as much control over them as possible. I'd use an
opaque object that keeps a reference to the connection and to the
prepared statement (in a sense it is just a smart cursor/connection
proxy, all the logic will continue to be there). The opaque object
should expose at least execute() and executemany() variations taking
only the parameters and expose the .connection and .cursor for
everything else (or we can proxy more than that...) You'll do:

prep = conn.prepare("INSERT INTO bla VALUES (%s, %s)")
prep.execute(("foo", "bar"))
prep.execute(("baz", "gaz"))
prep.connection.commit()

> So, I'd say once we know how we would use a prepare/execute feature we
> can implement it. In the meanwhile the feature can be somewhat
> prototyped by subclassing connection and cursor and mangling the
> queries with a PREPARE statement, with which we can use the current
> psycopg parameters adaptation.

Yes, we can probably have prepared statements in Python only.

federico

--
Federico Di Gregorio                         federico.digregorio@dndg.it
Studio Associato Di Nunzio e Di Gregorio                  http://dndg.it
 La macchina virtuale elabora quindi dati adempiendo le sue funzioni
  specifiche senza esistere nella realtà degli oggetti.  -- uno studente


Re: Session state per transaction

От
Tobias Oberstein
Дата:
Hi Daniele,

>> Btw: does Psycopg currently use PQprepare/PQexecPrepared ?
>
> Not yet: note that these function are very similar in signature to
> PQexecParams: supporting the latter would bring us closer to use
> prepare statement.
>
> Note 2: you can still prepare a statement in sql using the PREPARE
> statement, and this can currently be used by psycopg.
>
> But for both the methods, one question remains: how to use statement
> preparation? prepare() at each execute() seems overkilling if you have
> to execute a statement only once. An obvious place that would benefit
> of preparation is executemany(): we could prepare the query once on

Agreed. executemany is definitely a use case.

> the query and execPrepared once for each item.

My use case is this: all DB access is via Stored Procedures (SP) on a
pool of long lived DB connections. The set of eligible SPs is determined
in advance. Hence, something that does not lead to reparsing/replanning
of the SP call SELECT statements on every execution is desirable.

I notice there is

http://www.postgresql.org/docs/9.2/static/libpq-fastpath.html

Does Psycopg support this?

Is cursor.callproc using that under the hood?

(This won't allow me to do the "multiple statements" trick for setting
the per-transaction state .. but I may find another way to achieve the
latter).

The pqlib docs mention:

"This interface is somewhat obsolete, as one can achieve similar
performance and greater functionality by setting up a prepared statement
to define the function call. Then, executing the statement with binary
transmission of parameters and results substitutes for a fast-path
function call."

So it can be emulated, but one needs to do multiple things (not only
prepared statement)

>
> Still there is an important use case: in a connection, using one or
> several cursors, the same query could be repeated over and over. So
> what?
>
> - should we have a prepare() method to be called to manually prepare a
> query? How to refer to the prepared query? Should prepare return a
> name/opaque object? Should it just intern the string and detect that
> the same query is used by execute()?

+1 for returning an opaque object

I can build query string (plain SQL with placeholders before adaption)
to prepared statement object mapping in 3 lines of I need it on top.

> - should we have a connection subclass preparing all the queries you
> throw at it (until a certain limit after which start discarding the
> older ones)?

Mmh. As a convenience class, why not? Seems neat.

>
> So, I'd say once we know how we would use a prepare/execute feature we
> can implement it. In the meanwhile the feature can be somewhat
> prototyped by subclassing connection and cursor and mangling the
> queries with a PREPARE statement, with which we can use the current
> psycopg parameters adaptation.
>
> -- Daniele
>

Cheers,
Tobias


Re: Session state per transaction

От
Federico Di Gregorio
Дата:
On 27/09/2012 09:35, Tobias Oberstein wrote:
> My use case is this: all DB access is via Stored Procedures (SP) on a
> pool of long lived DB connections. The set of eligible SPs is determined
> in advance. Hence, something that does not lead to reparsing/replanning
> of the SP call SELECT statements on every execution is desirable.
>
> I notice there is
>
> http://www.postgresql.org/docs/9.2/static/libpq-fastpath.html
>
> Does Psycopg support this?

Nope.

federico

--
Federico Di Gregorio                         federico.digregorio@dndg.it
Studio Associato Di Nunzio e Di Gregorio                  http://dndg.it
            Bhoe, bhe, bhe. Sono brutto e cattivo. Brutto lama! -- Cuzco


Re: Session state per transaction

От
Tobias Oberstein
Дата:
Am 27.09.2012 09:47, schrieb Federico Di Gregorio:
> On 27/09/2012 09:35, Tobias Oberstein wrote:
>> My use case is this: all DB access is via Stored Procedures (SP) on a
>> pool of long lived DB connections. The set of eligible SPs is determined
>> in advance. Hence, something that does not lead to reparsing/replanning
>> of the SP call SELECT statements on every execution is desirable.
>>
>> I notice there is
>>
>> http://www.postgresql.org/docs/9.2/static/libpq-fastpath.html
>>
>> Does Psycopg support this?
>
> Nope.

;(

Then I am definitely interested in helping bringing support for prepared
stuff and also fastpath-SP-calls to Pscyopg (since the API for both seem
to be somewhat similar).

Tobias

>
> federico
>