Переменные уровня сессии - qhb-variables

Внимание!
В релизе QHB 1.5.1 эта функциональность является экспериментальной, её использование в установке на производственной среде не рекомендовано.

Описание

Qhb-variables позволяет создавать переменные, которые будут удалены по завершении сессии. Этот механизм является альтернативой временным таблицам в местах, где их производительности недостаточно. Переменные хранятся в пакетах. Существует два типа переменных: скалярные и табличные. В скалярных переменных за раз можно хранить только одно значение любого типа, в табличных же можно хранить множество строк. Переменные также подразделяются на транзакционные и нетранзакционные. Состояние первых не зависит от результата транзакции, а вторые меняют своё значение учитывая COMMIT и ROLLBACK. Тип переменной нельзя менять после создания до полного удаления (в случае транзакционной переменной до COMMIT'а).

Функции qhb-variables

Функции для работы со скалярными переменными

ФункцияВозвращаемое значениеОписание
var_set(package text, variable text, value (non)anynonarray, is_transactional bool default false)voidУстановка значения переменной, в том числе обновление
var_get(package text, variable text, var_type (non)anynonarray, strict bool default true)anynonarrayПолучение значения переменной

Примеры использования

SELECT var_set('pack', 'var', 0);
 var_set 
---------

(1 row)

SELECT var_get('pack', 'var', NULL::int);
 var_get 
---------
       0
(1 row)
BEGIN;
SELECT var_set('pack', 'var', 'before savepoint'::text, true);
 var_set 
---------
 
(1 row)

SAVEPOINT zero;
SELECT var_set('pack', 'var', 'after savepoint'::text, true);
 var_set 
---------
 
(1 row)

SELECT var_get('pack', 'var', NULL::text);
     var_get     
-----------------
 after savepoint
(1 row)

ROLLBACK TO zero;
SELECT var_get('pack', 'var', NULL::text);
     var_get      
------------------
 before savepoint
(1 row)

Функции для работы с табличными переменными

ФункцияВозвращаемое значениеОписание
var_insert(package text, variable text, r record, is_transactional bool default false)voidВставляет новую запись в табличную переменную. Первая колонка записи является первичным ключом. Возвращает ошибку, если запись с таким ключом уже есть или запись имеет другую сигнатуру.
var_update(package text, variable text, r record)booleanОбновляет запись в табличной переменной, опираясь на ключевое поле. Возвращает true, если запись была обновлена. Если сигнатура переменной отличается от предоставленной строки, то возникнет ошибка.
var_delete(package text, variable text, value anynonarray)booleanУдаляет запись по ключу. Возвращает true, если запись была удалена.
var_select(package text, variable text)set of recordВозвращает текущее значение переменной.
var_select(package text, variable text, value anynonarray)recordВозвращает текущее значение переменной по ключу.
var_select(package text, variable text, value anyarray)set of recordВозвращает текущее значение переменной по ключам.

Примеры использования

SELECT var_insert('pac', 'var', ROW(NULL::int, 0::int));
 var_insert 
------------
 
(1 row)

SELECT var_insert('pac', 'var', ROW(0::int, 1::int));
 var_insert 
------------
 
(1 row)

SELECT var_insert('pac', 'var1', ROW(0::int, 2::int));
 var_insert 
------------
 
(1 row)

SELECT var_insert('pac', 'var1', ROW(2::int, 2::int));
 var_insert 
------------
 
(1 row)

SELECT var_select('pac', 'var');
 var_select 
------------
 (,0)
 (0,1)
(2 rows)

SELECT var_select('pac', 'var1', 0);
 var_select 
------------
 (0,2)
(1 row)

SELECT var_select('pac', 'var1', ARRAY[0, 1]);
 var_select 
------------
 (0,2)
(1 row)

Общие функции переменных

ФункцияВозвращаемое значениеОписание
var_exists(package text, variable text, is_record bool)boolВозвращает true, если переменная существует в данном пакете.
var_exists(package text)boolВозвращает true, если пакет существует.
var_remove(package text, variable text, is_record bool)voidУдаляет переменную по имени. Если переменной или пакета не существует, то будет возвращена ошибка.
var_remove(package text)voidУдаляет пакет со всеми переменными внутри него. Если пакета не существует, то будет возвращена ошибка.
var_free()voidУдаляет все пакеты со всеми переменными внутри.
var_list()table(package text, variable text, variable_type text,is_transactional bool)Возвращает список всех пакетов и переменных с указанием их транзакционности и типа.
var_stats()table(allocated_memory bigint)Возвращает занимаемое место переменными. Обратите внимание, что эта функция возвращает выделенное место, которое может быть использовано не полностью. За более подробной информацией используйте pg_backend_memory_contexts()

Примечание
В одном пакете могут быть две переменные с одинаковым названием, но только при условии, что одна из них табличная, а другая скалярная. По умолчанию функции var_exists и var_remove удаляют обе переменные, если вам нужно выбрать только табличную переменную, передайте последним параметром true, для удаления только скалярной переменной передайте false. Этот параметр является опциональным.

BEGIN;
SELECT var_set('pack', 'var', 0, true);
 var_set 
---------
 
(1 row)

SELECT var_insert('pack', 'var', ROW(0::int, 1::int), true);
 var_insert 
------------
 
(1 row)

SELECT * FROM var_list();
 package | variable |         variable_type         | is_transactional 
---------+----------+-------------------------------+------------------
 pack    | var      | Scalar(integer)               | t
 pack    | var      | Record(row(integer, integer)) | t
(2 rows)

SAVEPOINT first;
SELECT var_remove('pack', 'var', true);
 var_remove 
------------
 
(1 row)

SELECT var_exists('pack', 'var', true);
 var_exists 
------------
 f
(1 row)

SELECT var_exists('pack', 'var', false);
 var_exists 
------------
 t
(1 row)

SELECT var_exists('pack', 'var');
 var_exists 
------------
 t
(1 row)

SELECT var_remove('pack', 'var');
 var_remove 
------------
 
(1 row)

SELECT var_exists('pack', 'var', false);
 var_exists 
------------
 f
(1 row)

ROLLBACK TO first;
SELECT var_exists('pack', 'var');
 var_exists 
------------
 t
(1 row)

Примеры использования:

BEGIN;
SELECT var_set('pack', 'var', 'non-transactional'::text);
 var_set 
---------
 
(1 row)

SELECT var_set('pack', 'var1', 'transactional'::text, true);
 var_set 
---------
 
(1 row)

SELECT * FROM var_list();
 package | variable | variable_type | is_transactional 
---------+----------+---------------+------------------
 pack    | var      | Scalar(text)  | f
 pack    | var1     | Scalar(text)  | t
(2 rows)

SAVEPOINT first;
SELECT var_free();
 var_free 
----------
 
(1 row)

SELECT var_stats();
 var_stats 
-----------
      8192
(1 row)

SELECT * FROM var_list();
 package | variable | variable_type | is_transactional 
---------+----------+---------------+------------------
(0 rows)

ROLLBACK TO first;
SELECT * FROM var_list();
 package | variable | variable_type | is_transactional 
---------+----------+---------------+------------------
 pack    | var1     | Scalar(text)  | t
(1 row)

Другие примеры использования

Вставка из таблицы и деструктуризация SELECT

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second');
SELECT var_insert('pack', 'var', test_table) FROM test_table;
 var_insert 
------------
 
 
(2 rows)

SELECT var_select('pack', 'var');
 var_select 
------------
 (1,second)
 (0,first)
(2 rows)

SELECT * FROM var_select('pack', 'var') AS (id int, t text);
 id |   t    
----+--------
  1 | second
  0 | first
(2 rows)

Изменение типа при полном удалении переменной

SELECT var_set('pack', 'var', 1::int);
 var_set 
---------
 
(1 row)

SELECT var_set('pack', 'var_transactional', 1::int, true);
 var_set 
---------
 
(1 row)

SELECT * FROM var_list();
 package |     variable      |  variable_type  | is_transactional 
---------+-------------------+-----------------+------------------
 pack    | var_transactional | Scalar(integer) | t
 pack    | var               | Scalar(integer) | f
(2 rows)

BEGIN;
SELECT var_free();
 var_free 
----------
 
(1 row)

SELECT var_set('pack', 'var', 1::text); -- ok
 var_set 
---------
 
(1 row)

-- SELECT var_set('pack', 'var_transactional', 1::text, true); -- Err
COMMIT;
SELECT var_set('pack', 'var_transactional', 1::text, true); -- Ok
 var_set 
---------
 
(1 row)

SELECT * FROM var_list();
 package |     variable      | variable_type | is_transactional 
---------+-------------------+---------------+------------------
 pack    | var_transactional | Scalar(text)  | t
 pack    | var               | Scalar(text)  | f
(2 rows)

Этот пример работает потому что переменные были безвозвратно удалены при вызове var_free.

Сохранение элементов таблицы

При вставке в переменную создаётся независимая копия данных.

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second'), (2, 'third');
SELECT var_insert('pack', 'var', test_table) FROM test_table WHERE id < 2;
 var_insert 
------------
 
 
(2 rows)

DROP TABLE test_table;
CREATE TABLE test_table AS SELECT * FROM var_select('pack', 'var') AS (id int, t text);
SELECT * FROM test_table;
 id |   t    
----+--------
  1 | second
  0 | first
(2 rows)

SELECT + CURSOR

При вызове var_select, создаётся мгновенная копия данных, поэтому на результат SELECTvar_update не повлияет. Однако, если переменная была удалена, тогда получение оставшихся данных станет невозможным.

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second'), (2, 'third');
SELECT var_insert('pack', 'var', test_table, true) FROM test_table;
 var_insert 
------------
 
 
 
(3 rows)

BEGIN;
DECLARE r1_cur CURSOR FOR SELECT var_select('pack', 'var');
FETCH 1 in r1_cur;
 var_select 
------------
 (2,third)
(1 row)

SELECT var_remove('pack');
 var_remove 
------------
 
(1 row)

FETCH 1 in r1_cur;
ERROR:  var_select: Existence: Variable with name `var` doesn't exist in package `pack`
ROLLBACK;
SELECT var_select('pack', 'var');
 var_select 
------------
 (2,third)
 (1,second)
 (0,first)
(3 rows)