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

ВНИМАНИЕ!
В релизе QHB 1.5.2 эта функциональность является экспериментальной, ее использование в установке на производственной среде не рекомендовано.

Описание

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


Функции qhb-variables

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

Функция
Описание
var_set ( пакет text, переменная text, значение (non)anynonarray, is_transactional bool default false ) → void
Установка значения переменной, в том числе обновление.
var_get ( пакет text, переменная text, тип_переменной (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 ( пакет text, переменная text, запись record, is_transactional bool default false ) → void
Вставляет новую запись в табличную переменную. Первый столбец записи является первичным ключом. Возвращает ошибку, если запись с таким ключом уже есть или запись имеет другую сигнатуру.
var_update ( пакет text, переменная text, запись record ) → boolean
Обновляет запись в табличной переменной, опираясь на поле ключа. Возвращает true, если запись была обновлена. Если сигнатура переменной отличается от предоставленной строки, то возникнет ошибка.
var_delete ( пакет text, переменная text, значение anynonarray ) → boolean
Удаляет запись по ключу. Возвращает true, если запись была удалена.
var_select ( пакет text, переменная text ) → set of record
Возвращает текущее значение переменной.
var_select ( пакет text, переменная text, значение anynonarray ) → record
Возвращает текущее значение переменной по ключу.
var_select ( пакет text, переменная text, значение 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 ( пакет text, переменная text, is_record bool ) → bool
Возвращает true, если переменная существует в данном пакете.
var_exists ( пакет text ) → bool
Возвращает true, если пакет существует.
var_remove ( пакет text, переменная text, is_record bool) → void
Удаляет переменную по имени. Если переменной или пакета не существует, то будет возвращена ошибка.
var_remove ( пакет text ) → void
Удаляет пакет со всеми переменными внутри него. Если пакета не существует, то будет возвращена ошибка.
var_free () → void
Удаляет все пакеты со всеми переменными внутри.
var_list () → table ( пакет text, переменная text, тип_переменной text, is_transactional bool )
Возвращает список всех пакетов и переменных с указанием их транзакционности и типа.
var_stats () → table ( выделенная_память 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); -- успешно
 var_set
---------

(1 row)

-- SELECT var_set('pack', 'var_transactional', 1::text, true); -- Ошибка
COMMIT;
SELECT var_set('pack', 'var_transactional', 1::text, true); -- Успешно
 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 создается мгновенная копия данных, поэтому на результат SELECT var_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` does not exist in package `pack`
-- ОШИБКА:  var_select: Существование: В пакете `pack` не существует переменной с именем `var`
ROLLBACK;
SELECT var_select('pack', 'var');
 var_select
------------
 (2,third)
 (1,second)
 (0,first)
(3 rows)