Thread: Any form of connection-level "session variable" ?

Any form of connection-level "session variable" ?

From
John McCawley
Date:
This is revisiting a problem I posed to this group a month or so ago
regarding separating different users' data through schema views.  The
solution we're using is based on a suggestion we received here:

http://archives.postgresql.org/pgsql-general/2006-12/msg00037.php

Everything is working great with the exception of performance.  One of
our tables has close to a million records, and the overhead of calling
the get_client_id() function per row is eating us alive, I assume
because it is having to per-row call a select  on a table to retrieve
the proper ID within the function.

Is there any way I could establish this ID initially in some sort of
connection-level variable, and from this point on reference that
variable?  What I'm thinking is something like the following:

select initialize_client_id(); //This would actually hit the DB to
retrieve the proper ID for the logged in user

//Now, in the view get_client_id() retrieves the earlier established
"variable" instead of hitting the DB
select foo,bar FROM tbl_foo WHERE client_id = get_client_id();

Am I incorrect in assuming that the statement:

select foo from tbl_bar WHERE client_id = get_client_id();

will call get_client_id() for every row?

John

Re: Any form of connection-level "session variable" ?

From
John McCawley
Date:
I think I got it:

CREATE FUNCTION new_get_emp_id() RETURNS INTEGER AS $$ select emp_id
from secureview.tbl_employee where username = (SELECT current_user) $$
LANGUAGE SQL IMMUTABLE;

I made the function immutable so it only calls it once, therefore no
longer requiring a call per-row.



John McCawley wrote:

> This is revisiting a problem I posed to this group a month or so ago
> regarding separating different users' data through schema views.  The
> solution we're using is based on a suggestion we received here:
>
> http://archives.postgresql.org/pgsql-general/2006-12/msg00037.php
>
> Everything is working great with the exception of performance.  One of
> our tables has close to a million records, and the overhead of calling
> the get_client_id() function per row is eating us alive, I assume
> because it is having to per-row call a select  on a table to retrieve
> the proper ID within the function.
>
> Is there any way I could establish this ID initially in some sort of
> connection-level variable, and from this point on reference that
> variable?  What I'm thinking is something like the following:
>
> select initialize_client_id(); //This would actually hit the DB to
> retrieve the proper ID for the logged in user
>
> //Now, in the view get_client_id() retrieves the earlier established
> "variable" instead of hitting the DB
> select foo,bar FROM tbl_foo WHERE client_id = get_client_id();
> Am I incorrect in assuming that the statement:
>
> select foo from tbl_bar WHERE client_id = get_client_id();
>
> will call get_client_id() for every row?
>
> John
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: if posting/reading through Usenet, please send an appropriate
>       subscribe-nomail command to majordomo@postgresql.org so that your
>       message can get through to the mailing list cleanly


Re: Any form of connection-level "session variable" ?

From
Tom Lane
Date:
John McCawley <nospam@hardgeus.com> writes:
> I think I got it:
> CREATE FUNCTION new_get_emp_id() RETURNS INTEGER AS $$ select emp_id
> from secureview.tbl_employee where username = (SELECT current_user) $$
> LANGUAGE SQL IMMUTABLE;
> I made the function immutable so it only calls it once, therefore no
> longer requiring a call per-row.

Since it's obviously *not* immutable, this will come back to bite you
sooner or later (probably sooner).  Labeling it STABLE would be
reasonable, although I'm not certain how much that helps you.  Do you
have indexes on the columns it's being compared to?

            regards, tom lane

Re: Any form of connection-level "session variable" ?

From
Erik Jones
Date:
Tom Lane wrote:
> John McCawley <nospam@hardgeus.com> writes:
>
>> I think I got it:
>> CREATE FUNCTION new_get_emp_id() RETURNS INTEGER AS $$ select emp_id
>> from secureview.tbl_employee where username = (SELECT current_user) $$
>> LANGUAGE SQL IMMUTABLE;
>> I made the function immutable so it only calls it once, therefore no
>> longer requiring a call per-row.
>>
>
> Since it's obviously *not* immutable, this will come back to bite you
> sooner or later (probably sooner).  Labeling it STABLE would be
> reasonable, although I'm not certain how much that helps you.  Do you
> have indexes on the columns it's being compared to?
>
Besides, a temp table is pretty much a session variable.

--
erik jones <erik@myemma.com>
software development
emma(r)


Re: Any form of connection-level "session variable" ?

From
Tom Lane
Date:
>> John McCawley <nospam@hardgeus.com> writes:
>>> CREATE FUNCTION new_get_emp_id() RETURNS INTEGER AS $$ select emp_id
>>> from secureview.tbl_employee where username = (SELECT current_user) $$
>>> LANGUAGE SQL IMMUTABLE;

One other thing: that no-op sub-SELECT isn't doing anything for you
except adding overhead ... you might've needed it to allow index use
back in 7.2 or before, but not in any remotely modern PG release.

            regards, tom lane

Re: Any form of connection-level "session variable" ?

From
John McCawley
Date:
I tried stable, and that didn't help at all.  How long does PostgreSQL
maintain the "state" of the function when using immutable?  Until a
restart?  Until the end of a session?  Until the function is dropped and
re-added?

While this value isn't 100% absolutely positively guaranteed to never
change, it is pretty dang close.  I have a strict correlation between
the username that someone uses to login and the ID returned.  Changing
someone's username would break a whole lot more than this one app on the
project...



Erik Jones wrote:

> Tom Lane wrote:
>
>> John McCawley <nospam@hardgeus.com> writes:
>>
>>
>>> I think I got it:
>>> CREATE FUNCTION new_get_emp_id() RETURNS INTEGER AS $$ select emp_id
>>> from secureview.tbl_employee where username = (SELECT current_user)
>>> $$ LANGUAGE SQL IMMUTABLE;
>>> I made the function immutable so it only calls it once, therefore no
>>> longer requiring a call per-row.
>>>
>>
>>
>> Since it's obviously *not* immutable, this will come back to bite you
>> sooner or later (probably sooner).  Labeling it STABLE would be
>> reasonable, although I'm not certain how much that helps you.  Do you
>> have indexes on the columns it's being compared to?
>>
>
> Besides, a temp table is pretty much a session variable.
>

Re: Any form of connection-level "session variable" ?

From
Tom Lane
Date:
John McCawley <nospam@hardgeus.com> writes:
> While this value isn't 100% absolutely positively guaranteed to never
> change, it is pretty dang close.

Counterexample: SET ROLE or SET SESSION AUTHORIZATION.

            regards, tom lane

Re: Any form of connection-level "session variable" ?

From
John McCawley
Date:
I'm not trying to be argumentative, but I honestly don't know what you
mean here...and the only reason I ask for clarification is that you are
probably trying to tell me something important :)

Tom Lane wrote:

>John McCawley <nospam@hardgeus.com> writes:
>
>
>>While this value isn't 100% absolutely positively guaranteed to never
>>change, it is pretty dang close.
>>
>>
>
>Counterexample: SET ROLE or SET SESSION AUTHORIZATION.
>
>            regards, tom lane
>
>---------------------------(end of broadcast)---------------------------
>TIP 2: Don't 'kill -9' the postmaster
>
>

Re: Any form of connection-level "session variable" ?

From
Scott Ribe
Date:
What about:

 create function set_emp_id() returns void as $$
 begin
    drop table if exists emp_1_id;
    select emp_id into temp emp_1_id from secureview.tbl_employee where
username = current_user;
 end;
 $$ language plpgsql;

 create function get_emp_id() returns int as $$
    return select emp_id from emp_1_id;
 $$ language plpgsql stable;

Call set_emp_id once on connection, then use get_emp_id thereafter. Would
that be any faster? (This is what Erik meant by " a temp table is pretty
much a session variable" in his earlier message.)

 --
Scott Ribe
scott_ribe@killerbytes.com
http://www.killerbytes.com/
(303) 722-0567 voice



Re: Any form of connection-level "session variable" ?

From
Joe Conway
Date:
Scott Ribe wrote:
> What about:
>
>  create function set_emp_id() returns void as $$
>  begin
>     drop table if exists emp_1_id;
>     select emp_id into temp emp_1_id from secureview.tbl_employee where
> username = current_user;
>  end;
>  $$ language plpgsql;
>
>  create function get_emp_id() returns int as $$
>     return select emp_id from emp_1_id;
>  $$ language plpgsql stable;
>
> Call set_emp_id once on connection, then use get_emp_id thereafter. Would
> that be any faster? (This is what Erik meant by " a temp table is pretty
> much a session variable" in his earlier message.)

Or, in similar fashion, use this:
http://www.joeconway.com/sessfunc.tar.gz
http://www.onlamp.com/pub/a/onlamp/2004/06/28/postgresql_extensions.html

HTH,

Joe

Re: Any form of connection-level "session variable" ?

From
David Fetter
Date:
On Thu, Jan 04, 2007 at 06:53:09PM -0700, Scott Ribe wrote:
> What about:
>
>  create function set_emp_id() returns void as $$
>  begin
>     drop table if exists emp_1_id;
>     select emp_id into temp emp_1_id from secureview.tbl_employee where
> username = current_user;
>  end;
>  $$ language plpgsql;
>
>  create function get_emp_id() returns int as $$
>     return select emp_id from emp_1_id;
>  $$ language plpgsql stable;
>
> Call set_emp_id once on connection, then use get_emp_id thereafter.
> Would that be any faster? (This is what Erik meant by "a temp table
> is pretty much a session variable" in his earlier message.)

You can use PL/Perl's %_SHARED hash
<http://www.postgresql.org/docs/current/static/plperl-global.html> to
store session-long variables, and you don't need to worry about
cleanup :)  Similar things exist in PL/Python and possibly others.

Cheers,
D
--
David Fetter <david@fetter.org> http://fetter.org/
phone: +1 415 235 3778        AIM: dfetter666
                              Skype: davidfetter

Remember to vote!

Re: Any form of connection-level "session variable" ?

From
Scott Ribe
Date:
> ...and you don't need to worry about cleanup...

What cleanup? Temp tables are dropped on connection close.

--
Scott Ribe
scott_ribe@killerbytes.com
http://www.killerbytes.com/
(303) 722-0567 voice



Re: Any form of connection-level "session variable" ?

From
ptjm@interlog.com (Patrick TJ McPhee)
Date:
In article <459D4FA0.4020202@hardgeus.com>,
John McCawley <nospam@hardgeus.com> wrote:

% Is there any way I could establish this ID initially in some sort of
% connection-level variable, and from this point on reference that
% variable?

We do this sort of thing using a custom C function, but I've been
thinking lately you could use a custom variable class to do the same
thing.

Put
 custom_variable_classes = 'session'

in your postgresql.conf, and you can have
 set session.myid = 23;

then retrieve the value either by joining to pg_settings or using
 show session.myid;

You can perform per-user initialisation with
 alter user set session.myid = 23;

Which allows the value to persist between sessions.

I haven't done anything with this idea so I can't say how well it
works or whether there are downsides to it.
--

Patrick TJ McPhee
North York  Canada
ptjm@interlog.com

Re: Any form of connection-level "session variable" ?

From
ptjm@interlog.com (Patrick TJ McPhee)
Date:
In article <459D4FA0.4020202@hardgeus.com>,
John McCawley <nospam@hardgeus.com> wrote:

% Is there any way I could establish this ID initially in some sort of
% connection-level variable, and from this point on reference that
% variable?

We do this sort of thing using a custom C function, but I've been
thinking lately you could use a custom variable class to do the same
thing.

Put
 custom_variable_classes = 'session'

in your postgresql.conf, and you can have
 set session.myid = 23;

then retrieve the value either by joining to pg_settings or using
 show session.myid;

You can perform per-user initialisation with
 alter user set session.myid = 23;

Which allows the value to persist between sessions.

I haven't done anything with this idea so I can't say how well it
works or whether there are downsides to it.
--

Patrick TJ McPhee
North York  Canada
ptjm@interlog.com