Переменные уровня сессии - 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)