📜 ⬆️ ⬇️

PostgreSQL. User data within the session

Recently, I had an interesting task of storing some data in a PostgreSQL database session (TTL = lifetime of a single connection to the database). The original question was:
Is it possible instead of such a construction:
 some_procedure1 (user_id, param1, ..., paramN);
 ...
 some_procedureX (user_id, param1, ..., paramN);

use this:
 set_user (id);
 some_procedure1 (param1, ..., paramN);
 ....
 some_procedureX (param1, ..., paramN);

those. use a certain global variable within the session to store the value of the user ID that will be available to all procedures within the database.
Rummaging in Google, asking around on the forum, I found not even one solution, but as many as 3! What I share with you ...

First option. Using a temporary table.


As the documentation says, I quote:
CREATE TEMPORARY TABLE command for each temporary table to be used.

for each session we need to re-create a temporary table. Great, what we needed! Let's try ...
An example is taken from the forum sql.ru :
 CREATE OR REPLACE FUNCTION set_var (p_var_name varchar, p_var_value varchar) RETURNS void AS
 $$
 DECLARE
   v_cnt integer;
 BEGIN
   SELECT Count (pc.relname) into v_cnt
   FROM pg_catalog.pg_class pc, pg_namespace pn
   WHERE pc.relname = 'session_var_tbl'
     AND pc.relnamespace = pn.oid 
     AND pn.oid = pg_my_temp_schema ();
   IF v_cnt = 0 THEN
     EXECUTE 'CREATE GLOBAL TEMPORARY TABLE session_var_tbl (var_name varchar (100) not null, var_value varchar (100)) <br />
 ON COMMIT preserve ROWS ';
   END IF;
   UPDATE session_var_tbl 
   SET var_value = p_var_value
   WHERE var_name = p_var_name;
   IF NOT FOUND THEN
     INSERT INTO session_var_tbl (var_name, var_value)
     VALUES (p_var_name, p_var_value);
   END IF;
 END;
 $$
 LANGUAGE 'plpgsql';

 CREATE OR REPLACE FUNCTION get_var (p_var_name varchar) RETURNS varchar AS
 $$
 DECLARE
   v_cnt integer;
   v_result varchar (100);
 BEGIN
   SELECT Count (pc.relname) 
   INTO v_cnt
   FROM pg_catalog.pg_class pc, pg_namespace pn
   WHERE pc.relname = 'session_var_tbl' 
     AND pc.relnamespace = pn.oid 
     AND pn.oid = pg_my_temp_schema ();
   IF v_cnt = 0 THEN
     v_result: = null;
   ELSE
     SELECT var_value
     INTO v_result
     FROM session_var_tbl
     WHERE var_name = p_var_name;
     IF NOT FOUND THEN
       v_result: = null;
     END IF;
   END IF;
   RETURN v_result;
 END;
 $$
 LANGUAGE 'plpgsql';

The advantage of this method is that there is no need for additional settings or modules, and the PL / PgSQL language is almost always there.
The disadvantage of this method is the need for a decent amount of additional operations that perform the above procedures, including queries to system tables.

The second option. Using the globaldata array.


As the documentation tells us again, we can use in such languages ​​PL / Perl, PL / Tcl, PL / Python a special array of data visible within the session.
An example is taken from official documentation :
 CREATE OR REPLACE FUNCTION set_var (name text, val text) RETURNS text AS $$
     if ($ _SHARED {$ _ [0]} = $ _ [1]) {
         return 'ok';
     } else {
         return "cannot set shared variable $ _ [0] to $ _ [1]";
     }
 $$ LANGUAGE plperl;

 CREATE OR REPLACE FUNCTION get_var (name text) RETURNS text AS $$
     return $ _SHARED {$ _ [0]};
 $$ LANGUAGE plperl;

The advantage of this method is the minimum number of operations and ease of use.
The disadvantage is the need for an additional language module for PostgreSQL (postgresql-plperl). It needs to be reinstalled independently, and on shared hosting, it is often impossible.
')

The third option. Use Customized Options.


Several non-standard use of additional parameters in PostgreSQL. The configuration variable custom_variable_classes was originally intended to create additional classes of configuration variables that PostgreSQL additional modules can use. But in the course of the research it turned out that if you do not define variables in the class from postgresql.conf, then they “live” exactly the current session.
To use this method, you need to register in postgresql.conf the appropriate setting:
 custom_variable_classes = 'usrvar'

Example:
 CREATE OR REPLACE FUNCTION set_var (p_var_name varchar, p_var_value varchar) RETURNS void AS
 $ body $
 BEGIN
   PERFORM set_config ('usrvar.' || p_var_name, p_var_value, false);
 END;
 $ body $
 LANGUAGE 'plpgsql';

 CREATE OR REPLACE FUNCTION get_var (p_var_name varchar) RETURNS varchar AS
 $ body $
 DECLARE
   v_var_value varchar;
 BEGIN
   SELECT INTO v_var_value current_setting ('usrvar.' || p_var_name);
   RETURN v_var_value;
 EXCEPTION
 WHEN syntax_error_or_access_rule_violation THEN
   v_var_value: = null;
   RETURN v_var_value;
 END;
 $ body $
 LANGUAGE 'plpgsql';

The advantage of this method is the use of PostgreSQL internal resources and the absence of the need to use additional languages ​​or tables.
The disadvantage is the need to access the server configuration file.
Well, that's probably all. The choice of one of the three solutions depends entirely on the capabilities of your database installation. For myself, I chose the 3rd solution.

Source: https://habr.com/ru/post/44015/


All Articles