From 5510083cbf97644f3e38d66fa7fa6332ee76a808 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 13 Apr 2018 19:06:31 +0300 Subject: [PATCH 1/8] Version 1.1: Add support of transactions and savepoints --- Makefile | 4 +- README.md | 118 +- expected/pg_variables.out | 102 +- expected/pg_variables_any.out | 90 +- expected/pg_variables_trans.out | 3002 +++++++++++++++++++++++++++++++ pg_variables--1.0--1.1.sql | 68 + pg_variables--1.1.sql | 156 ++ pg_variables.c | 583 +++++- pg_variables.control | 2 +- pg_variables.h | 40 +- pg_variables_record.c | 68 +- sql/pg_variables.sql | 1 + sql/pg_variables_trans.sql | 648 +++++++ 13 files changed, 4710 insertions(+), 172 deletions(-) create mode 100644 expected/pg_variables_trans.out create mode 100644 pg_variables--1.0--1.1.sql create mode 100644 pg_variables--1.1.sql mode change 100644 => 100755 pg_variables.c mode change 100644 => 100755 pg_variables.h create mode 100644 sql/pg_variables_trans.sql diff --git a/Makefile b/Makefile index 49592e0..da53a06 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ MODULE_big = pg_variables OBJS = pg_variables.o pg_variables_record.o $(WIN32RES) EXTENSION = pg_variables -DATA = pg_variables--1.0.sql +DATA = pg_variables--1.0.sql pg_variables--1.1.sql pg_variables--1.0--1.1.sql PGFILEDESC = "pg_variables - sessional variables" -REGRESS = pg_variables pg_variables_any +REGRESS = pg_variables pg_variables_any pg_variables_trans ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/README.md b/README.md index ee9d34a..7a9f2b5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The **pg_variables** module provides functions to work with variables of various types. Created variables live only in the current user session. -Note that the module does **not support transactions and savepoints**. For +Note that the module does **not support transactions and savepoints by default**. For example: ```sql @@ -15,13 +15,29 @@ SELECT pgv_set('vars', 'int2', 102); ROLLBACK; SELECT * FROM pgv_list() order by package, name; - package | name ----------+------ - vars | int1 - vars | int2 + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f (2 rows) ``` +But if variable created with flag **is_transactional**, it does: +```sql +BEGIN; +SELECT pgv_set('vars', 'trans_int', 101, true); +SAVEPOINT sp1; +SELECT pgv_set('vars', 'trans_int', 102, true); +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars', 'trans_int', NULL::int); + + pgv_get +--------- + 101 +(1 row) +``` + ## License This module available under the same license as @@ -72,7 +88,7 @@ ERROR: variable "int1" requires "integer" value Function | Returns -------- | ------- -`pgv_set(package text, name text, value anynonarray)` | `void` +`pgv_set(package text, name text, value anynonarray, is_transactional bool default false)` | `void` `pgv_get(package text, name text, var_type anynonarray, strict bool default true)` | `anynonarray` ## **Deprecated** scalar variables functions @@ -81,49 +97,49 @@ Function | Returns Function | Returns -------- | ------- -`pgv_set_int(package text, name text, value int)` | `void` +`pgv_set_int(package text, name text, value int, is_transactional bool default false)` | `void` `pgv_get_int(package text, name text, strict bool default true)` | `int` ### Text variables Function | Returns -------- | ------- -`pgv_set_text(package text, name text, value text)` | `void` +`pgv_set_text(package text, name text, value text, is_transactional bool default false)` | `void` `pgv_get_text(package text, name text, strict bool default true)` | `text` ### Numeric variables Function | Returns -------- | ------- -`pgv_set_numeric(package text, name text, value numeric)` | `void` +`pgv_set_numeric(package text, name text, value numeric, is_transactional bool default false)` | `void` `pgv_get_numeric(package text, name text, strict bool default true)` | `numeric` ### Timestamp variables Function | Returns -------- | ------- -`pgv_set_timestamp(package text, name text, value timestamp)` | `void` +`pgv_set_timestamp(package text, name text, value timestamp, is_transactional bool default false)` | `void` `pgv_get_timestamp(package text, name text, strict bool default true)` | `timestamp` ### Timestamp with timezone variables Function | Returns -------- | ------- -`pgv_set_timestamptz(package text, name text, value timestamptz)` | `void` +`pgv_set_timestamptz(package text, name text, value timestamptz, is_transactional bool default false)` | `void` `pgv_get_timestamptz(package text, name text, strict bool default true)` | `timestamptz` ### Date variables Function | Returns -------- | ------- -`pgv_set_date(package text, name text, value date)` | `void` +`pgv_set_date(package text, name text, value date, is_transactional bool default false)` | `void` `pgv_get_date(package text, name text, strict bool default true)` | `date` ### Jsonb variables Function | Returns -------- | ------- -`pgv_set_jsonb(package text, name text, value jsonb)` | `void` +`pgv_set_jsonb(package text, name text, value jsonb, is_transactional bool default false)` | `void` `pgv_get_jsonb(package text, name text, strict bool default true)` | `jsonb` ## Record variables functions @@ -142,7 +158,7 @@ raised. Function | Returns | Description -------- | ------- | ----------- -`pgv_insert(package text, name text, r record)` | `void` | Inserts a record to the variable collection. If package and variable do not exists they will be created. The first column of **r** will be a primary key. If exists a record with the same primary key the error will be raised. If this variable collection has other structure the error will be raised. +`pgv_insert(package text, name text, r record, is_transactional bool default false)` | `void` | Inserts a record to the variable collection. If package and variable do not exists they will be created. The first column of **r** will be a primary key. If exists a record with the same primary key the error will be raised. If this variable collection has other structure the error will be raised. `pgv_update(package text, name text, r record)` | `boolean` | Updates a record with the corresponding primary key (the first column of **r** is a primary key). Returns **true** if a record was found. If this variable collection has other structure the error will be raised. `pgv_delete(package text, name text, value anynonarray)` | `boolean` | Deletes a record with the corresponding primary key (the first column of **r** is a primary key). Returns **true** if a record was found. `pgv_select(package text, name text)` | `set of record` | Returns the variable collection records. @@ -158,7 +174,7 @@ Function | Returns | Description `pgv_remove(package text, name text)` | `void` | Removes the variable with the corresponding name. Required package and variable must exists, otherwise the error will be raised. `pgv_remove(package text)` | `void` | Removes the package and all package variables with the corresponding name. Required package must exists, otherwise the error will be raised. `pgv_free()` | `void` | Removes all packages and variables. -`pgv_list()` | `table(package text, name text)` | Returns set of records of assigned packages and variables. +`pgv_list()` | `table(package text, name text, is_transactional bool)` | Returns set of records of assigned packages and variables. `pgv_stats()` | `table(package text, used_memory bigint)` | Returns list of assigned packages and used memory in bytes. Note that **pgv_stats()** works only with the PostgreSQL 9.6 and newer. @@ -172,13 +188,13 @@ SELECT pgv_set('vars', 'int1', 101); SELECT pgv_set('vars', 'int2', 102); SELECT pgv_get('vars', 'int1', NULL::int); - pgv_get_int + pgv_get_int ------------- 101 (1 row) SELECT pgv_get('vars', 'int2', NULL::int); - pgv_get_int + pgv_get_int ------------- 102 (1 row) @@ -235,7 +251,7 @@ You can list packages and variables: ```sql SELECT * FROM pgv_list() order by package, name; - package | name + package | name ---------+------ vars | int1 vars | int2 @@ -253,7 +269,7 @@ SELECT * FROM pgv_stats() order by package; (1 row) ``` -You can delete variables or hole packages: +You can delete variables or whole packages: ```sql SELECT pgv_remove('vars', 'int1'); @@ -264,3 +280,67 @@ You can delete all packages and variables: ```sql SELECT pgv_free(); ``` + +If you want variables with support of transactions and savepoints, you should add flag +`is_transactional = true` as the last argument in functions `pgv_set()` +or `pgv_insert()`. +Following use cases describe behavior of transactional variables: +```sql +SELECT pgv_set('pack', 'var_text', 'before transaction block'::text, true); +BEGIN; +SELECT pgv_set('pack', 'var_text', 'before savepoint'::text, true); +SAVEPOINT sp1; +SELECT pgv_set('pack', 'var_text', 'savepoint sp1'::text, true); +SELECT pgv_get('pack', 'var_text', NULL::text); +SAVEPOINT sp2; +SELECT pgv_set('pack', 'var_text', 'savepoint sp2'::text, true); +RELEASE sp2; +SELECT pgv_get('pack', 'var_text', NULL::text); + pgv_get +--------------- + savepoint sp2 + +ROLLBACK TO sp1; +SELECT pgv_get('pack', 'var_text', NULL::text); + pgv_get +------------------ + before savepoint +(1 row) + +ROLLBACK; +SELECT pgv_get('pack', 'var_text', NULL::text); + pgv_get +-------------------------- + before transaction block + +``` +If you create variable after `BEGIN` or `SAVEPOINT` and than rollback to previous state - variable will not be exist: +```sql +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('pack', 'var_int', 122, true); +RELEASE SAVEPOINT sp2; +SELECT pgv_get('pack', 'var_int', NULL::int); +pgv_get +--------- + 122 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('pack','var_int', NULL::int); +ERROR: unrecognized variable "var_int" +COMMIT; +``` +If you created transactional variable once, you should use flag `is_transactional` every time when you want to change variable value by functions `pgv_set()`, `pgv_insert()` and deprecated setters (i.e. `pgv_set_int()`). If you try to change this option, you'll get an error: +```sql +SELECT pgv_insert('pack', 'var_record', row(123::int, 'text'::text), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('pack', 'var_record', row(456::int, 'another text'::text)); +ERROR: variable "var_record" already created as TRANSACTIONAL +``` +Functions `pgv_update()` and `pgv_delete()` do not require this flag. diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 326b3f1..7293c6c 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -645,32 +645,40 @@ SELECT pgv_select('vars2', 'j1'); ERROR: variable "j1" requires "jsonb" value -- Manipulate variables SELECT * FROM pgv_list() order by package, name; - package | name ----------+---------- - vars | d1 - vars | d2 - vars | dNULL - vars | int1 - vars | int2 - vars | intNULL - vars | jNULL - vars | num1 - vars | num2 - vars | numNULL - vars | str1 - vars | str2 - vars | strNULL - vars | ts1 - vars | ts2 - vars | tsNULL - vars | tstz1 - vars | tstz2 - vars | tstzNULL - vars2 | j1 - vars2 | j2 - vars3 | r1 + package | name | is_transactional +---------+----------+------------------ + vars | d1 | f + vars | d2 | f + vars | dNULL | f + vars | int1 | f + vars | int2 | f + vars | intNULL | f + vars | jNULL | f + vars | num1 | f + vars | num2 | f + vars | numNULL | f + vars | str1 | f + vars | str2 | f + vars | strNULL | f + vars | ts1 | f + vars | ts2 | f + vars | tsNULL | f + vars | tstz1 | f + vars | tstz2 | f + vars | tstzNULL | f + vars2 | j1 | f + vars2 | j2 | f + vars3 | r1 | f (22 rows) +SELECT package FROM pgv_stats() order by package; + package +--------- + vars + vars2 + vars3 +(3 rows) + SELECT pgv_remove('vars', 'int3'); ERROR: unrecognized variable "int3" SELECT pgv_remove('vars', 'int1'); @@ -702,27 +710,27 @@ SELECT pgv_exists('vars2'); (1 row) SELECT * FROM pgv_list() order by package, name; - package | name ----------+---------- - vars | d1 - vars | d2 - vars | dNULL - vars | int2 - vars | intNULL - vars | jNULL - vars | num1 - vars | num2 - vars | numNULL - vars | str1 - vars | str2 - vars | strNULL - vars | ts1 - vars | ts2 - vars | tsNULL - vars | tstz1 - vars | tstz2 - vars | tstzNULL - vars3 | r1 + package | name | is_transactional +---------+----------+------------------ + vars | d1 | f + vars | d2 | f + vars | dNULL | f + vars | int2 | f + vars | intNULL | f + vars | jNULL | f + vars | num1 | f + vars | num2 | f + vars | numNULL | f + vars | str1 | f + vars | str2 | f + vars | strNULL | f + vars | ts1 | f + vars | ts2 | f + vars | tsNULL | f + vars | tstz1 | f + vars | tstz2 | f + vars | tstzNULL | f + vars3 | r1 | f (19 rows) SELECT pgv_free(); @@ -738,7 +746,7 @@ SELECT pgv_exists('vars'); (1 row) SELECT * FROM pgv_list() order by package, name; - package | name ----------+------ + package | name | is_transactional +---------+------+------------------ (0 rows) diff --git a/expected/pg_variables_any.out b/expected/pg_variables_any.out index b7b6cb9..12987b0 100644 --- a/expected/pg_variables_any.out +++ b/expected/pg_variables_any.out @@ -532,29 +532,29 @@ SELECT pgv_get('vars', 'jNULL', NULL::jsonb); -- Manipulate variables SELECT * FROM pgv_list() order by package, name; - package | name ----------+---------- - vars | d1 - vars | d2 - vars | dNULL - vars | int1 - vars | int2 - vars | intNULL - vars | jNULL - vars | num1 - vars | num2 - vars | numNULL - vars | str1 - vars | str2 - vars | strNULL - vars | ts1 - vars | ts2 - vars | tsNULL - vars | tstz1 - vars | tstz2 - vars | tstzNULL - vars2 | j1 - vars2 | j2 + package | name | is_transactional +---------+----------+------------------ + vars | d1 | f + vars | d2 | f + vars | dNULL | f + vars | int1 | f + vars | int2 | f + vars | intNULL | f + vars | jNULL | f + vars | num1 | f + vars | num2 | f + vars | numNULL | f + vars | str1 | f + vars | str2 | f + vars | strNULL | f + vars | ts1 | f + vars | ts2 | f + vars | tsNULL | f + vars | tstz1 | f + vars | tstz2 | f + vars | tstzNULL | f + vars2 | j1 | f + vars2 | j2 | f (21 rows) SELECT pgv_remove('vars', 'int3'); @@ -588,26 +588,26 @@ SELECT pgv_exists('vars2'); (1 row) SELECT * FROM pgv_list() order by package, name; - package | name ----------+---------- - vars | d1 - vars | d2 - vars | dNULL - vars | int2 - vars | intNULL - vars | jNULL - vars | num1 - vars | num2 - vars | numNULL - vars | str1 - vars | str2 - vars | strNULL - vars | ts1 - vars | ts2 - vars | tsNULL - vars | tstz1 - vars | tstz2 - vars | tstzNULL + package | name | is_transactional +---------+----------+------------------ + vars | d1 | f + vars | d2 | f + vars | dNULL | f + vars | int2 | f + vars | intNULL | f + vars | jNULL | f + vars | num1 | f + vars | num2 | f + vars | numNULL | f + vars | str1 | f + vars | str2 | f + vars | strNULL | f + vars | ts1 | f + vars | ts2 | f + vars | tsNULL | f + vars | tstz1 | f + vars | tstz2 | f + vars | tstzNULL | f (18 rows) SELECT pgv_free(); @@ -623,7 +623,7 @@ SELECT pgv_exists('vars'); (1 row) SELECT * FROM pgv_list() order by package, name; - package | name ----------+------ + package | name | is_transactional +---------+------+------------------ (0 rows) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out new file mode 100644 index 0000000..bf96f4a --- /dev/null +++ b/expected/pg_variables_trans.out @@ -0,0 +1,3002 @@ +SET timezone = 'Europe/Moscow'; -- Need to proper output of datetime variables +--CHECK SAVEPOINT RELEASE +BEGIN; +-- Declare variables +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'some value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 102); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', NULL, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's102'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +SAVEPOINT comm; +-- Set new values +SELECT pgv_set('vars', 'any1', 'another value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'another value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 103, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 103); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', 104, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's103', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's103'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.03, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.03); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-04-02'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); + pgv_set_jsonb +--------------- + +(1 row) + +-- Check values before releasing savepoint +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +-- Check values after releasing savepoint +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +COMMIT; +BEGIN; +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; + pgv_insert +------------ + + + + +(4 rows) + +SELECT pgv_insert('vars3', 'r2', tab) FROM tab; + pgv_insert +------------ + + + + +(4 rows) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +--CHECK SAVEPOINT ROLLBACK +BEGIN; +-- Variables are already declared +SAVEPOINT comm2; +-- Set new values +SELECT pgv_set('vars', 'any1', 'one more value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'one more value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 102); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', NULL, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's102'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +-- Check values before rollback to savepoint +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 101 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s101 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.01 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 10:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 11:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 14:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 03-29-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 03-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +-------------------------------------------------- + {"bar": "baz", "active": false, "balance": 7.77} +(1 row) + +-- Check values after rollback to savepoint +ROLLBACK TO comm2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 11:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 03-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +-------------------------------------------------- + {"bar": "baz", "active": false, "balance": 7.77} +(1 row) + +COMMIT; +-- Record variables +BEGIN; +SAVEPOINT comm2; +SELECT pgv_delete('vars3', 'r1', 5); + pgv_delete +------------ + t +(1 row) + +SELECT pgv_delete('vars3', 'r2', 5); + pgv_delete +------------ + t +(1 row) + +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +COMMIT; +-- TRYING TO CHANGE FLAG 'IS_TRANSACTIONAL' +SELECT pgv_set('vars', 'any1', 'value'::text); +ERROR: variable "any1" already created as TRANSACTIONAL +SELECT pgv_set('vars', 'any2', 'value'::text, true); +ERROR: variable "any2" already created as NOT TRANSACTIONAL +SELECT pgv_set_int('vars', 'int1', 301); +ERROR: variable "int1" already created as TRANSACTIONAL +SELECT pgv_set_int('vars', 'int2', 302, true); +ERROR: variable "int2" already created as NOT TRANSACTIONAL +SELECT pgv_set_text('vars', 'str1', 's301'); +ERROR: variable "str1" already created as TRANSACTIONAL +SELECT pgv_set_text('vars', 'str2', 's302', true); +ERROR: variable "str2" already created as NOT TRANSACTIONAL +SELECT pgv_set_numeric('vars', 'num1', 3.01); +ERROR: variable "num1" already created as TRANSACTIONAL +SELECT pgv_set_numeric('vars', 'num2', 3.02, true); +ERROR: variable "num2" already created as NOT TRANSACTIONAL +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 20:00:00'); +ERROR: variable "ts1" already created as TRANSACTIONAL +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 21:00:00', true); +ERROR: variable "ts2" already created as NOT TRANSACTIONAL +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 20:00:00 GMT+01'); +ERROR: variable "tstz1" already created as TRANSACTIONAL +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 21:00:00 GMT+02', true); +ERROR: variable "tstz2" already created as NOT TRANSACTIONAL +SELECT pgv_set_date('vars', 'd1', '2016-04-29'); +ERROR: variable "d1" already created as TRANSACTIONAL +SELECT pgv_set_date('vars', 'd2', '2016-04-30', true); +ERROR: variable "d2" already created as NOT TRANSACTIONAL +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo2", null]'); +ERROR: variable "j1" already created as TRANSACTIONAL +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz2", "balance": 7.77, "active": true}', true); +ERROR: variable "j2" already created as NOT TRANSACTIONAL +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str66' :: varchar)); +ERROR: variable "r1" already created as TRANSACTIONAL +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str66' :: varchar),true); +ERROR: variable "r2" already created as NOT TRANSACTIONAL +-- CHECK pgv_list() WHILE WE HAVE A LOT OF MISCELLANEOUS VARIABLES +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+---------+------------------ + vars | any1 | t + vars | any2 | f + vars | d1 | t + vars | d2 | f + vars | int1 | t + vars | int2 | f + vars | intNULL | t + vars | num1 | t + vars | num2 | f + vars | str1 | t + vars | str2 | f + vars | ts1 | t + vars | ts2 | f + vars | tstz1 | t + vars | tstz2 | f + vars2 | j1 | t + vars2 | j2 | f + vars3 | r1 | t + vars3 | r2 | f +(19 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- VARIABLES DECLARED IN SUBTRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK TO SAVEPOINT +BEGIN; +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'text value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 401, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 402); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's401', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's402'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 4.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 4.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-04-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); + pgv_insert +------------ + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +------------ + text value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); +ERROR: unrecognized variable "int1" +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 402 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); +ERROR: unrecognized variable "str1" +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s402 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); +ERROR: unrecognized variable "num1" +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 4.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); +ERROR: unrecognized variable "ts1" +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Sat Apr 30 21:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); +ERROR: unrecognized variable "tstz1" +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Sun May 01 02:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); +ERROR: unrecognized variable "d1" +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); +ERROR: unrecognized variable "j1" +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +--------------------------------------------------- + {"bar": "baz4", "active": false, "balance": 4.44} +(1 row) + +SELECT pgv_select('vars3', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 100, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's100', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 102, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's102', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.02, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 01-21-2016 +(1 row) + +SELECT pgv_get_jsonb('vars', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 100 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s100 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.00 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 10:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 14:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 01-01-2016 +(1 row) + +SELECT pgv_get_jsonb('vars', 'j1'); + pgv_get_jsonb +--------------------- + [1, 0, "foo", null] +(1 row) + +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars2', 'int1', 102, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars2', 'str1', 's102', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +SELECT pgv_get_int('vars2', 'int1'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_text('vars2', 'str1'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars2', 'num1'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars2', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars2', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars2', 'd1'); + pgv_get_date +-------------- + 01-21-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_get_int('vars2', 'int1'); +ERROR: unrecognized variable "int1" +SELECT pgv_get_text('vars2', 'str1'); +ERROR: unrecognized variable "str1" +SELECT pgv_get_numeric('vars2', 'num1'); +ERROR: unrecognized variable "num1" +SELECT pgv_get_timestamp('vars2', 'ts1'); +ERROR: unrecognized variable "ts1" +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +ERROR: unrecognized variable "tstz1" +SELECT pgv_get_date('vars2', 'd1'); +ERROR: unrecognized variable "d1" +SELECT pgv_get_jsonb('vars2', 'j1'); +ERROR: unrecognized variable "j1" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--CHECK TRANSACTION COMMIT +-- Declare variables +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'some value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 102); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', NULL, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's102'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'another value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'another value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 103, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 103); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', 104, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's103', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's103'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.03, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.03); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-04-02'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); + pgv_set_jsonb +--------------- + +(1 row) + +-- Check values before committing transaction +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +-- Check values after committing transaction +COMMIT; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; + pgv_insert +------------ + + + + +(4 rows) + +SELECT pgv_insert('vars3', 'r2', tab) FROM tab; + pgv_insert +------------ + + + + +(4 rows) + +BEGIN; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) + +-- CHECK TRANSACTION ROLLBACK +-- Variables are already declared +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'one more value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'one more value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 102); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'intNULL', NULL, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's102'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 1.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +-- Check values before rollback +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 101 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s101 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.01 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 10:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 11:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 14:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 03-29-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 03-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +-------------------------------------------------- + {"bar": "baz", "active": false, "balance": 7.77} +(1 row) + +-- Check values after rollback +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value +(1 row) + +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 103 +(1 row) + +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_int('vars', 'intNULL'); + pgv_get_int +------------- + 104 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s103 +(1 row) + +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.03 +(1 row) + +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Wed Mar 30 11:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 18:00:00 2016 MSK +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Wed Mar 30 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 04-02-2016 +(1 row) + +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 03-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +----------------------------------------------------- + {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +-------------------------------------------------- + {"bar": "baz", "active": false, "balance": 7.77} +(1 row) + +-- Record variables +BEGIN; +SELECT pgv_delete('vars3', 'r1', 5); + pgv_delete +------------ + t +(1 row) + +SELECT pgv_delete('vars3', 'r2', 5); + pgv_delete +------------ + t +(1 row) + +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- VARIABLES DECLARED IN TRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK +BEGIN; +SELECT pgv_set('vars', 'any1', 'text value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 401, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_int('vars', 'int2', 402); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's401', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str2', 's402'); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 4.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num2', 4.02); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd2', '2016-04-30'); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); + pgv_set_jsonb +--------------- + +(1 row) + +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); + pgv_insert +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +------------ + text value +(1 row) + +SELECT pgv_get_int('vars', 'int1'); +ERROR: unrecognized variable "int1" +SELECT pgv_get_int('vars', 'int2'); + pgv_get_int +------------- + 402 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); +ERROR: unrecognized variable "str1" +SELECT pgv_get_text('vars', 'str2'); + pgv_get_text +-------------- + s402 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); +ERROR: unrecognized variable "num1" +SELECT pgv_get_numeric('vars', 'num2'); + pgv_get_numeric +----------------- + 4.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); +ERROR: unrecognized variable "ts1" +SELECT pgv_get_timestamp('vars', 'ts2'); + pgv_get_timestamp +-------------------------- + Sat Apr 30 21:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); +ERROR: unrecognized variable "tstz1" +SELECT pgv_get_timestamptz('vars', 'tstz2'); + pgv_get_timestamptz +------------------------------ + Sun May 01 02:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); +ERROR: unrecognized variable "d1" +SELECT pgv_get_date('vars', 'd2'); + pgv_get_date +-------------- + 04-30-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); +ERROR: unrecognized variable "j1" +SELECT pgv_get_jsonb('vars2', 'j2'); + pgv_get_jsonb +--------------------------------------------------- + {"bar": "baz4", "active": false, "balance": 4.44} +(1 row) + +SELECT pgv_select('vars3', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 100, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's100', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 100, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's100', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 101, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's101', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars', 'int1', 102, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars', 'str1', 's102', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars', 'num1', 1.02, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 01-21-2016 +(1 row) + +SELECT pgv_get_jsonb('vars', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 100 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s100 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.00 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 10:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 14:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 01-01-2016 +(1 row) + +SELECT pgv_get_jsonb('vars', 'j1'); + pgv_get_jsonb +--------------------- + [1, 0, "foo", null] +(1 row) + +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +-------------------------- + before transaction block +(1 row) + +SELECT pgv_get_int('vars', 'int1'); + pgv_get_int +------------- + 100 +(1 row) + +SELECT pgv_get_text('vars', 'str1'); + pgv_get_text +-------------- + s100 +(1 row) + +SELECT pgv_get_numeric('vars', 'num1'); + pgv_get_numeric +----------------- + 1.00 +(1 row) + +SELECT pgv_get_timestamp('vars', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 10:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 14:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars', 'd1'); + pgv_get_date +-------------- + 01-01-2016 +(1 row) + +SELECT pgv_get_jsonb('vars', 'j1'); + pgv_get_jsonb +--------------------- + [1, 0, "foo", null] +(1 row) + +BEGIN; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_set_int('vars2', 'int1', 102, true); + pgv_set_int +------------- + +(1 row) + +SELECT pgv_set_text('vars2', 'str1', 's102', true); + pgv_set_text +-------------- + +(1 row) + +SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); + pgv_set_numeric +----------------- + +(1 row) + +SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); + pgv_set_timestamp +------------------- + +(1 row) + +SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); + pgv_set_timestamptz +--------------------- + +(1 row) + +SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); + pgv_set_date +-------------- + +(1 row) + +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + pgv_set_jsonb +--------------- + +(1 row) + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +SELECT pgv_get_int('vars2', 'int1'); + pgv_get_int +------------- + 102 +(1 row) + +SELECT pgv_get_text('vars2', 'str1'); + pgv_get_text +-------------- + s102 +(1 row) + +SELECT pgv_get_numeric('vars2', 'num1'); + pgv_get_numeric +----------------- + 1.02 +(1 row) + +SELECT pgv_get_timestamp('vars2', 'ts1'); + pgv_get_timestamp +-------------------------- + Fri Jan 01 12:00:00 2016 +(1 row) + +SELECT pgv_get_timestamptz('vars2', 'tstz1'); + pgv_get_timestamptz +------------------------------ + Fri Jan 01 16:00:00 2016 MSK +(1 row) + +SELECT pgv_get_date('vars2', 'd1'); + pgv_get_date +-------------- + 01-21-2016 +(1 row) + +SELECT pgv_get_jsonb('vars2', 'j1'); + pgv_get_jsonb +--------------------- + [1, 2, "foo", null] +(1 row) + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_get_int('vars2', 'int1'); +ERROR: unrecognized variable "int1" +SELECT pgv_get_text('vars2', 'str1'); +ERROR: unrecognized variable "str1" +SELECT pgv_get_numeric('vars2', 'num1'); +ERROR: unrecognized variable "num1" +SELECT pgv_get_timestamp('vars2', 'ts1'); +ERROR: unrecognized variable "ts1" +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +ERROR: unrecognized variable "tstz1" +SELECT pgv_get_date('vars2', 'd1'); +ERROR: unrecognized variable "d1" +SELECT pgv_get_jsonb('vars2', 'j1'); +ERROR: unrecognized variable "j1" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables--1.0--1.1.sql b/pg_variables--1.0--1.1.sql new file mode 100644 index 0000000..1627b24 --- /dev/null +++ b/pg_variables--1.0--1.1.sql @@ -0,0 +1,68 @@ +/* contrib/pg_variables/pg_variables--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_variables UPDATE TO '1.1'" to load this file. \quit + +-- Delete previous vresion of functions. +DROP FUNCTION pgv_set(package text, name text, value anynonarray); +DROP FUNCTION pgv_set_int(package text, name text, value int); +DROP FUNCTION pgv_set_text(package text, name text, value text); +DROP FUNCTION pgv_set_numeric(package text, name text, value numeric); +DROP FUNCTION pgv_set_timestamp(package text, name text, value timestamp); +DROP FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz); +DROP FUNCTION pgv_set_date(package text, name text, value date); +DROP FUNCTION pgv_set_jsonb(package text, name text, value jsonb); +DROP FUNCTION pgv_insert(package text, name text, r record); +DROP FUNCTION pgv_list(); + +-- Create new versions of setters +CREATE FUNCTION pgv_set(package text, name text, value anynonarray, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_any' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_int(package text, name text, value int, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_int' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_text(package text, name text, value text, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_text' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_numeric(package text, name text, value numeric, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_numeric' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_timestamp(package text, name text, value timestamp, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_timestamp' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_timestamptz' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_date(package text, name text, value date, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_date' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_jsonb(package text, name text, value jsonb, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_jsonb' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_insert(package text, name text, r record, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_insert' +LANGUAGE C VOLATILE; + +-- pgv_list() changed output +CREATE FUNCTION pgv_list() +RETURNS TABLE(package text, name text, is_transactional bool) +AS 'MODULE_PATHNAME', 'get_packages_and_variables' +LANGUAGE C VOLATILE; diff --git a/pg_variables--1.1.sql b/pg_variables--1.1.sql new file mode 100644 index 0000000..ead5bf2 --- /dev/null +++ b/pg_variables--1.1.sql @@ -0,0 +1,156 @@ +/* contrib/pg_variables/pg_variables--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION pg_variables" to load this file. \quit + +-- Scalar variables functions + +CREATE FUNCTION pgv_set(package text, name text, value anynonarray, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_any' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get(package text, name text, var_type anynonarray, strict bool default true) +RETURNS anynonarray +AS 'MODULE_PATHNAME', 'variable_get_any' +LANGUAGE C VOLATILE; + +-- Deprecated scalar variables functions + +CREATE FUNCTION pgv_set_int(package text, name text, value int, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_int' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_int(package text, name text, strict bool default true) +RETURNS int +AS 'MODULE_PATHNAME', 'variable_get_int' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_text(package text, name text, value text, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_text' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_text(package text, name text, strict bool default true) +RETURNS text +AS 'MODULE_PATHNAME', 'variable_get_text' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_numeric(package text, name text, value numeric, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_numeric' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_numeric(package text, name text, strict bool default true) +RETURNS numeric +AS 'MODULE_PATHNAME', 'variable_get_numeric' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_timestamp(package text, name text, value timestamp, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_timestamp' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_timestamp(package text, name text, strict bool default true) +RETURNS timestamp +AS 'MODULE_PATHNAME', 'variable_get_timestamp' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_timestamptz' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_timestamptz(package text, name text, strict bool default true) +RETURNS timestamptz +AS 'MODULE_PATHNAME', 'variable_get_timestamptz' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_date(package text, name text, value date, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_date' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_date(package text, name text, strict bool default true) +RETURNS date +AS 'MODULE_PATHNAME', 'variable_get_date' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_set_jsonb(package text, name text, value jsonb, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_jsonb' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get_jsonb(package text, name text, strict bool default true) +RETURNS jsonb +AS 'MODULE_PATHNAME', 'variable_get_jsonb' +LANGUAGE C VOLATILE; + +-- Functions to work with records +CREATE FUNCTION pgv_insert(package text, name text, r record, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_insert' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_update(package text, name text, r record) +RETURNS boolean +AS 'MODULE_PATHNAME', 'variable_update' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_delete(package text, name text, value anynonarray) +RETURNS boolean +AS 'MODULE_PATHNAME', 'variable_delete' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_select(package text, name text) +RETURNS setof record +AS 'MODULE_PATHNAME', 'variable_select' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_select(package text, name text, value anynonarray) +RETURNS record +AS 'MODULE_PATHNAME', 'variable_select_by_value' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_select(package text, name text, value anyarray) +RETURNS setof record +AS 'MODULE_PATHNAME', 'variable_select_by_values' +LANGUAGE C VOLATILE; + +-- Functions to work with packages + +CREATE FUNCTION pgv_exists(package text, name text) +RETURNS bool +AS 'MODULE_PATHNAME', 'variable_exists' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_exists(package text) +RETURNS bool +AS 'MODULE_PATHNAME', 'package_exists' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_remove(package text, name text) +RETURNS void +AS 'MODULE_PATHNAME', 'remove_variable' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_remove(package text) +RETURNS void +AS 'MODULE_PATHNAME', 'remove_package' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_free() +RETURNS void +AS 'MODULE_PATHNAME', 'remove_packages' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_list() +RETURNS TABLE(package text, name text, is_transactional bool) +AS 'MODULE_PATHNAME', 'get_packages_and_variables' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_stats() +RETURNS TABLE(package text, allocated_memory bigint) +AS 'MODULE_PATHNAME', 'get_packages_stats' +LANGUAGE C VOLATILE; diff --git a/pg_variables.c b/pg_variables.c old mode 100644 new mode 100755 index 975f826..262ab68 --- a/pg_variables.c +++ b/pg_variables.c @@ -12,6 +12,7 @@ #include "funcapi.h" #include "access/htup_details.h" +#include "access/xact.h" #include "catalog/pg_type.h" #include "parser/scansup.h" #include "utils/builtins.h" @@ -63,6 +64,8 @@ PG_FUNCTION_INFO_V1(remove_packages); PG_FUNCTION_INFO_V1(get_packages_and_variables); PG_FUNCTION_INFO_V1(get_packages_stats); +extern void _PG_init(void); +extern void _PG_fini(void); static void getKeyFromName(text *name, char *key); static void ensurePackagesHashExists(); static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); @@ -72,6 +75,11 @@ static HashVariableEntry *getVariableByNameWithType(HTAB *variables, Oid typid, bool create, bool strict); +static HashVariableEntry * +getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, + Oid typid, bool create, bool strict, bool is_transactional); +static void +create_savepoint(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -92,24 +100,90 @@ static MemoryContext ModuleContext = NULL; static HashPackageEntry *LastPackage = NULL; /* Recent variable */ static HashVariableEntry *LastVariable = NULL; +/* + * List of variables, changed in top level transaction. Used to limit + * number of proceeded variables on start of transaction. + * NOTE that subtransactions affect ALL transactional variables, even if + * they haven't changed during transaction. + */ +static dlist_head *changedVars = NULL; + +static bool +isVarChangedInTrans(HashVariableEntry *variable) +{ + dlist_iter iter; + if (!changedVars) + return false; + dlist_foreach(iter, changedVars) + { + ChangedVarsNode *cvn; + cvn = dlist_container(ChangedVarsNode, node, iter.cur); + if (cvn->variable == variable) + return true; + } + return false; +} + +static void +freeChangedVars() +{ + MemoryContext oldcxt; + Assert(ModuleContext && changedVars); + oldcxt = MemoryContextSwitchTo(ModuleContext); + while(!dlist_is_empty(changedVars)){ + ChangedVarsNode *cvnToDelete; + cvnToDelete = dlist_container( ChangedVarsNode, + node, + dlist_pop_head_node(changedVars)); + pfree(cvnToDelete); + } + pfree(changedVars); + changedVars = NULL; + MemoryContextSwitchTo(oldcxt); +} + +/* + * The function deletes the variable only from the list, + * the variable itself continues to exist. + */ +static void +deleteFromChangedVars(HashVariableEntry *variable) +{ + MemoryContext oldcxt; + dlist_mutable_iter miter; + Assert(ModuleContext && changedVars); + oldcxt = MemoryContextSwitchTo(ModuleContext); + dlist_foreach_modify(miter, changedVars) + { + ChangedVarsNode *cvn; + cvn = dlist_container(ChangedVarsNode, node, miter.cur); + if (cvn->variable == variable) + { + dlist_delete(miter.cur); + return; + } + } + MemoryContextSwitchTo(oldcxt); +} /* * Set value of variable, typlen could be 0 if typbyval == true */ static void variable_set(text *package_name, text *var_name, - Oid typid, Datum value, bool is_null) + Oid typid, Datum value, bool is_null, bool is_transactional) { HashPackageEntry *package; HashVariableEntry *variable; ScalarVar *scalar; + MemoryContext oldcxt; package = getPackageByName(package_name, true, false); - variable = getVariableByNameWithType(package->variablesHash, - var_name, typid, true, false); - - scalar = &variable->value.scalar; + oldcxt = MemoryContextSwitchTo(package->hctx); + variable = getVariableByNameWithTypeAndTrans(package, var_name, typid, true, + false, is_transactional); + scalar = &(get_actual_value_scalar(variable)); /* Release memory for variable */ if (scalar->typbyval == false && scalar->is_null == false) pfree(DatumGetPointer(scalar->value)); @@ -117,15 +191,11 @@ variable_set(text *package_name, text *var_name, scalar->is_null = is_null; if (!scalar->is_null) { - MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(package->hctx); - scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); - - MemoryContextSwitchTo(oldcxt); } else scalar->value = 0; + MemoryContextSwitchTo(oldcxt); } static Datum @@ -151,9 +221,7 @@ variable_get(text *package_name, text *var_name, *is_null = true; return 0; } - - scalar = &(variable->value.scalar); - + scalar = &get_actual_value_scalar(variable); *is_null = scalar->is_null; return scalar->value; } @@ -163,15 +231,17 @@ variable_set_any(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, get_fn_expr_argtype(fcinfo->flinfo, 2), PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -210,15 +280,17 @@ variable_set_int(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, INT4OID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -256,15 +328,17 @@ variable_set_text(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, TEXTOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -302,15 +376,17 @@ variable_set_numeric(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, NUMERICOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -348,15 +424,17 @@ variable_set_timestamp(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, TIMESTAMPOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -394,15 +472,17 @@ variable_set_timestamptz(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, TIMESTAMPTZOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -440,15 +520,17 @@ variable_set_date(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, DATEOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -486,15 +568,17 @@ variable_set_jsonb(PG_FUNCTION_ARGS) { text *package_name; text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); + is_transactional = PG_GETARG_BOOL(3); variable_set(package_name, var_name, JSONBOID, PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), - PG_ARGISNULL(2)); + PG_ARGISNULL(2), is_transactional); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -535,6 +619,7 @@ variable_insert(PG_FUNCTION_ARGS) HeapTupleHeader rec; HashPackageEntry *package; HashVariableEntry *variable; + bool is_transactional; Oid tupType; int32 tupTypmod; @@ -552,6 +637,7 @@ variable_insert(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); rec = PG_GETARG_HEAPTUPLEHEADER(2); + is_transactional = PG_GETARG_BOOL(3); /* Get cached package */ if (LastPackage == NULL || @@ -572,21 +658,35 @@ variable_insert(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableByNameWithType(package->variablesHash, - var_name, RECORDOID, true, false); + MemoryContext oldcxt; + oldcxt = MemoryContextSwitchTo(package->hctx); + variable = getVariableByNameWithTypeAndTrans(package, var_name, + RECORDOID, true, false, + is_transactional); LastVariable = variable; + MemoryContextSwitchTo(oldcxt); } - else + else if (LastVariable->is_transactional == is_transactional) variable = LastVariable; + else + { + char key[NAMEDATALEN]; + getKeyFromName(var_name, key); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg( + "variable \"%s\" already created as %sTRANSACTIONAL", + key, LastVariable->is_transactional?"":"NOT "))); + } /* Insert a record */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - if (!variable->value.record.tupdesc) + if (!get_actual_value_record(variable).tupdesc) { - /* + /*/* * This is the first record for the var_name. Initialize attributes. */ init_attributes(variable, tupdesc, package->hctx); @@ -768,7 +868,7 @@ variable_select(PG_FUNCTION_ARGS) variable = getVariableByNameWithType(package->variablesHash, var_name, RECORDOID, false, true); - record = &(variable->value.record); + record = &get_actual_value_record(variable); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -844,7 +944,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) if (!value_is_null) check_record_key(variable, value_type); - record = &(variable->value.record); + record = &get_actual_value_record(variable); /* Search a record */ k.value = value; @@ -916,7 +1016,8 @@ variable_select_by_values(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - funcctx->tuple_desc = CreateTupleDescCopy(variable->value.record.tupdesc); + funcctx->tuple_desc = CreateTupleDescCopy( + get_actual_value_record(variable).tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -938,7 +1039,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) bool found; RecordVar *record; - record = &(var->variable->value.record); + record = &get_actual_value_record(var->variable); /* Search a record */ k.value = value; k.is_null = isnull; @@ -962,14 +1063,34 @@ variable_select_by_values(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } +/* + * Remove one entry from history of states of arg 'variable' + */ static void -clean_variable(HashVariableEntry *variable) +clean_variable_current_state(HashVariableEntry *variable) { + ValueHistory *history; + ValueHistoryEntry *historyEntryToDelete; if (variable->typid == RECORDOID) clean_records(variable); - else if (variable->value.scalar.typbyval == false && - variable->value.scalar.is_null == false) - pfree(DatumGetPointer(variable->value.scalar.value)); + else if (get_actual_value_scalar(variable).typbyval == false && + get_actual_value_scalar(variable).is_null == false) + pfree(DatumGetPointer(get_actual_value_scalar(variable).value)); + history = &variable->data; + historyEntryToDelete = get_history_entry(dlist_pop_head_node(history)); + pfree(historyEntryToDelete); +} + +/* + * Remove all entries from history of states of arg 'variable'. + * DOES NOT remove 'variable' itself. + */ +static void +clean_variable_all_states(HashVariableEntry *variable) +{ + while(!dlist_is_empty(&variable->data)){ + clean_variable_current_state(variable); + } } /* @@ -1061,7 +1182,7 @@ remove_variable(PG_FUNCTION_ARGS) /* Remove variable from cache */ LastVariable = NULL; - clean_variable(variable); + clean_variable_all_states(variable); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -1131,8 +1252,9 @@ remove_packages(PG_FUNCTION_ARGS) /* All packages and variables will be freed */ MemoryContextDelete(ModuleContext); - packagesHash = NULL; + packagesHash = NULL; ModuleContext = NULL; + changedVars = NULL; PG_RETURN_VOID(); } @@ -1144,6 +1266,7 @@ typedef struct { char *package; char *variable; + bool is_transactional; } VariableRec; /* @@ -1208,6 +1331,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) recs[nRecs].package = package->name; recs[nRecs].variable = variable->name; + recs[nRecs].is_transactional = variable->is_transactional; nRecs++; } } @@ -1228,8 +1352,8 @@ get_packages_and_variables(PG_FUNCTION_ARGS) if (funcctx->call_cntr < funcctx->max_calls) { - Datum values[2]; - bool nulls[2]; + Datum values[3]; + bool nulls[3]; HeapTuple tuple; Datum result; int i = funcctx->call_cntr; @@ -1240,6 +1364,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) values[0] = PointerGetDatum(cstring_to_text(recs[i].package)); values[1] = PointerGetDatum(cstring_to_text(recs[i].variable)); + values[2] = recs[i].is_transactional; tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); @@ -1263,7 +1388,11 @@ getMemoryTotalSpace(MemoryContext context, int level, Size *totalspace) /* Examine the context itself */ memset(&totals, 0, sizeof(totals)); +# if PG_VERSION_NUM >= 110000 + (*context->methods->stats) (context, NULL, NULL, &totals); +# else (*context->methods->stats) (context, level, false, &totals); +# endif *totalspace += totals.totalspace; /* @@ -1449,7 +1578,7 @@ getPackageByName(text* name, bool create, bool strict) #if PG_VERSION_NUM >= 110000 package->hctx = AllocSetContextCreateExtended(ModuleContext, - hash_name, 0, + hash_name, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); @@ -1481,6 +1610,11 @@ getPackageByName(text* name, bool create, bool strict) return package; } +/* + * Create a variable or return a pointer to existing one. + * Function is useful to request a value of existing variable and + * flag 'is_transactional' of this variable is unknown. + */ static HashVariableEntry * getVariableByNameWithType(HTAB *variables, text *name, Oid typid, bool create, bool strict) @@ -1517,14 +1651,19 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, /* Variable entry was created, so initialize new variable. */ if (variable) { - memset(&variable->value, 0, sizeof(variable->value)); + ValueHistoryEntry *historyEntry; + + memset(&variable->data, 0, sizeof(variable->data)); variable->typid = typid; + dlist_init(&(variable->data)); + historyEntry = palloc0(sizeof(ValueHistoryEntry)); + dlist_push_head(&variable->data, &historyEntry->node); if (typid != RECORDOID) { - get_typlenbyval(variable->typid, - &variable->value.scalar.typlen, - &variable->value.scalar.typbyval); - variable->value.scalar.is_null = true; + ScalarVar *scalar = &get_actual_value_scalar(variable); + get_typlenbyval(variable->typid, &scalar->typlen, + &scalar->typbyval); + scalar->is_null = true; } } else if (strict) @@ -1535,3 +1674,359 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, return variable; } + +/* + * Create a variable or return a pointer to existing one. + * Function is useful to set new value to variable and + * flag 'is_transactional' is known. + */ +static HashVariableEntry * +getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typid, + bool create, bool strict, bool is_transactional) +{ + HashVariableEntry *variable; + char key[NAMEDATALEN]; + bool found; + + getKeyFromName(name, key); + + if (create) + variable = (HashVariableEntry *) hash_search(package->variablesHash, + key, HASH_ENTER, &found); + else + variable = (HashVariableEntry *) hash_search(package->variablesHash, + key, HASH_FIND, &found); + + /* Check variable type */ + if (found) + { + if (variable->typid != typid) + { + char *var_type = DatumGetCString(DirectFunctionCall1( + regtypeout, ObjectIdGetDatum(variable->typid))); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" requires \"%s\" value", + key, var_type))); + } + if (variable->is_transactional!=is_transactional) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg( + "variable \"%s\" already created as %sTRANSACTIONAL", + key, variable->is_transactional?"":"NOT "))); + } + + /* + * Create savepoint only if we in top transaction. + * Subtransactions manage it another way + */ + if (variable->is_transactional && + !isVarChangedInTrans(variable) && + GetCurrentTransactionNestLevel() < 2) + { + create_savepoint(package, variable); + } + } + else + { + /* Variable entry was created, so initialize new variable. */ + if (variable) + { + ValueHistoryEntry *historyEntry; + memset(&variable->data, 0, sizeof(variable->data)); + variable->typid = typid; + variable->is_transactional = is_transactional; + dlist_init(&(variable->data)); + historyEntry = palloc0(sizeof(ValueHistoryEntry)); + dlist_push_head(&variable->data, &historyEntry->node); + if (typid != RECORDOID) + { + ScalarVar *scalar = &get_actual_value_scalar(variable); + get_typlenbyval(variable->typid, &scalar->typlen, + &scalar->typbyval); + scalar->is_null = true; + } + } + else if (strict) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized variable \"%s\"", key))); + } + /* If it is necessary, put variable to changedVars */ + if (is_transactional) + { + MemoryContext oldcxt; + oldcxt = MemoryContextSwitchTo(ModuleContext); + if (!changedVars) + { + changedVars = palloc0(sizeof(dlist_head)); + dlist_init(changedVars); + } + if (!isVarChangedInTrans(variable)) + { + ChangedVarsNode *cvn = palloc0(sizeof(ChangedVarsNode)); + cvn->package = package; + cvn->variable = variable; + dlist_push_head(changedVars, &cvn->node); + } + MemoryContextSwitchTo(oldcxt); + } + + return variable; +} + +/* + * Rollback variable to previous state and remove current value + */ +static void +rollback_savepoint(HashPackageEntry *package, HashVariableEntry *variable) +{ + clean_variable_current_state(variable); + /* Remove variable if it was created in rolled back transaction */ + if (dlist_is_empty(&variable->data)) + { + bool found; + hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); + deleteFromChangedVars(variable); + } +} + +/* + * Create a new history point of variable and copy value from + * previous state + */ +static void +create_savepoint(HashPackageEntry *package, HashVariableEntry *variable) +{ + + if(variable->typid == RECORDOID) + { + insert_savepoint(variable, package->hctx); + } + else + { + ScalarVar *scalar; + ValueHistory *history; + ValueHistoryEntry *history_entry_new, + *history_entry_prev; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(package->hctx); + history = &variable->data; + /* Release memory for variable */ + history_entry_new = palloc0(sizeof(ValueHistoryEntry)); + history_entry_prev = dlist_head_element(ValueHistoryEntry, node, history); + scalar = &history_entry_new->value.scalar; + *scalar = history_entry_prev->value.scalar; + + if (!scalar->is_null) + { + scalar->value = datumCopy( + history_entry_prev->value.scalar.value, + scalar->typbyval, + scalar->typlen); + } + else + scalar->value = 0; + dlist_push_head(history, &history_entry_new->node); + MemoryContextSwitchTo(oldcxt); + } +} + +/* + * Remove previous state of variable + */ +static void +release_savepoint(HashVariableEntry *variable) +{ + ValueHistory *history; + + history = &variable->data; + if (dlist_has_next(history, dlist_head_node(history))) + { + ValueHistoryEntry *historyEntryToDelete; + dlist_node *nodeToDelete; + + nodeToDelete = dlist_next_node(history, dlist_head_node(history)); + historyEntryToDelete = get_history_entry(nodeToDelete); + + if (variable->typid == RECORDOID) + { + hash_destroy(historyEntryToDelete->value.record.rhash); + FreeTupleDesc(historyEntryToDelete->value.record.tupdesc); + /* All records will be freed */ + MemoryContextDelete(historyEntryToDelete->value.record.hctx); + } + else if (historyEntryToDelete->value.scalar.typbyval == false && + historyEntryToDelete->value.scalar.is_null == false) + pfree(DatumGetPointer(historyEntryToDelete->value.scalar.value)); + + dlist_delete(nodeToDelete); + pfree(historyEntryToDelete); + } +} + +/* + * Possible actions on variables + */ +enum Action +{ + RELEASE_SAVEPOINT, + ROLLBACK_TO_SAVEPOINT, + CREATE_SAVEPOINT_SUB, + RELEASE_SAVEPOINT_SUB, + ROLLBACK_TO_SAVEPOINT_SUB +}; + +/* + * Iterate all variables from all packages and + * apply corresponding action on them + */ +static void +apply_action_on_variables(enum Action action) +{ + HashPackageEntry *package; + HashVariableEntry *variable; + HASH_SEQ_STATUS vstat, + pstat; + + if (packagesHash) + { + /* Get packages list */ + hash_seq_init(&pstat, packagesHash); + while ((package = + (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) + { + /* Get variables list for package */ + hash_seq_init(&vstat, package->variablesHash); + while ((variable = + (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) + { + if(variable->is_transactional) + { + switch(action) + { + case CREATE_SAVEPOINT_SUB: + create_savepoint(package, variable); + break; + case RELEASE_SAVEPOINT_SUB: + release_savepoint(variable); + break; + case ROLLBACK_TO_SAVEPOINT_SUB: + rollback_savepoint(package, variable); + break; + default: + /* transactions proceeded in apply_action_on_changedVars() */ + break; + } + } + } + } + } +} + +/* + * Iterate variables from 'changedVars' list and + * apply corresponding action on them + */ +static void +apply_action_on_changedVars(enum Action action) +{ + dlist_mutable_iter miter; + Assert(changedVars); + dlist_foreach_modify(miter, changedVars) + { + ChangedVarsNode *cvn = dlist_container(ChangedVarsNode, node, miter.cur); + switch(action) + { + case RELEASE_SAVEPOINT: + release_savepoint(cvn->variable); + break; + case ROLLBACK_TO_SAVEPOINT: + rollback_savepoint(cvn->package, cvn->variable); + break; + default: + /* subtransactions proceeded in apply_action_on_variables() */ + break; + } + } +} + +/* + * Intercept execution during subtransaction processing + * Since sub-transactions are created less often, but can have several levels + * of nesting, it's easier to create and roll back savepoints during events. + * Unfortunately, you have to savepoint/rollback all existing transact variables. + */ +static void +pgv_sub_trans_callback(SubXactEvent event, SubTransactionId mySubid, + SubTransactionId parentSubid, void *arg) +{ + switch (event){ + case SUBXACT_EVENT_START_SUB: + apply_action_on_variables(CREATE_SAVEPOINT_SUB); + break; + case SUBXACT_EVENT_COMMIT_SUB: + apply_action_on_variables(RELEASE_SAVEPOINT_SUB); + break; + case SUBXACT_EVENT_ABORT_SUB: + apply_action_on_variables(ROLLBACK_TO_SAVEPOINT_SUB); + break; + case SUBXACT_EVENT_PRE_COMMIT_SUB: + break; + } +} + +/* + * Intercept execution during transaction processing + * Since this event occurs frequently, it is necessary to limit + * the number of processed variables. + */ +static void +pgv_trans_callback(XactEvent event, void *arg) +{ + if (changedVars) + { + switch (event){ + case XACT_EVENT_PRE_COMMIT: + apply_action_on_changedVars(RELEASE_SAVEPOINT); + freeChangedVars(); + break; + case XACT_EVENT_ABORT: + apply_action_on_changedVars(ROLLBACK_TO_SAVEPOINT); + freeChangedVars(); + break; + case XACT_EVENT_PARALLEL_PRE_COMMIT: + apply_action_on_changedVars(RELEASE_SAVEPOINT); + freeChangedVars(); + break; + case XACT_EVENT_PARALLEL_ABORT: + apply_action_on_changedVars(ROLLBACK_TO_SAVEPOINT); + freeChangedVars(); + break; + default: + break; + } + } +} + +/* + * Register callback function when module starts + */ +void _PG_init(void) +{ + RegisterXactCallback(pgv_trans_callback, NULL); + RegisterSubXactCallback(pgv_sub_trans_callback, NULL); +} + +/* + * Unregister callback function when module unloads + */ +void _PG_fini(void) +{ + UnregisterXactCallback(pgv_trans_callback, NULL); + UnregisterSubXactCallback(pgv_sub_trans_callback, NULL); +} diff --git a/pg_variables.control b/pg_variables.control index f44cf24..2776600 100644 --- a/pg_variables.control +++ b/pg_variables.control @@ -1,5 +1,5 @@ # pg_variables extension comment = 'session variables with various types' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/pg_variables' relocatable = true diff --git a/pg_variables.h b/pg_variables.h old mode 100644 new mode 100755 index abe69f8..adfaccd --- a/pg_variables.h +++ b/pg_variables.h @@ -19,6 +19,7 @@ #include "utils/hsearch.h" #include "utils/numeric.h" #include "utils/jsonb.h" +#include "lib/ilist.h" /* Accessor for the i'th attribute of tupdesc. */ #if PG_VERSION_NUM > 100000 @@ -59,16 +60,30 @@ typedef struct ScalarVar int16 typlen; } ScalarVar; -typedef struct HashVariableEntry -{ - char name[NAMEDATALEN]; +/* List node that stores one of the variables states */ +typedef struct ValueHistoryEntry{ + dlist_node node; union { ScalarVar scalar; RecordVar record; } value; +} ValueHistoryEntry; +typedef dlist_head ValueHistory; + +/* Variable by itself */ +typedef struct HashVariableEntry +{ + char name[NAMEDATALEN]; + /* Entry point to list with states of value */ + ValueHistory data; Oid typid; + /* + * The flag determines the further behavior of the variable. + * Can be specified only when creating a variable. + */ + bool is_transactional; } HashVariableEntry; typedef struct HashRecordKey @@ -87,6 +102,14 @@ typedef struct HashRecordEntry HeapTuple tuple; } HashRecordEntry; +/* Element of list with variables, changed within transaction */ +typedef struct ChangedVarsNode +{ + dlist_node node; + HashPackageEntry *package; + HashVariableEntry *variable; +} ChangedVarsNode; + extern void init_attributes(HashVariableEntry* variable, TupleDesc tupdesc, MemoryContext topctx); extern void check_attributes(HashVariableEntry *variable, TupleDesc tupdesc); @@ -100,4 +123,15 @@ extern bool delete_record(HashVariableEntry* variable, Datum value, bool is_null); extern void clean_records(HashVariableEntry *variable); +extern void insert_savepoint(HashVariableEntry *variable, + MemoryContext packageContext); + +/* Internal macros to manage with dlist structure */ +#define get_actual_value_scalar(variable) \ + (dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.scalar +#define get_actual_value_record(variable) \ + (dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.record +#define get_history_entry(node_ptr) \ + dlist_container(ValueHistoryEntry, node, node_ptr) + #endif /* __PG_VARIABLES_H__ */ diff --git a/pg_variables_record.c b/pg_variables_record.c index 0c91b6d..10fc971 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -78,11 +78,11 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, sprintf(hash_name, "Records hash for variable \"%s\"", variable->name); - record = &(variable->value.record); + record = &(get_actual_value_record(variable)); #if PG_VERSION_NUM >= 110000 record->hctx = AllocSetContextCreateExtended(topctx, - hash_name, 0, + hash_name, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); @@ -139,11 +139,13 @@ void check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) { int i; + RecordVar *record; Assert(variable->typid == RECORDOID); + record = &get_actual_value_record(variable); /* First, check columns count. */ - if (variable->value.record.tupdesc->natts != tupdesc->natts) + if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record structure differs from variable \"%s\" " @@ -152,7 +154,7 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) { - Form_pg_attribute attr1 = GetTupleDescAttr(variable->value.record.tupdesc, i), + Form_pg_attribute attr1 = GetTupleDescAttr(record->tupdesc, i), attr2 = GetTupleDescAttr(tupdesc, i); if ((attr1->atttypid != attr2->atttypid) @@ -171,9 +173,11 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) void check_record_key(HashVariableEntry *variable, Oid typid) { + RecordVar *record; Assert(variable->typid == RECORDOID); + record = &get_actual_value_record(variable); - if (GetTupleDescAttr(variable->value.record.tupdesc, 0)->atttypid != typid) + if (GetTupleDescAttr(record->tupdesc, 0)->atttypid != typid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested value type differs from variable \"%s\" " @@ -199,7 +203,7 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = &(variable->value.record); + record = &(get_actual_value_record(variable)); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -259,7 +263,7 @@ update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = &(variable->value.record); + record = &(get_actual_value_record(variable)); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -309,7 +313,7 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) Assert(variable->typid == RECORDOID); - record = &(variable->value.record); + record = &(get_actual_value_record(variable)); /* Delete a record */ k.value = value; @@ -331,11 +335,53 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) void clean_records(HashVariableEntry *variable) { + RecordVar *record; Assert(variable->typid == RECORDOID); - hash_destroy(variable->value.record.rhash); - FreeTupleDesc(variable->value.record.tupdesc); + record = &get_actual_value_record(variable); + hash_destroy(record->rhash); + FreeTupleDesc(record->tupdesc); /* All records will be freed */ - MemoryContextDelete(variable->value.record.hctx); + MemoryContextDelete(record->hctx); +} + +/* + * Create a new history point of record variable and copy all tulpes from + * previous state + */ +void +insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) +{ + RecordVar *record_prev, + *record_new; + HashRecordEntry *item_prev, + *item_new; + ValueHistoryEntry *history_entry_new; + HASH_SEQ_STATUS *rstat; + bool found; + MemoryContext oldcxt; + + Assert(variable->typid == RECORDOID); + + /* Create new hstory entry */ + record_prev = &(get_actual_value_record(variable)); + oldcxt = MemoryContextSwitchTo(packageContext); + history_entry_new = palloc0(sizeof(ValueHistoryEntry)); + record_new = &(history_entry_new->value.record); + dlist_push_head(&variable->data, &history_entry_new->node); + init_attributes(variable, record_prev->tupdesc, packageContext); + + /* Copy previous history entry into the new one*/ + rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); + hash_seq_init(rstat, record_prev->rhash); + while((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) !=NULL) + { + HashRecordKey k; + k = item_prev->key; + item_new = (HashRecordEntry *) hash_search(record_new->rhash, &k, + HASH_ENTER, &found); + item_new->tuple = heap_copytuple(item_prev->tuple); + } + MemoryContextSwitchTo(oldcxt); } diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 12155e0..36778ad 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -174,6 +174,7 @@ SELECT pgv_select('vars2', 'j1'); -- Manipulate variables SELECT * FROM pgv_list() order by package, name; +SELECT package FROM pgv_stats() order by package; SELECT pgv_remove('vars', 'int3'); SELECT pgv_remove('vars', 'int1'); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql new file mode 100644 index 0000000..a533d13 --- /dev/null +++ b/sql/pg_variables_trans.sql @@ -0,0 +1,648 @@ +SET timezone = 'Europe/Moscow'; -- Need to proper output of datetime variables +--CHECK SAVEPOINT RELEASE +BEGIN; +-- Declare variables +SELECT pgv_set('vars', 'any1', 'some value'::text, true); +SELECT pgv_set('vars', 'any2', 'some value'::text); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_int('vars', 'int2', 102); +SELECT pgv_set_int('vars', 'intNULL', NULL, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_text('vars', 'str2', 's102'); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_numeric('vars', 'num2', 1.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + +SAVEPOINT comm; +-- Set new values +SELECT pgv_set('vars', 'any1', 'another value'::text, true); +SELECT pgv_set('vars', 'any2', 'another value'::text); +SELECT pgv_set_int('vars', 'int1', 103, true); +SELECT pgv_set_int('vars', 'int2', 103); +SELECT pgv_set_int('vars', 'intNULL', 104, true); +SELECT pgv_set_text('vars', 'str1', 's103', true); +SELECT pgv_set_text('vars', 'str2', 's103'); +SELECT pgv_set_numeric('vars', 'num1', 1.03, true); +SELECT pgv_set_numeric('vars', 'num2', 1.03); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); +SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); +SELECT pgv_set_date('vars', 'd2', '2016-04-02'); +SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); + +-- Check values before releasing savepoint +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + +-- Check values after releasing savepoint +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); +COMMIT; + + +BEGIN; +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; +SELECT pgv_insert('vars3', 'r2', tab) FROM tab; +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); +SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +COMMIT; + + + +--CHECK SAVEPOINT ROLLBACK +BEGIN; +-- Variables are already declared +SAVEPOINT comm2; +-- Set new values +SELECT pgv_set('vars', 'any1', 'one more value'::text, true); +SELECT pgv_set('vars', 'any2', 'one more value'::text); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_int('vars', 'int2', 102); +SELECT pgv_set_int('vars', 'intNULL', NULL, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_text('vars', 'str2', 's102'); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_numeric('vars', 'num2', 1.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + +-- Check values before rollback to savepoint +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + +-- Check values after rollback to savepoint +ROLLBACK TO comm2; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); +COMMIT; + + +-- Record variables +BEGIN; +SAVEPOINT comm2; +SELECT pgv_delete('vars3', 'r1', 5); +SELECT pgv_delete('vars3', 'r2', 5); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +COMMIT; + + +-- TRYING TO CHANGE FLAG 'IS_TRANSACTIONAL' +SELECT pgv_set('vars', 'any1', 'value'::text); +SELECT pgv_set('vars', 'any2', 'value'::text, true); +SELECT pgv_set_int('vars', 'int1', 301); +SELECT pgv_set_int('vars', 'int2', 302, true); +SELECT pgv_set_text('vars', 'str1', 's301'); +SELECT pgv_set_text('vars', 'str2', 's302', true); +SELECT pgv_set_numeric('vars', 'num1', 3.01); +SELECT pgv_set_numeric('vars', 'num2', 3.02, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 20:00:00'); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 21:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 20:00:00 GMT+01'); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 21:00:00 GMT+02', true); +SELECT pgv_set_date('vars', 'd1', '2016-04-29'); +SELECT pgv_set_date('vars', 'd2', '2016-04-30', true); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo2", null]'); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz2", "balance": 7.77, "active": true}', true); +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str66' :: varchar)); +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str66' :: varchar),true); + +-- CHECK pgv_list() WHILE WE HAVE A LOT OF MISCELLANEOUS VARIABLES +SELECT * FROM pgv_list() order by package, name; + +SELECT pgv_free(); + +-- VARIABLES DECLARED IN SUBTRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK TO SAVEPOINT + +BEGIN; +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'text value'::text, true); +SELECT pgv_set('vars', 'any2', 'text value'::text); +SELECT pgv_set_int('vars', 'int1', 401, true); +SELECT pgv_set_int('vars', 'int2', 402); +SELECT pgv_set_text('vars', 'str1', 's401', true); +SELECT pgv_set_text('vars', 'str2', 's402'); +SELECT pgv_set_numeric('vars', 'num1', 4.01, true); +SELECT pgv_set_numeric('vars', 'num2', 4.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-04-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); + +SELECT pgv_free(); + + +-- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); +SELECT pgv_set_int('vars', 'int1', 100, true); +SELECT pgv_set_text('vars', 'str1', 's100', true); +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); +SELECT pgv_set_int('vars', 'int1', 102, true); +SELECT pgv_set_text('vars', 'str1', 's102', true); +SELECT pgv_set_numeric('vars', 'num1', 1.02, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_jsonb('vars', 'j1'); + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_jsonb('vars', 'j1'); + +COMMIT; + + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); +SELECT pgv_set_int('vars2', 'int1', 102, true); +SELECT pgv_set_text('vars2', 'str1', 's102', true); +SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); +SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); +SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); +SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); +SELECT pgv_get_int('vars2', 'int1'); +SELECT pgv_get_text('vars2', 'str1'); +SELECT pgv_get_numeric('vars2', 'num1'); +SELECT pgv_get_timestamp('vars2', 'ts1'); +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +SELECT pgv_get_date('vars2', 'd1'); +SELECT pgv_get_jsonb('vars2', 'j1'); + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +SELECT pgv_get_int('vars2', 'int1'); +SELECT pgv_get_text('vars2', 'str1'); +SELECT pgv_get_numeric('vars2', 'num1'); +SELECT pgv_get_timestamp('vars2', 'ts1'); +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +SELECT pgv_get_date('vars2', 'd1'); +SELECT pgv_get_jsonb('vars2', 'j1'); + +SELECT pgv_free(); + +--CHECK TRANSACTION COMMIT +-- Declare variables +SELECT pgv_set('vars', 'any1', 'some value'::text, true); +SELECT pgv_set('vars', 'any2', 'some value'::text); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_int('vars', 'int2', 102); +SELECT pgv_set_int('vars', 'intNULL', NULL, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_text('vars', 'str2', 's102'); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_numeric('vars', 'num2', 1.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'another value'::text, true); +SELECT pgv_set('vars', 'any2', 'another value'::text); +SELECT pgv_set_int('vars', 'int1', 103, true); +SELECT pgv_set_int('vars', 'int2', 103); +SELECT pgv_set_int('vars', 'intNULL', 104, true); +SELECT pgv_set_text('vars', 'str1', 's103', true); +SELECT pgv_set_text('vars', 'str2', 's103'); +SELECT pgv_set_numeric('vars', 'num1', 1.03, true); +SELECT pgv_set_numeric('vars', 'num2', 1.03); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); +SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); +SELECT pgv_set_date('vars', 'd2', '2016-04-02'); +SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); + +-- Check values before committing transaction +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + +-- Check values after committing transaction +COMMIT; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + + +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; +SELECT pgv_insert('vars3', 'r2', tab) FROM tab; +BEGIN; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); +SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +COMMIT; +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); + + +-- CHECK TRANSACTION ROLLBACK +-- Variables are already declared +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'one more value'::text, true); +SELECT pgv_set('vars', 'any2', 'one more value'::text); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_int('vars', 'int2', 102); +SELECT pgv_set_int('vars', 'intNULL', NULL, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_text('vars', 'str2', 's102'); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_numeric('vars', 'num2', 1.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-03-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); + +-- Check values before rollback +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + +-- Check values after rollback +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_int('vars', 'intNULL'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); + + +-- Record variables +BEGIN; +SELECT pgv_delete('vars3', 'r1', 5); +SELECT pgv_delete('vars3', 'r2', 5); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); + +SELECT pgv_free(); + + +-- VARIABLES DECLARED IN TRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK +BEGIN; +SELECT pgv_set('vars', 'any1', 'text value'::text, true); +SELECT pgv_set('vars', 'any2', 'text value'::text); +SELECT pgv_set_int('vars', 'int1', 401, true); +SELECT pgv_set_int('vars', 'int2', 402); +SELECT pgv_set_text('vars', 'str1', 's401', true); +SELECT pgv_set_text('vars', 'str2', 's402'); +SELECT pgv_set_numeric('vars', 'num1', 4.01, true); +SELECT pgv_set_numeric('vars', 'num2', 4.02); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); +SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); +SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); +SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); +SELECT pgv_set_date('vars', 'd2', '2016-04-30'); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); +SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); +SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); +SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get('vars', 'any2',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_int('vars', 'int2'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_text('vars', 'str2'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_numeric('vars', 'num2'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamp('vars', 'ts2'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_timestamptz('vars', 'tstz2'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_date('vars', 'd2'); +SELECT pgv_get_jsonb('vars2', 'j1'); +SELECT pgv_get_jsonb('vars2', 'j2'); +SELECT pgv_select('vars3', 'r1'); +SELECT pgv_select('vars3', 'r2'); + +SELECT pgv_free(); + + +-- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); +SELECT pgv_set_int('vars', 'int1', 100, true); +SELECT pgv_set_text('vars', 'str1', 's100', true); +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); +SELECT pgv_set_int('vars', 'int1', 100, true); +SELECT pgv_set_text('vars', 'str1', 's100', true); +SELECT pgv_set_numeric('vars', 'num1', 1.00, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); +SELECT pgv_set_int('vars', 'int1', 101, true); +SELECT pgv_set_text('vars', 'str1', 's101', true); +SELECT pgv_set_numeric('vars', 'num1', 1.01, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); +SELECT pgv_set_int('vars', 'int1', 102, true); +SELECT pgv_set_text('vars', 'str1', 's102', true); +SELECT pgv_set_numeric('vars', 'num1', 1.02, true); +SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); +SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); +SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); +SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_jsonb('vars', 'j1'); + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_jsonb('vars', 'j1'); + +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); +SELECT pgv_get_int('vars', 'int1'); +SELECT pgv_get_text('vars', 'str1'); +SELECT pgv_get_numeric('vars', 'num1'); +SELECT pgv_get_timestamp('vars', 'ts1'); +SELECT pgv_get_timestamptz('vars', 'tstz1'); +SELECT pgv_get_date('vars', 'd1'); +SELECT pgv_get_jsonb('vars', 'j1'); + + +BEGIN; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); +SELECT pgv_set_int('vars2', 'int1', 102, true); +SELECT pgv_set_text('vars2', 'str1', 's102', true); +SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); +SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); +SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); +SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); +SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); +SELECT pgv_get_int('vars2', 'int1'); +SELECT pgv_get_text('vars2', 'str1'); +SELECT pgv_get_numeric('vars2', 'num1'); +SELECT pgv_get_timestamp('vars2', 'ts1'); +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +SELECT pgv_get_date('vars2', 'd1'); +SELECT pgv_get_jsonb('vars2', 'j1'); + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +SELECT pgv_get_int('vars2', 'int1'); +SELECT pgv_get_text('vars2', 'str1'); +SELECT pgv_get_numeric('vars2', 'num1'); +SELECT pgv_get_timestamp('vars2', 'ts1'); +SELECT pgv_get_timestamptz('vars2', 'tstz1'); +SELECT pgv_get_date('vars2', 'd1'); +SELECT pgv_get_jsonb('vars2', 'j1'); + +SELECT pgv_free(); From 81a684b88c91f774e61631c255b2f818ac01f1d7 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 18 Apr 2018 19:18:56 +0300 Subject: [PATCH 2/8] Extension script generates from init.sql. Refactoring --- .gitignore | 2 + Makefile | 7 +- README.md | 1 - expected/pg_variables_trans.out | 3 +- pg_variables--1.1.sql => init.sql | 10 +- pg_variables--1.0.sql | 156 ----------- pg_variables.c | 436 ++++++++++++++---------------- pg_variables.h | 4 +- pg_variables_record.c | 21 +- 9 files changed, 228 insertions(+), 412 deletions(-) rename pg_variables--1.1.sql => init.sql (94%) delete mode 100644 pg_variables--1.0.sql diff --git a/.gitignore b/.gitignore index cbf8d79..a1eb79a 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ lib*.pc /Debug/ /Release/ /tmp_install/ +pg_variables--1.0.sql +pg_variables--1.1.sql diff --git a/Makefile b/Makefile index da53a06..c2b3358 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,12 @@ MODULE_big = pg_variables OBJS = pg_variables.o pg_variables_record.o $(WIN32RES) EXTENSION = pg_variables -DATA = pg_variables--1.0.sql pg_variables--1.1.sql pg_variables--1.0--1.1.sql +EXTVERSION = 1.1 +DATA = pg_variables--1.0--1.1.sql +DATA_built = $(EXTENSION)--$(EXTVERSION).sql +$(EXTENSION)--$(EXTVERSION).sql: init.sql + cat $^ > $@ + PGFILEDESC = "pg_variables - sessional variables" REGRESS = pg_variables pg_variables_any pg_variables_trans diff --git a/README.md b/README.md index 7a9f2b5..44d5012 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,6 @@ BEGIN; SELECT pgv_set('pack', 'var_text', 'before savepoint'::text, true); SAVEPOINT sp1; SELECT pgv_set('pack', 'var_text', 'savepoint sp1'::text, true); -SELECT pgv_get('pack', 'var_text', NULL::text); SAVEPOINT sp2; SELECT pgv_set('pack', 'var_text', 'savepoint sp2'::text, true); RELEASE sp2; diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index bf96f4a..894911f 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2328,8 +2328,9 @@ SELECT pgv_select('vars3', 'r1'); (,strNULL) (2,) (1,str33) + (5,str55) (0,str00) -(4 rows) +(5 rows) SELECT pgv_select('vars3', 'r2'); pgv_select diff --git a/pg_variables--1.1.sql b/init.sql similarity index 94% rename from pg_variables--1.1.sql rename to init.sql index ead5bf2..e335d39 100644 --- a/pg_variables--1.1.sql +++ b/init.sql @@ -1,4 +1,12 @@ -/* contrib/pg_variables/pg_variables--1.0.sql */ +/* ------------------------------------------------------------------------ + * + * init.sql + * Provides common utility functions + * + * Copyright (c) 2015-2018, Postgres Professional + * + * ------------------------------------------------------------------------ + */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_variables" to load this file. \quit diff --git a/pg_variables--1.0.sql b/pg_variables--1.0.sql deleted file mode 100644 index 2b19ac6..0000000 --- a/pg_variables--1.0.sql +++ /dev/null @@ -1,156 +0,0 @@ -/* contrib/pg_variables/pg_variables--1.0.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION pg_variables" to load this file. \quit - --- Scalar variables functions - -CREATE FUNCTION pgv_set(package text, name text, value anynonarray) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_any' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get(package text, name text, var_type anynonarray, strict bool default true) -RETURNS anynonarray -AS 'MODULE_PATHNAME', 'variable_get_any' -LANGUAGE C VOLATILE; - --- Deprecated scalar variables functions - -CREATE FUNCTION pgv_set_int(package text, name text, value int) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_int' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_int(package text, name text, strict bool default true) -RETURNS int -AS 'MODULE_PATHNAME', 'variable_get_int' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_text(package text, name text, value text) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_text' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_text(package text, name text, strict bool default true) -RETURNS text -AS 'MODULE_PATHNAME', 'variable_get_text' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_numeric(package text, name text, value numeric) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_numeric' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_numeric(package text, name text, strict bool default true) -RETURNS numeric -AS 'MODULE_PATHNAME', 'variable_get_numeric' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_timestamp(package text, name text, value timestamp) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_timestamp' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_timestamp(package text, name text, strict bool default true) -RETURNS timestamp -AS 'MODULE_PATHNAME', 'variable_get_timestamp' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_timestamptz' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_timestamptz(package text, name text, strict bool default true) -RETURNS timestamptz -AS 'MODULE_PATHNAME', 'variable_get_timestamptz' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_date(package text, name text, value date) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_date' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_date(package text, name text, strict bool default true) -RETURNS date -AS 'MODULE_PATHNAME', 'variable_get_date' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_set_jsonb(package text, name text, value jsonb) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_set_jsonb' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_get_jsonb(package text, name text, strict bool default true) -RETURNS jsonb -AS 'MODULE_PATHNAME', 'variable_get_jsonb' -LANGUAGE C VOLATILE; - --- Functions to work with records -CREATE FUNCTION pgv_insert(package text, name text, r record) -RETURNS void -AS 'MODULE_PATHNAME', 'variable_insert' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_update(package text, name text, r record) -RETURNS boolean -AS 'MODULE_PATHNAME', 'variable_update' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_delete(package text, name text, value anynonarray) -RETURNS boolean -AS 'MODULE_PATHNAME', 'variable_delete' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_select(package text, name text) -RETURNS setof record -AS 'MODULE_PATHNAME', 'variable_select' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_select(package text, name text, value anynonarray) -RETURNS record -AS 'MODULE_PATHNAME', 'variable_select_by_value' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_select(package text, name text, value anyarray) -RETURNS setof record -AS 'MODULE_PATHNAME', 'variable_select_by_values' -LANGUAGE C VOLATILE; - --- Functions to work with packages - -CREATE FUNCTION pgv_exists(package text, name text) -RETURNS bool -AS 'MODULE_PATHNAME', 'variable_exists' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_exists(package text) -RETURNS bool -AS 'MODULE_PATHNAME', 'package_exists' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_remove(package text, name text) -RETURNS void -AS 'MODULE_PATHNAME', 'remove_variable' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_remove(package text) -RETURNS void -AS 'MODULE_PATHNAME', 'remove_package' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_free() -RETURNS void -AS 'MODULE_PATHNAME', 'remove_packages' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_list() -RETURNS TABLE(package text, name text) -AS 'MODULE_PATHNAME', 'get_packages_and_variables' -LANGUAGE C VOLATILE; - -CREATE FUNCTION pgv_stats() -RETURNS TABLE(package text, allocated_memory bigint) -AS 'MODULE_PATHNAME', 'get_packages_stats' -LANGUAGE C VOLATILE; diff --git a/pg_variables.c b/pg_variables.c index 262ab68..5758d47 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -70,16 +70,13 @@ static void getKeyFromName(text *name, char *key); static void ensurePackagesHashExists(); static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); -static HashVariableEntry *getVariableByNameWithType(HTAB *variables, - text *name, - Oid typid, - bool create, - bool strict); static HashVariableEntry * -getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, - Oid typid, bool create, bool strict, bool is_transactional); +getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict); +static HashVariableEntry * +createVariableInternal(HashPackageEntry *package, text *name, Oid typid, + bool is_transactional); static void -create_savepoint(HashPackageEntry *package, HashVariableEntry *variable); +createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -103,20 +100,19 @@ static HashVariableEntry *LastVariable = NULL; /* * List of variables, changed in top level transaction. Used to limit * number of proceeded variables on start of transaction. - * NOTE that subtransactions affect ALL transactional variables, even if - * they haven't changed during transaction. */ static dlist_head *changedVars = NULL; +static MemoryContext changedVarsContext = NULL; static bool -isVarChangedInTrans(HashVariableEntry *variable) +isVarChangedInTrans(HashVariableEntry *variable) { - dlist_iter iter; + dlist_iter iter; if (!changedVars) return false; dlist_foreach(iter, changedVars) { - ChangedVarsNode *cvn; + ChangedVarsNode *cvn; cvn = dlist_container(ChangedVarsNode, node, iter.cur); if (cvn->variable == variable) return true; @@ -124,21 +120,54 @@ isVarChangedInTrans(HashVariableEntry *variable) return false; } + + static void -freeChangedVars() +freeChangedVars(void) { - MemoryContext oldcxt; - Assert(ModuleContext && changedVars); - oldcxt = MemoryContextSwitchTo(ModuleContext); - while(!dlist_is_empty(changedVars)){ - ChangedVarsNode *cvnToDelete; - cvnToDelete = dlist_container( ChangedVarsNode, - node, - dlist_pop_head_node(changedVars)); - pfree(cvnToDelete); + if(changedVarsContext) + { + MemoryContextDelete(changedVarsContext); + changedVars = NULL; + changedVarsContext = NULL; + } +} + +static void +addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) +{ + MemoryContext oldcxt; + if(!changedVarsContext) + { + char context_name[BUFSIZ]; + sprintf(context_name, "Memory context for changedVars list"); +#if PG_VERSION_NUM >= 110000 + changedVarsContext = AllocSetContextCreateExtended(ModuleContext, + context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#else + changedVarsContext = AllocSetContextCreate(ModuleContext, + context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#endif + } + oldcxt = MemoryContextSwitchTo(changedVarsContext); + if (!changedVars) + { + changedVars = palloc0(sizeof(dlist_head)); + dlist_init(changedVars); + } + if (!isVarChangedInTrans(variable)) + { + ChangedVarsNode *cvn = palloc0(sizeof(ChangedVarsNode)); + cvn->package = package; + cvn->variable = variable; + dlist_push_head(changedVars, &cvn->node); } - pfree(changedVars); - changedVars = NULL; MemoryContextSwitchTo(oldcxt); } @@ -149,10 +178,8 @@ freeChangedVars() static void deleteFromChangedVars(HashVariableEntry *variable) { - MemoryContext oldcxt; - dlist_mutable_iter miter; - Assert(ModuleContext && changedVars); - oldcxt = MemoryContextSwitchTo(ModuleContext); + dlist_mutable_iter miter; + Assert(changedVarsContext && changedVars); dlist_foreach_modify(miter, changedVars) { ChangedVarsNode *cvn; @@ -163,7 +190,6 @@ deleteFromChangedVars(HashVariableEntry *variable) return; } } - MemoryContextSwitchTo(oldcxt); } /* @@ -180,19 +206,17 @@ variable_set(text *package_name, text *var_name, package = getPackageByName(package_name, true, false); oldcxt = MemoryContextSwitchTo(package->hctx); - variable = getVariableByNameWithTypeAndTrans(package, var_name, typid, true, - false, is_transactional); + variable = createVariableInternal(package, var_name, typid, + is_transactional); - scalar = &(get_actual_value_scalar(variable)); + scalar = get_actual_value_scalar(variable); /* Release memory for variable */ if (scalar->typbyval == false && scalar->is_null == false) pfree(DatumGetPointer(scalar->value)); scalar->is_null = is_null; if (!scalar->is_null) - { scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); - } else scalar->value = 0; MemoryContextSwitchTo(oldcxt); @@ -213,15 +237,15 @@ variable_get(text *package_name, text *var_name, return 0; } - variable = getVariableByNameWithType(package->variablesHash, - var_name, typid, false, strict); + variable = getVariableInternal(package->variablesHash, + var_name, typid, strict); if (variable == NULL) { *is_null = true; return 0; } - scalar = &get_actual_value_scalar(variable); + scalar = get_actual_value_scalar(variable); *is_null = scalar->is_null; return scalar->value; } @@ -660,23 +684,34 @@ variable_insert(PG_FUNCTION_ARGS) { MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(package->hctx); - variable = getVariableByNameWithTypeAndTrans(package, var_name, - RECORDOID, true, false, - is_transactional); + variable = createVariableInternal(package, var_name, RECORDOID, + is_transactional); LastVariable = variable; MemoryContextSwitchTo(oldcxt); } - else if (LastVariable->is_transactional == is_transactional) - variable = LastVariable; else { - char key[NAMEDATALEN]; - getKeyFromName(var_name, key); - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg( - "variable \"%s\" already created as %sTRANSACTIONAL", - key, LastVariable->is_transactional?"":"NOT "))); + if (LastVariable->is_transactional == is_transactional) + variable = LastVariable; + else + { + char key[NAMEDATALEN]; + getKeyFromName(var_name, key); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg( + "variable \"%s\" already created as %sTRANSACTIONAL", + key, LastVariable->is_transactional?"":"NOT "))); + } + if (!isVarChangedInTrans(variable) && variable->is_transactional) + { + int level = GetCurrentTransactionNestLevel(); + while(level -- > 0) + { + createSavepoint(package, variable); + } + addToChangedVars(package, variable); + } } /* Insert a record */ @@ -684,9 +719,9 @@ variable_insert(PG_FUNCTION_ARGS) tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - if (!get_actual_value_record(variable).tupdesc) + if (!(get_actual_value_record(variable))->tupdesc) { - /*/* + /* * This is the first record for the var_name. Initialize attributes. */ init_attributes(variable, tupdesc, package->hctx); @@ -751,13 +786,23 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableByNameWithType(package->variablesHash, - var_name, RECORDOID, false, true); + variable = getVariableInternal(package->variablesHash, + var_name, RECORDOID, true); LastVariable = variable; } else variable = LastVariable; + if (variable->is_transactional && !isVarChangedInTrans(variable)) + { + int level = GetCurrentTransactionNestLevel(); + while(level -- > 0) + { + createSavepoint(package, variable); + } + addToChangedVars(package, variable); + } + /* Update a record */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); @@ -823,12 +868,21 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableByNameWithType(package->variablesHash, var_name, - RECORDOID, false, true); + variable = getVariableInternal(package->variablesHash, + var_name, RECORDOID, true); LastVariable = variable; } else variable = LastVariable; + if (variable->is_transactional && !isVarChangedInTrans(variable)) + { + int level = GetCurrentTransactionNestLevel(); + while(level -- > 0) + { + createSavepoint(package, variable); + } + addToChangedVars(package, variable); + } /* Delete a record */ if (!value_is_null) @@ -865,10 +919,10 @@ variable_select(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableByNameWithType(package->variablesHash, - var_name, RECORDOID, false, true); + variable = getVariableInternal(package->variablesHash, + var_name, RECORDOID, true); - record = &get_actual_value_record(variable); + record = get_actual_value_record(variable); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -938,13 +992,13 @@ variable_select_by_value(PG_FUNCTION_ARGS) } package = getPackageByName(package_name, false, true); - variable = getVariableByNameWithType(package->variablesHash, - var_name, RECORDOID, false, true); + variable = getVariableInternal(package->variablesHash, + var_name, RECORDOID, true); if (!value_is_null) check_record_key(variable, value_type); - record = &get_actual_value_record(variable); + record = get_actual_value_record(variable); /* Search a record */ k.value = value; @@ -1008,8 +1062,8 @@ variable_select_by_values(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableByNameWithType(package->variablesHash, - var_name, RECORDOID, false, true); + variable = getVariableInternal(package->variablesHash, + var_name, RECORDOID, true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -1017,7 +1071,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = CreateTupleDescCopy( - get_actual_value_record(variable).tupdesc); + *get_actual_value_record(variable).tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -1039,7 +1093,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) bool found; RecordVar *record; - record = &get_actual_value_record(var->variable); + record = get_actual_value_record(var->variable); /* Search a record */ k.value = value; k.is_null = isnull; @@ -1047,7 +1101,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) k.cmp_proc = &record->cmp_proc; item = (HashRecordEntry *) hash_search(record->rhash, &k, - HASH_FIND, &found); + HASH_FIND, &found); if (found) { Datum result; @@ -1067,15 +1121,18 @@ variable_select_by_values(PG_FUNCTION_ARGS) * Remove one entry from history of states of arg 'variable' */ static void -clean_variable_current_state(HashVariableEntry *variable) +cleanVariableCurrentState(HashVariableEntry *variable) { ValueHistory *history; ValueHistoryEntry *historyEntryToDelete; if (variable->typid == RECORDOID) clean_records(variable); - else if (get_actual_value_scalar(variable).typbyval == false && - get_actual_value_scalar(variable).is_null == false) - pfree(DatumGetPointer(get_actual_value_scalar(variable).value)); + else + { + ScalarVar *scalar = get_actual_value_scalar(variable); + if (scalar->typbyval == false && scalar->is_null == false) + pfree(DatumGetPointer(scalar->value)); + } history = &variable->data; historyEntryToDelete = get_history_entry(dlist_pop_head_node(history)); pfree(historyEntryToDelete); @@ -1086,10 +1143,11 @@ clean_variable_current_state(HashVariableEntry *variable) * DOES NOT remove 'variable' itself. */ static void -clean_variable_all_states(HashVariableEntry *variable) +cleanVariableAllStates(HashVariableEntry *variable) { - while(!dlist_is_empty(&variable->data)){ - clean_variable_current_state(variable); + while(!dlist_is_empty(&variable->data)) + { + cleanVariableCurrentState(variable); } } @@ -1182,7 +1240,7 @@ remove_variable(PG_FUNCTION_ARGS) /* Remove variable from cache */ LastVariable = NULL; - clean_variable_all_states(variable); + cleanVariableAllStates(variable); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -1225,7 +1283,6 @@ remove_package(PG_FUNCTION_ARGS) LastPackage = NULL; LastVariable = NULL; - hash_destroy(package->variablesHash); /* All variables will be freed */ MemoryContextDelete(package->hctx); @@ -1248,7 +1305,6 @@ remove_packages(PG_FUNCTION_ARGS) LastPackage = NULL; LastVariable = NULL; - hash_destroy(packagesHash); /* All packages and variables will be freed */ MemoryContextDelete(ModuleContext); @@ -1611,13 +1667,12 @@ getPackageByName(text* name, bool create, bool strict) } /* - * Create a variable or return a pointer to existing one. + * Return a pointer to existing variable. * Function is useful to request a value of existing variable and * flag 'is_transactional' of this variable is unknown. */ static HashVariableEntry * -getVariableByNameWithType(HTAB *variables, text *name, Oid typid, - bool create, bool strict) +getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) { HashVariableEntry *variable; char key[NAMEDATALEN]; @@ -1625,11 +1680,7 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, getKeyFromName(name, key); - if (create) - variable = (HashVariableEntry *) hash_search(variables, - key, HASH_ENTER, &found); - else - variable = (HashVariableEntry *) hash_search(variables, + variable = (HashVariableEntry *) hash_search(variables, key, HASH_FIND, &found); /* Check variable type */ @@ -1637,7 +1688,7 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, { if (variable->typid != typid) { - char *var_type = DatumGetCString(DirectFunctionCall1( + char *var_type = DatumGetCString(DirectFunctionCall1( regtypeout, ObjectIdGetDatum(variable->typid))); ereport(ERROR, @@ -1648,25 +1699,7 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, } else { - /* Variable entry was created, so initialize new variable. */ - if (variable) - { - ValueHistoryEntry *historyEntry; - - memset(&variable->data, 0, sizeof(variable->data)); - variable->typid = typid; - dlist_init(&(variable->data)); - historyEntry = palloc0(sizeof(ValueHistoryEntry)); - dlist_push_head(&variable->data, &historyEntry->node); - if (typid != RECORDOID) - { - ScalarVar *scalar = &get_actual_value_scalar(variable); - get_typlenbyval(variable->typid, &scalar->typlen, - &scalar->typbyval); - scalar->is_null = true; - } - } - else if (strict) + if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); @@ -1681,8 +1714,8 @@ getVariableByNameWithType(HTAB *variables, text *name, Oid typid, * flag 'is_transactional' is known. */ static HashVariableEntry * -getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typid, - bool create, bool strict, bool is_transactional) +createVariableInternal(HashPackageEntry *package, text *name, Oid typid, + bool is_transactional) { HashVariableEntry *variable; char key[NAMEDATALEN]; @@ -1690,23 +1723,19 @@ getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typ getKeyFromName(name, key); - if (create) - variable = (HashVariableEntry *) hash_search(package->variablesHash, + variable = (HashVariableEntry *) hash_search(package->variablesHash, key, HASH_ENTER, &found); - else - variable = (HashVariableEntry *) hash_search(package->variablesHash, - key, HASH_FIND, &found); /* Check variable type */ if (found) { if (variable->typid != typid) { - char *var_type = DatumGetCString(DirectFunctionCall1( + char *var_type = DatumGetCString(DirectFunctionCall1( regtypeout, ObjectIdGetDatum(variable->typid))); ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } @@ -1720,14 +1749,17 @@ getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typ } /* - * Create savepoint only if we in top transaction. - * Subtransactions manage it another way + * Savepoint creates when variable changed in current transaction. + * For each transaction level there should be own savepoint. + * New value should be stored in a last state. */ - if (variable->is_transactional && - !isVarChangedInTrans(variable) && - GetCurrentTransactionNestLevel() < 2) + if (variable->is_transactional && !isVarChangedInTrans(variable)) { - create_savepoint(package, variable); + int level = GetCurrentTransactionNestLevel(); + while(level -- > 0) + { + createSavepoint(package, variable); + } } } else @@ -1735,7 +1767,7 @@ getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typ /* Variable entry was created, so initialize new variable. */ if (variable) { - ValueHistoryEntry *historyEntry; + ValueHistoryEntry *historyEntry; memset(&variable->data, 0, sizeof(variable->data)); variable->typid = typid; variable->is_transactional = is_transactional; @@ -1744,35 +1776,17 @@ getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typ dlist_push_head(&variable->data, &historyEntry->node); if (typid != RECORDOID) { - ScalarVar *scalar = &get_actual_value_scalar(variable); + ScalarVar *scalar = get_actual_value_scalar(variable); get_typlenbyval(variable->typid, &scalar->typlen, &scalar->typbyval); scalar->is_null = true; } } - else if (strict) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized variable \"%s\"", key))); } /* If it is necessary, put variable to changedVars */ if (is_transactional) { - MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(ModuleContext); - if (!changedVars) - { - changedVars = palloc0(sizeof(dlist_head)); - dlist_init(changedVars); - } - if (!isVarChangedInTrans(variable)) - { - ChangedVarsNode *cvn = palloc0(sizeof(ChangedVarsNode)); - cvn->package = package; - cvn->variable = variable; - dlist_push_head(changedVars, &cvn->node); - } - MemoryContextSwitchTo(oldcxt); + addToChangedVars(package, variable); } return variable; @@ -1782,9 +1796,9 @@ getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name, Oid typ * Rollback variable to previous state and remove current value */ static void -rollback_savepoint(HashPackageEntry *package, HashVariableEntry *variable) +rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) { - clean_variable_current_state(variable); + cleanVariableCurrentState(variable); /* Remove variable if it was created in rolled back transaction */ if (dlist_is_empty(&variable->data)) { @@ -1799,7 +1813,7 @@ rollback_savepoint(HashPackageEntry *package, HashVariableEntry *variable) * previous state */ static void -create_savepoint(HashPackageEntry *package, HashVariableEntry *variable) +createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) { if(variable->typid == RECORDOID) @@ -1840,9 +1854,9 @@ create_savepoint(HashPackageEntry *package, HashVariableEntry *variable) * Remove previous state of variable */ static void -release_savepoint(HashVariableEntry *variable) +releaseSavepoint(HashVariableEntry *variable) { - ValueHistory *history; + ValueHistory *history; history = &variable->data; if (dlist_has_next(history, dlist_head_node(history))) @@ -1855,8 +1869,6 @@ release_savepoint(HashVariableEntry *variable) if (variable->typid == RECORDOID) { - hash_destroy(historyEntryToDelete->value.record.rhash); - FreeTupleDesc(historyEntryToDelete->value.record.tupdesc); /* All records will be freed */ MemoryContextDelete(historyEntryToDelete->value.record.hctx); } @@ -1872,68 +1884,19 @@ release_savepoint(HashVariableEntry *variable) /* * Possible actions on variables */ -enum Action +typedef enum Action { + CREATE_SAVEPOINT, RELEASE_SAVEPOINT, - ROLLBACK_TO_SAVEPOINT, - CREATE_SAVEPOINT_SUB, - RELEASE_SAVEPOINT_SUB, - ROLLBACK_TO_SAVEPOINT_SUB -}; - -/* - * Iterate all variables from all packages and - * apply corresponding action on them - */ -static void -apply_action_on_variables(enum Action action) -{ - HashPackageEntry *package; - HashVariableEntry *variable; - HASH_SEQ_STATUS vstat, - pstat; - - if (packagesHash) - { - /* Get packages list */ - hash_seq_init(&pstat, packagesHash); - while ((package = - (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) - { - /* Get variables list for package */ - hash_seq_init(&vstat, package->variablesHash); - while ((variable = - (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) - { - if(variable->is_transactional) - { - switch(action) - { - case CREATE_SAVEPOINT_SUB: - create_savepoint(package, variable); - break; - case RELEASE_SAVEPOINT_SUB: - release_savepoint(variable); - break; - case ROLLBACK_TO_SAVEPOINT_SUB: - rollback_savepoint(package, variable); - break; - default: - /* transactions proceeded in apply_action_on_changedVars() */ - break; - } - } - } - } - } -} + ROLLBACK_TO_SAVEPOINT +} Action; /* * Iterate variables from 'changedVars' list and * apply corresponding action on them */ static void -apply_action_on_changedVars(enum Action action) +applyActionOnChangedVars(Action action) { dlist_mutable_iter miter; Assert(changedVars); @@ -1943,13 +1906,13 @@ apply_action_on_changedVars(enum Action action) switch(action) { case RELEASE_SAVEPOINT: - release_savepoint(cvn->variable); + releaseSavepoint(cvn->variable); break; case ROLLBACK_TO_SAVEPOINT: - rollback_savepoint(cvn->package, cvn->variable); + rollbackSavepoint(cvn->package, cvn->variable); break; - default: - /* subtransactions proceeded in apply_action_on_variables() */ + case CREATE_SAVEPOINT: + createSavepoint(cvn->package, cvn->variable); break; } } @@ -1957,59 +1920,56 @@ apply_action_on_changedVars(enum Action action) /* * Intercept execution during subtransaction processing - * Since sub-transactions are created less often, but can have several levels - * of nesting, it's easier to create and roll back savepoints during events. - * Unfortunately, you have to savepoint/rollback all existing transact variables. */ static void -pgv_sub_trans_callback(SubXactEvent event, SubTransactionId mySubid, +pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { - switch (event){ - case SUBXACT_EVENT_START_SUB: - apply_action_on_variables(CREATE_SAVEPOINT_SUB); - break; - case SUBXACT_EVENT_COMMIT_SUB: - apply_action_on_variables(RELEASE_SAVEPOINT_SUB); - break; - case SUBXACT_EVENT_ABORT_SUB: - apply_action_on_variables(ROLLBACK_TO_SAVEPOINT_SUB); - break; - case SUBXACT_EVENT_PRE_COMMIT_SUB: - break; + if (changedVars) + { + switch (event) + { + case SUBXACT_EVENT_START_SUB: + applyActionOnChangedVars(CREATE_SAVEPOINT); + break; + case SUBXACT_EVENT_COMMIT_SUB: + applyActionOnChangedVars(RELEASE_SAVEPOINT); + break; + case SUBXACT_EVENT_ABORT_SUB: + applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); + break; + case SUBXACT_EVENT_PRE_COMMIT_SUB: + break; + } } } /* * Intercept execution during transaction processing - * Since this event occurs frequently, it is necessary to limit - * the number of processed variables. */ static void -pgv_trans_callback(XactEvent event, void *arg) +pgvTransCallback(XactEvent event, void *arg) { if (changedVars) { - switch (event){ + switch (event) + { case XACT_EVENT_PRE_COMMIT: - apply_action_on_changedVars(RELEASE_SAVEPOINT); - freeChangedVars(); + applyActionOnChangedVars(RELEASE_SAVEPOINT); break; case XACT_EVENT_ABORT: - apply_action_on_changedVars(ROLLBACK_TO_SAVEPOINT); - freeChangedVars(); + applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); break; case XACT_EVENT_PARALLEL_PRE_COMMIT: - apply_action_on_changedVars(RELEASE_SAVEPOINT); - freeChangedVars(); + applyActionOnChangedVars(RELEASE_SAVEPOINT); break; case XACT_EVENT_PARALLEL_ABORT: - apply_action_on_changedVars(ROLLBACK_TO_SAVEPOINT); - freeChangedVars(); + applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); break; default: break; } + freeChangedVars(); } } @@ -2018,8 +1978,8 @@ pgv_trans_callback(XactEvent event, void *arg) */ void _PG_init(void) { - RegisterXactCallback(pgv_trans_callback, NULL); - RegisterSubXactCallback(pgv_sub_trans_callback, NULL); + RegisterXactCallback(pgvTransCallback, NULL); + RegisterSubXactCallback(pgvSubTransCallback, NULL); } /* @@ -2027,6 +1987,6 @@ void _PG_init(void) */ void _PG_fini(void) { - UnregisterXactCallback(pgv_trans_callback, NULL); - UnregisterSubXactCallback(pgv_sub_trans_callback, NULL); + UnregisterXactCallback(pgvTransCallback, NULL); + UnregisterSubXactCallback(pgvSubTransCallback, NULL); } diff --git a/pg_variables.h b/pg_variables.h index adfaccd..53b5fe1 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -128,9 +128,9 @@ extern void insert_savepoint(HashVariableEntry *variable, /* Internal macros to manage with dlist structure */ #define get_actual_value_scalar(variable) \ - (dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.scalar + &((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.scalar) #define get_actual_value_record(variable) \ - (dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.record + &((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.record) #define get_history_entry(node_ptr) \ dlist_container(ValueHistoryEntry, node, node_ptr) diff --git a/pg_variables_record.c b/pg_variables_record.c index 10fc971..3de8aa7 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -78,7 +78,7 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, sprintf(hash_name, "Records hash for variable \"%s\"", variable->name); - record = &(get_actual_value_record(variable)); + record = get_actual_value_record(variable); #if PG_VERSION_NUM >= 110000 record->hctx = AllocSetContextCreateExtended(topctx, @@ -143,7 +143,7 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) Assert(variable->typid == RECORDOID); - record = &get_actual_value_record(variable); + record = get_actual_value_record(variable); /* First, check columns count. */ if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, @@ -175,7 +175,7 @@ check_record_key(HashVariableEntry *variable, Oid typid) { RecordVar *record; Assert(variable->typid == RECORDOID); - record = &get_actual_value_record(variable); + record = get_actual_value_record(variable); if (GetTupleDescAttr(record->tupdesc, 0)->atttypid != typid) ereport(ERROR, @@ -203,7 +203,7 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = &(get_actual_value_record(variable)); + record = get_actual_value_record(variable); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -263,7 +263,7 @@ update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = &(get_actual_value_record(variable)); + record = get_actual_value_record(variable); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -313,7 +313,7 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) Assert(variable->typid == RECORDOID); - record = &(get_actual_value_record(variable)); + record = get_actual_value_record(variable); /* Delete a record */ k.value = value; @@ -338,10 +338,7 @@ clean_records(HashVariableEntry *variable) RecordVar *record; Assert(variable->typid == RECORDOID); - record = &get_actual_value_record(variable); - hash_destroy(record->rhash); - FreeTupleDesc(record->tupdesc); - + record = get_actual_value_record(variable); /* All records will be freed */ MemoryContextDelete(record->hctx); } @@ -365,7 +362,7 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) Assert(variable->typid == RECORDOID); /* Create new hstory entry */ - record_prev = &(get_actual_value_record(variable)); + record_prev = get_actual_value_record(variable); oldcxt = MemoryContextSwitchTo(packageContext); history_entry_new = palloc0(sizeof(ValueHistoryEntry)); record_new = &(history_entry_new->value.record); @@ -377,7 +374,7 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) hash_seq_init(rstat, record_prev->rhash); while((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) !=NULL) { - HashRecordKey k; + HashRecordKey k; k = item_prev->key; item_new = (HashRecordEntry *) hash_search(record_new->rhash, &k, HASH_ENTER, &found); From a86437fe276ac420dd0393a40afffcef22e711d9 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 20 Apr 2018 20:26:56 +0300 Subject: [PATCH 3/8] Fix Makefile --- .gitignore | 1 - Makefile | 7 ++++--- init.sql => pg_variables--1.0.sql | 30 +++++++++++------------------- 3 files changed, 15 insertions(+), 23 deletions(-) rename init.sql => pg_variables--1.0.sql (83%) diff --git a/.gitignore b/.gitignore index 5239d7a..0f55eec 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,4 @@ lib*.pc /Release/ /tmp_install/ Dockerfile -pg_variables--1.0.sql pg_variables--1.1.sql diff --git a/Makefile b/Makefile index c2b3358..2f47be0 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,8 @@ OBJS = pg_variables.o pg_variables_record.o $(WIN32RES) EXTENSION = pg_variables EXTVERSION = 1.1 -DATA = pg_variables--1.0--1.1.sql +DATA = pg_variables--1.0.sql pg_variables--1.0--1.1.sql DATA_built = $(EXTENSION)--$(EXTVERSION).sql -$(EXTENSION)--$(EXTVERSION).sql: init.sql - cat $^ > $@ PGFILEDESC = "pg_variables - sessional variables" @@ -24,3 +22,6 @@ top_builddir = ../.. include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif + +$(EXTENSION)--$(EXTVERSION).sql: $(DATA) + cat $^ > $@ diff --git a/init.sql b/pg_variables--1.0.sql similarity index 83% rename from init.sql rename to pg_variables--1.0.sql index e335d39..2b19ac6 100644 --- a/init.sql +++ b/pg_variables--1.0.sql @@ -1,19 +1,11 @@ -/* ------------------------------------------------------------------------ - * - * init.sql - * Provides common utility functions - * - * Copyright (c) 2015-2018, Postgres Professional - * - * ------------------------------------------------------------------------ - */ +/* contrib/pg_variables/pg_variables--1.0.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_variables" to load this file. \quit -- Scalar variables functions -CREATE FUNCTION pgv_set(package text, name text, value anynonarray, is_transactional bool default false) +CREATE FUNCTION pgv_set(package text, name text, value anynonarray) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_any' LANGUAGE C VOLATILE; @@ -25,7 +17,7 @@ LANGUAGE C VOLATILE; -- Deprecated scalar variables functions -CREATE FUNCTION pgv_set_int(package text, name text, value int, is_transactional bool default false) +CREATE FUNCTION pgv_set_int(package text, name text, value int) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_int' LANGUAGE C VOLATILE; @@ -35,7 +27,7 @@ RETURNS int AS 'MODULE_PATHNAME', 'variable_get_int' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_text(package text, name text, value text, is_transactional bool default false) +CREATE FUNCTION pgv_set_text(package text, name text, value text) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_text' LANGUAGE C VOLATILE; @@ -45,7 +37,7 @@ RETURNS text AS 'MODULE_PATHNAME', 'variable_get_text' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_numeric(package text, name text, value numeric, is_transactional bool default false) +CREATE FUNCTION pgv_set_numeric(package text, name text, value numeric) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_numeric' LANGUAGE C VOLATILE; @@ -55,7 +47,7 @@ RETURNS numeric AS 'MODULE_PATHNAME', 'variable_get_numeric' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_timestamp(package text, name text, value timestamp, is_transactional bool default false) +CREATE FUNCTION pgv_set_timestamp(package text, name text, value timestamp) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_timestamp' LANGUAGE C VOLATILE; @@ -65,7 +57,7 @@ RETURNS timestamp AS 'MODULE_PATHNAME', 'variable_get_timestamp' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz, is_transactional bool default false) +CREATE FUNCTION pgv_set_timestamptz(package text, name text, value timestamptz) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_timestamptz' LANGUAGE C VOLATILE; @@ -75,7 +67,7 @@ RETURNS timestamptz AS 'MODULE_PATHNAME', 'variable_get_timestamptz' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_date(package text, name text, value date, is_transactional bool default false) +CREATE FUNCTION pgv_set_date(package text, name text, value date) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_date' LANGUAGE C VOLATILE; @@ -85,7 +77,7 @@ RETURNS date AS 'MODULE_PATHNAME', 'variable_get_date' LANGUAGE C VOLATILE; -CREATE FUNCTION pgv_set_jsonb(package text, name text, value jsonb, is_transactional bool default false) +CREATE FUNCTION pgv_set_jsonb(package text, name text, value jsonb) RETURNS void AS 'MODULE_PATHNAME', 'variable_set_jsonb' LANGUAGE C VOLATILE; @@ -96,7 +88,7 @@ AS 'MODULE_PATHNAME', 'variable_get_jsonb' LANGUAGE C VOLATILE; -- Functions to work with records -CREATE FUNCTION pgv_insert(package text, name text, r record, is_transactional bool default false) +CREATE FUNCTION pgv_insert(package text, name text, r record) RETURNS void AS 'MODULE_PATHNAME', 'variable_insert' LANGUAGE C VOLATILE; @@ -154,7 +146,7 @@ AS 'MODULE_PATHNAME', 'remove_packages' LANGUAGE C VOLATILE; CREATE FUNCTION pgv_list() -RETURNS TABLE(package text, name text, is_transactional bool) +RETURNS TABLE(package text, name text) AS 'MODULE_PATHNAME', 'get_packages_and_variables' LANGUAGE C VOLATILE; From 430989c4c7b52ee5ab775731ac5d71eabfe2a997 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 21 Apr 2018 15:28:31 +0300 Subject: [PATCH 4/8] Fix README.md --- README.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 049421d..0f4e335 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,12 @@ # pg_variables - session variables with various types -[![Build Status](https://travis-ci.org/postgrespro/pg_variables.svg?branch=master)](https://travis-ci.org/postgrespro/pg_variables) -[![codecov](https://codecov.io/gh/postgrespro/pg_variables/branch/master/graph/badge.svg)](https://codecov.io/gh/postgrespro/pg_variables) -[![GitHub license](https://img.shields.io/badge/license-PostgreSQL-blue.svg)](https://raw.githubusercontent.com/postgrespro/pg_variables/master/README.md) - ## Introduction The **pg_variables** module provides functions to work with variables of various types. Created variables live only in the current user session. - -Note that the module does **not support transactions and savepoints by default**. For -example: - +By default, created variables are not transactional (i.e. they are not affected +by `BEGIN`, `COMMIT` or `ROLLBACK` statements). This, however, is customizable +by argument `is_transactional` of `pgv_set()`: ```sql SELECT pgv_set('vars', 'int1', 101); BEGIN; @@ -26,7 +21,7 @@ SELECT * FROM pgv_list() order by package, name; (2 rows) ``` -But if variable created with flag **is_transactional**, it does: +But if variable created with flag **is_transactional**: ```sql BEGIN; SELECT pgv_set('vars', 'trans_int', 101, true); @@ -255,11 +250,11 @@ You can list packages and variables: ```sql SELECT * FROM pgv_list() order by package, name; - package | name ----------+------ - vars | int1 - vars | int2 - vars | r1 + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | r1 | f (3 rows) ``` @@ -285,8 +280,8 @@ You can delete all packages and variables: SELECT pgv_free(); ``` -If you want variables with support of transactions and savepoints, you should add flag -`is_transactional = true` as the last argument in functions `pgv_set()` +If you want variables with support of transactions and savepoints, you should +add flag `is_transactional = true` as the last argument in functions `pgv_set()` or `pgv_insert()`. Following use cases describe behavior of transactional variables: ```sql @@ -317,7 +312,8 @@ SELECT pgv_get('pack', 'var_text', NULL::text); before transaction block ``` -If you create variable after `BEGIN` or `SAVEPOINT` and than rollback to previous state - variable will not be exist: +If you create variable after `BEGIN` or `SAVEPOINT` statements and than rollback +to previous state - variable will not be exist: ```sql BEGIN; SAVEPOINT sp1; @@ -335,7 +331,10 @@ SELECT pgv_get('pack','var_int', NULL::int); ERROR: unrecognized variable "var_int" COMMIT; ``` -If you created transactional variable once, you should use flag `is_transactional` every time when you want to change variable value by functions `pgv_set()`, `pgv_insert()` and deprecated setters (i.e. `pgv_set_int()`). If you try to change this option, you'll get an error: +If you created transactional variable once, you should use flag `is_transactional` +every time when you want to change variable value by functions `pgv_set()`, +`pgv_insert()` and deprecated setters (i.e. `pgv_set_int()`). If you try to +change this option, you'll get an error: ```sql SELECT pgv_insert('pack', 'var_record', row(123::int, 'text'::text), true); pgv_insert From 2fb64f8a500f758b8d4954f3ee8647a8f713f7b7 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 21 Apr 2018 15:29:20 +0300 Subject: [PATCH 5/8] Optimize savepoint creation number --- expected/pg_variables_trans.out | 2309 ++++++------------------------- pg_variables.c | 348 +++-- pg_variables.h | 8 + sql/pg_variables_trans.sql | 375 +---- 4 files changed, 732 insertions(+), 2308 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 894911f..4b3109f 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -928,6 +928,7 @@ SELECT pgv_free(); (1 row) -- VARIABLES DECLARED IN SUBTRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK TO SAVEPOINT +-- For better readability we don't use deprecated api functions in test below BEGIN; SAVEPOINT sp_to_rollback; SELECT pgv_set('vars', 'any1', 'text value'::text, true); @@ -942,90 +943,6 @@ SELECT pgv_set('vars', 'any2', 'text value'::text); (1 row) -SELECT pgv_set_int('vars', 'int1', 401, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'int2', 402); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's401', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str2', 's402'); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 4.01, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_numeric('vars', 'num2', 4.02); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd2', '2016-04-30'); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); - pgv_set_jsonb ---------------- - -(1 row) - SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); pgv_insert ------------ @@ -1048,62 +965,6 @@ SELECT pgv_get('vars', 'any2',NULL::text); text value (1 row) -SELECT pgv_get_int('vars', 'int1'); -ERROR: unrecognized variable "int1" -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 402 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); -ERROR: unrecognized variable "str1" -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s402 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); -ERROR: unrecognized variable "num1" -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 4.02 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); -ERROR: unrecognized variable "ts1" -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Sat Apr 30 21:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); -ERROR: unrecognized variable "tstz1" -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Sun May 01 02:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); -ERROR: unrecognized variable "d1" -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 04-30-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); -ERROR: unrecognized variable "j1" -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb ---------------------------------------------------- - {"bar": "baz4", "active": false, "balance": 4.44} -(1 row) - SELECT pgv_select('vars3', 'r1'); ERROR: unrecognized variable "r1" SELECT pgv_select('vars3', 'r2'); @@ -1126,1757 +987,385 @@ SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); (1 row) -SELECT pgv_set_int('vars', 'int1', 100, true); - pgv_set_int -------------- +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_text('vars', 'str1', 's100', true); - pgv_set_text --------------- +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); - pgv_set_numeric ------------------ - +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 (1 row) -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); - pgv_set_timestamp -------------------- - +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 (1 row) -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); - pgv_set_date --------------- - +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists (1 row) -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - pgv_set_jsonb ---------------- +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_free(); + pgv_free +---------- (1 row) -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); +--CHECK TRANSACTION COMMIT +-- Declare variables +SELECT pgv_set('vars', 'any1', 'some value'::text, true); pgv_set --------- (1 row) -SELECT pgv_set_int('vars', 'int1', 101, true); - pgv_set_int -------------- +SELECT pgv_set('vars', 'any2', 'some value'::text); + pgv_set +--------- (1 row) -SELECT pgv_set_text('vars', 'str1', 's101', true); - pgv_set_text --------------- +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'another value'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); - pgv_set_numeric ------------------ +SELECT pgv_set('vars', 'any2', 'another value'::text); + pgv_set +--------- (1 row) -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); - pgv_set_timestamp -------------------- - +-- Check values before committing transaction +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value (1 row) -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +--------------- + another value (1 row) -SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); - pgv_set_date --------------- - +-- Check values after committing transaction +COMMIT; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value (1 row) -SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); - pgv_set_jsonb +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get --------------- - + another value (1 row) -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; + pgv_insert +------------ -(1 row) - -SELECT pgv_set_int('vars', 'int1', 102, true); - pgv_set_int -------------- -(1 row) + + +(4 rows) -SELECT pgv_set_text('vars', 'str1', 's102', true); - pgv_set_text --------------- +SELECT pgv_insert('vars3', 'r2', tab) FROM tab; + pgv_insert +------------ -(1 row) + + + +(4 rows) -SELECT pgv_set_numeric('vars', 'num1', 1.02, true); - pgv_set_numeric ------------------ +BEGIN; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); + pgv_insert +------------ (1 row) -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); - pgv_set_timestamp -------------------- +SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); + pgv_insert +------------ (1 row) -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - after savepoint sp2 -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 102 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s102 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.02 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 16:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 01-21-2016 -(1 row) - -SELECT pgv_get_jsonb('vars', 'j1'); - pgv_get_jsonb ---------------------- - [1, 2, "foo", null] -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------------- - before savepoint sp1 -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 100 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s100 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.00 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 10:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 14:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 01-01-2016 -(1 row) - -SELECT pgv_get_jsonb('vars', 'j1'); - pgv_get_jsonb ---------------------- - [1, 0, "foo", null] -(1 row) - -COMMIT; -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars2', 'int1', 102, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars2', 'str1', 's102', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -SELECT pgv_get_int('vars2', 'int1'); - pgv_get_int -------------- - 102 -(1 row) - -SELECT pgv_get_text('vars2', 'str1'); - pgv_get_text --------------- - s102 -(1 row) - -SELECT pgv_get_numeric('vars2', 'num1'); - pgv_get_numeric ------------------ - 1.02 -(1 row) - -SELECT pgv_get_timestamp('vars2', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars2', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 16:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars2', 'd1'); - pgv_get_date --------------- - 01-21-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ---------------------- - [1, 2, "foo", null] -(1 row) - -ROLLBACK TO sp1; -COMMIT; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized variable "any1" -SELECT pgv_get_int('vars2', 'int1'); -ERROR: unrecognized variable "int1" -SELECT pgv_get_text('vars2', 'str1'); -ERROR: unrecognized variable "str1" -SELECT pgv_get_numeric('vars2', 'num1'); -ERROR: unrecognized variable "num1" -SELECT pgv_get_timestamp('vars2', 'ts1'); -ERROR: unrecognized variable "ts1" -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -ERROR: unrecognized variable "tstz1" -SELECT pgv_get_date('vars2', 'd1'); -ERROR: unrecognized variable "d1" -SELECT pgv_get_jsonb('vars2', 'j1'); -ERROR: unrecognized variable "j1" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---CHECK TRANSACTION COMMIT --- Declare variables -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'some value'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 101, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'int2', 102); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'intNULL', NULL, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's101', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str2', 's102'); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_numeric('vars', 'num2', 1.02); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd2', '2016-03-30'); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); - pgv_set_jsonb ---------------- - -(1 row) - -BEGIN; --- Set new values -SELECT pgv_set('vars', 'any1', 'another value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'another value'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 103, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'int2', 103); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'intNULL', 104, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's103', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str2', 's103'); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.03, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_numeric('vars', 'num2', 1.03); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd2', '2016-04-02'); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); - pgv_set_jsonb ---------------- - -(1 row) - --- Check values before committing transaction -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------- - another value -(1 row) - -SELECT pgv_get('vars', 'any2',NULL::text); - pgv_get ---------------- - another value -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 103 -(1 row) - -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 103 -(1 row) - -SELECT pgv_get_int('vars', 'intNULL'); - pgv_get_int -------------- - 104 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s103 -(1 row) - -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s103 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.03 -(1 row) - -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 1.03 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Wed Mar 30 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Wed Mar 30 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 18:00:00 2016 MSK -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 18:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 04-02-2016 -(1 row) - -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 04-02-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ------------------------------------------------------ - {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb ------------------------------------------------------ - {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} -(1 row) - --- Check values after committing transaction -COMMIT; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------- - another value -(1 row) - -SELECT pgv_get('vars', 'any2',NULL::text); - pgv_get ---------------- - another value -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 103 -(1 row) - -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 103 -(1 row) - -SELECT pgv_get_int('vars', 'intNULL'); - pgv_get_int -------------- - 104 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s103 -(1 row) - -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s103 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.03 -(1 row) - -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 1.03 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Wed Mar 30 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Wed Mar 30 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 18:00:00 2016 MSK -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 18:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 04-02-2016 -(1 row) - -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 04-02-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ------------------------------------------------------ - {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb ------------------------------------------------------ - {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} -(1 row) - -SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; - pgv_insert ------------- - - - - -(4 rows) - -SELECT pgv_insert('vars3', 'r2', tab) FROM tab; - pgv_insert ------------- - - - - -(4 rows) - -BEGIN; -SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'str55' :: varchar),true); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('vars3', 'r2', row(5 :: integer, 'str55' :: varchar)); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (5,str55) - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (5,str55) - (0,str00) -(5 rows) - --- CHECK TRANSACTION ROLLBACK --- Variables are already declared -BEGIN; --- Set new values -SELECT pgv_set('vars', 'any1', 'one more value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'one more value'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 101, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'int2', 102); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'intNULL', NULL, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's101', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str2', 's102'); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_numeric('vars', 'num2', 1.02); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd2', '2016-03-30'); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); - pgv_set_jsonb ---------------- - -(1 row) - --- Check values before rollback -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------- - one more value -(1 row) - -SELECT pgv_get('vars', 'any2',NULL::text); - pgv_get ----------------- - one more value -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 101 -(1 row) - -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 102 -(1 row) - -SELECT pgv_get_int('vars', 'intNULL'); - pgv_get_int -------------- - -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s101 -(1 row) - -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s102 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.01 -(1 row) - -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 1.02 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Wed Mar 30 10:00:00 2016 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Wed Mar 30 11:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 14:00:00 2016 MSK -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 16:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 03-29-2016 -(1 row) - -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 03-30-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ---------------------- - [1, 2, "foo", null] -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb --------------------------------------------------- - {"bar": "baz", "active": false, "balance": 7.77} -(1 row) - --- Check values after rollback -ROLLBACK; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------- - another value -(1 row) - -SELECT pgv_get('vars', 'any2',NULL::text); - pgv_get ----------------- - one more value -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 103 -(1 row) - -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 102 -(1 row) - -SELECT pgv_get_int('vars', 'intNULL'); - pgv_get_int -------------- - 104 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s103 -(1 row) - -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s102 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.03 -(1 row) - -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 1.02 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Wed Mar 30 12:00:00 2016 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Wed Mar 30 11:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 18:00:00 2016 MSK -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Wed Mar 30 16:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 04-02-2016 -(1 row) - -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 03-30-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ------------------------------------------------------ - {"foo": [true, "bar"], "tags": {"a": 1, "b": null}} -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb --------------------------------------------------- - {"bar": "baz", "active": false, "balance": 7.77} -(1 row) - --- Record variables -BEGIN; -SELECT pgv_delete('vars3', 'r1', 5); - pgv_delete ------------- - t -(1 row) - -SELECT pgv_delete('vars3', 'r2', 5); - pgv_delete ------------- - t -(1 row) - -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (0,str00) -(4 rows) - -ROLLBACK; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (2,) - (1,str33) - (0,str00) -(4 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- VARIABLES DECLARED IN TRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK -BEGIN; -SELECT pgv_set('vars', 'any1', 'text value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'text value'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 401, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_int('vars', 'int2', 402); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's401', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str2', 's402'); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 4.01, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_numeric('vars', 'num2', 4.02); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd2', '2016-04-30'); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); - pgv_set_jsonb ---------------- - -(1 row) - -SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); - pgv_insert ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized variable "any1" -SELECT pgv_get('vars', 'any2',NULL::text); - pgv_get ------------- - text value -(1 row) - -SELECT pgv_get_int('vars', 'int1'); -ERROR: unrecognized variable "int1" -SELECT pgv_get_int('vars', 'int2'); - pgv_get_int -------------- - 402 -(1 row) - -SELECT pgv_get_text('vars', 'str1'); -ERROR: unrecognized variable "str1" -SELECT pgv_get_text('vars', 'str2'); - pgv_get_text --------------- - s402 -(1 row) - -SELECT pgv_get_numeric('vars', 'num1'); -ERROR: unrecognized variable "num1" -SELECT pgv_get_numeric('vars', 'num2'); - pgv_get_numeric ------------------ - 4.02 -(1 row) - -SELECT pgv_get_timestamp('vars', 'ts1'); -ERROR: unrecognized variable "ts1" -SELECT pgv_get_timestamp('vars', 'ts2'); - pgv_get_timestamp --------------------------- - Sat Apr 30 21:00:00 2016 -(1 row) - -SELECT pgv_get_timestamptz('vars', 'tstz1'); -ERROR: unrecognized variable "tstz1" -SELECT pgv_get_timestamptz('vars', 'tstz2'); - pgv_get_timestamptz ------------------------------- - Sun May 01 02:00:00 2016 MSK -(1 row) - -SELECT pgv_get_date('vars', 'd1'); -ERROR: unrecognized variable "d1" -SELECT pgv_get_date('vars', 'd2'); - pgv_get_date --------------- - 04-30-2016 -(1 row) - -SELECT pgv_get_jsonb('vars2', 'j1'); -ERROR: unrecognized variable "j1" -SELECT pgv_get_jsonb('vars2', 'j2'); - pgv_get_jsonb ---------------------------------------------------- - {"bar": "baz4", "active": false, "balance": 4.44} -(1 row) - -SELECT pgv_select('vars3', 'r1'); -ERROR: unrecognized variable "r1" -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (6,str44) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION -SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 100, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's100', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_set_int('vars', 'int1', 100, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's100', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); - pgv_set_date --------------- - -(1 row) +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); +-- CHECK TRANSACTION ROLLBACK +-- Variables are already declared +BEGIN; +-- Set new values +SELECT pgv_set('vars', 'any1', 'one more value'::text, true); pgv_set --------- (1 row) -SELECT pgv_set_int('vars', 'int1', 101, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's101', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); +SELECT pgv_set('vars', 'any2', 'one more value'::text); pgv_set --------- (1 row) -SELECT pgv_set_int('vars', 'int1', 102, true); - pgv_set_int -------------- - -(1 row) - -SELECT pgv_set_text('vars', 'str1', 's102', true); - pgv_set_text --------------- - -(1 row) - -SELECT pgv_set_numeric('vars', 'num1', 1.02, true); - pgv_set_numeric ------------------ - -(1 row) - -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); - pgv_set_timestamp -------------------- - -(1 row) - -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- - -(1 row) - -SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); - pgv_set_date --------------- - -(1 row) - -SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- - -(1 row) - -RELEASE sp2; +-- Check values before rollback SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - after savepoint sp2 -(1 row) - -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 102 + pgv_get +---------------- + one more value (1 row) -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s102 +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value (1 row) -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.02 +-- Check values after rollback +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------- + another value (1 row) -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 12:00:00 2016 +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +---------------- + one more value (1 row) -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 16:00:00 2016 MSK +-- Record variables +BEGIN; +SELECT pgv_delete('vars3', 'r1', 5); + pgv_delete +------------ + t (1 row) -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 01-21-2016 +SELECT pgv_delete('vars3', 'r2', 5); + pgv_delete +------------ + t (1 row) -SELECT pgv_get_jsonb('vars', 'j1'); - pgv_get_jsonb ---------------------- - [1, 2, "foo", null] -(1 row) +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------------- - before savepoint sp1 -(1 row) +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 100 -(1 row) +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (5,str55) + (0,str00) +(5 rows) -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s100 -(1 row) +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (2,) + (1,str33) + (0,str00) +(4 rows) -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.00 +SELECT pgv_free(); + pgv_free +---------- + (1 row) -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 10:00:00 2016 +-- VARIABLES DECLARED IN TRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK +BEGIN; +SELECT pgv_set('vars', 'any1', 'text value'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 14:00:00 2016 MSK +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + (1 row) -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 01-01-2016 +SELECT pgv_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + (1 row) -SELECT pgv_get_jsonb('vars', 'j1'); - pgv_get_jsonb ---------------------- - [1, 0, "foo", null] +SELECT pgv_insert('vars', 'r2', row(6 :: integer, 'str44' :: varchar)); + pgv_insert +------------ + (1 row) ROLLBACK; SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get --------------------------- - before transaction block +ERROR: unrecognized variable "any1" +SELECT pgv_get('vars', 'any2',NULL::text); + pgv_get +------------ + text value (1 row) -SELECT pgv_get_int('vars', 'int1'); - pgv_get_int -------------- - 100 +SELECT pgv_select('vars', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (6,str44) (1 row) -SELECT pgv_get_text('vars', 'str1'); - pgv_get_text --------------- - s100 +SELECT pgv_remove('vars'); + pgv_remove +------------ + (1 row) -SELECT pgv_get_numeric('vars', 'num1'); - pgv_get_numeric ------------------ - 1.00 +-- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_timestamp('vars', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 10:00:00 2016 +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_timestamptz('vars', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 14:00:00 2016 MSK +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_date('vars', 'd1'); - pgv_get_date --------------- - 01-01-2016 +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_jsonb('vars', 'j1'); - pgv_get_jsonb +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get --------------------- - [1, 0, "foo", null] + after savepoint sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +-------------------------- + before transaction block (1 row) BEGIN; @@ -2887,117 +1376,225 @@ SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); (1 row) -SELECT pgv_set_int('vars2', 'int1', 102, true); - pgv_set_int -------------- - +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists (1 row) -SELECT pgv_set_text('vars2', 'str1', 's102', true); - pgv_set_text --------------- +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +SELECT pgv_free(); + pgv_free +---------- (1 row) -SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); - pgv_set_numeric ------------------ +--Additional tests +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; + pgv_insert +------------ + + + + +(4 rows) + +BEGIN; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'before savepoint sp1' :: varchar),true); + pgv_insert +------------ (1 row) -SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); - pgv_set_timestamp -------------------- +SAVEPOINT sp1; +SELECT pgv_update('vars3', 'r1', row(5 :: integer, 'after savepoint sp1' :: varchar)); + pgv_update +------------ + t +(1 row) + +SAVEPOINT sp2; +SELECT pgv_insert('vars3', 'r1', row(7 :: integer, 'row after sp2 to remove in sp4' :: varchar),true); + pgv_insert +------------ (1 row) -SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); - pgv_set_timestamptz ---------------------- +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_delete('vars3', 'r1', 7); + pgv_delete +------------ + t +(1 row) + +SAVEPOINT sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"after savepoint sp1") + (0,str00) + (7,"row after sp2 to remove in sp4") +(6 rows) + +RELEASE sp2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"after savepoint sp1") + (0,str00) + (7,"row after sp2 to remove in sp4") +(6 rows) + +ROLLBACK TO sp1; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (2,) + (1,str33) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); - pgv_set_date --------------- +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); + pgv_set +--------- (1 row) -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - pgv_set_jsonb ---------------- +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); + pgv_set +--------- (1 row) -RELEASE sp1; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_int('vars2', 'int1'); - pgv_get_int -------------- - 102 +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); + pgv_set +--------- + (1 row) -SELECT pgv_get_text('vars2', 'str1'); - pgv_get_text --------------- - s102 +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 (1 row) -SELECT pgv_get_numeric('vars2', 'num1'); - pgv_get_numeric ------------------ - 1.02 +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 (1 row) -SELECT pgv_get_timestamp('vars2', 'ts1'); - pgv_get_timestamp --------------------------- - Fri Jan 01 12:00:00 2016 +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 (1 row) -SELECT pgv_get_timestamptz('vars2', 'tstz1'); - pgv_get_timestamptz ------------------------------- - Fri Jan 01 16:00:00 2016 MSK +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 (1 row) -SELECT pgv_get_date('vars2', 'd1'); - pgv_get_date --------------- - 01-21-2016 +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 (1 row) -SELECT pgv_get_jsonb('vars2', 'j1'); - pgv_get_jsonb ---------------------- - [1, 2, "foo", null] +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + begin (1 row) ROLLBACK; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized variable "any1" -SELECT pgv_get_int('vars2', 'int1'); -ERROR: unrecognized variable "int1" -SELECT pgv_get_text('vars2', 'str1'); -ERROR: unrecognized variable "str1" -SELECT pgv_get_numeric('vars2', 'num1'); -ERROR: unrecognized variable "num1" -SELECT pgv_get_timestamp('vars2', 'ts1'); -ERROR: unrecognized variable "ts1" -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -ERROR: unrecognized variable "tstz1" -SELECT pgv_get_date('vars2', 'd1'); -ERROR: unrecognized variable "d1" -SELECT pgv_get_jsonb('vars2', 'j1'); -ERROR: unrecognized variable "j1" -SELECT pgv_free(); - pgv_free ----------- - +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + outer (1 row) +BEGIN; +SELECT pgv_set('vars', 'any1', 'wrong type'::varchar, true); +ERROR: variable "any1" requires "text" value +COMMIT; diff --git a/pg_variables.c b/pg_variables.c index 5758d47..2fed54a 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -77,6 +77,10 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool is_transactional); static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); +static bool +isVarChangedInTrans(HashVariableEntry *variable); +static void +addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -97,100 +101,18 @@ static MemoryContext ModuleContext = NULL; static HashPackageEntry *LastPackage = NULL; /* Recent variable */ static HashVariableEntry *LastVariable = NULL; + /* * List of variables, changed in top level transaction. Used to limit * number of proceeded variables on start of transaction. */ static dlist_head *changedVars = NULL; static MemoryContext changedVarsContext = NULL; +static dlist_head *changedVarsStack = NULL; +#define get_actual_changed_vars_list() \ + (dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ + changedVarsList -static bool -isVarChangedInTrans(HashVariableEntry *variable) -{ - dlist_iter iter; - if (!changedVars) - return false; - dlist_foreach(iter, changedVars) - { - ChangedVarsNode *cvn; - cvn = dlist_container(ChangedVarsNode, node, iter.cur); - if (cvn->variable == variable) - return true; - } - return false; -} - - - -static void -freeChangedVars(void) -{ - if(changedVarsContext) - { - MemoryContextDelete(changedVarsContext); - changedVars = NULL; - changedVarsContext = NULL; - } -} - -static void -addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) -{ - MemoryContext oldcxt; - if(!changedVarsContext) - { - char context_name[BUFSIZ]; - sprintf(context_name, "Memory context for changedVars list"); -#if PG_VERSION_NUM >= 110000 - changedVarsContext = AllocSetContextCreateExtended(ModuleContext, - context_name, - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#else - changedVarsContext = AllocSetContextCreate(ModuleContext, - context_name, - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#endif - } - oldcxt = MemoryContextSwitchTo(changedVarsContext); - if (!changedVars) - { - changedVars = palloc0(sizeof(dlist_head)); - dlist_init(changedVars); - } - if (!isVarChangedInTrans(variable)) - { - ChangedVarsNode *cvn = palloc0(sizeof(ChangedVarsNode)); - cvn->package = package; - cvn->variable = variable; - dlist_push_head(changedVars, &cvn->node); - } - MemoryContextSwitchTo(oldcxt); -} - -/* - * The function deletes the variable only from the list, - * the variable itself continues to exist. - */ -static void -deleteFromChangedVars(HashVariableEntry *variable) -{ - dlist_mutable_iter miter; - Assert(changedVarsContext && changedVars); - dlist_foreach_modify(miter, changedVars) - { - ChangedVarsNode *cvn; - cvn = dlist_container(ChangedVarsNode, node, miter.cur); - if (cvn->variable == variable) - { - dlist_delete(miter.cur); - return; - } - } -} /* * Set value of variable, typlen could be 0 if typbyval == true @@ -705,11 +627,7 @@ variable_insert(PG_FUNCTION_ARGS) } if (!isVarChangedInTrans(variable) && variable->is_transactional) { - int level = GetCurrentTransactionNestLevel(); - while(level -- > 0) - { - createSavepoint(package, variable); - } + createSavepoint(package, variable); addToChangedVars(package, variable); } } @@ -795,11 +713,7 @@ variable_update(PG_FUNCTION_ARGS) if (variable->is_transactional && !isVarChangedInTrans(variable)) { - int level = GetCurrentTransactionNestLevel(); - while(level -- > 0) - { - createSavepoint(package, variable); - } + createSavepoint(package, variable); addToChangedVars(package, variable); } @@ -876,11 +790,7 @@ variable_delete(PG_FUNCTION_ARGS) variable = LastVariable; if (variable->is_transactional && !isVarChangedInTrans(variable)) { - int level = GetCurrentTransactionNestLevel(); - while(level -- > 0) - { - createSavepoint(package, variable); - } + createSavepoint(package, variable); addToChangedVars(package, variable); } @@ -1749,17 +1659,14 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, } /* - * Savepoint creates when variable changed in current transaction. + * Savepoint must be created when variable changed in current + * transaction. * For each transaction level there should be own savepoint. * New value should be stored in a last state. */ if (variable->is_transactional && !isVarChangedInTrans(variable)) { - int level = GetCurrentTransactionNestLevel(); - while(level -- > 0) - { - createSavepoint(package, variable); - } + createSavepoint(package, variable); } } else @@ -1785,29 +1692,11 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, } /* If it is necessary, put variable to changedVars */ if (is_transactional) - { addToChangedVars(package, variable); - } return variable; } -/* - * Rollback variable to previous state and remove current value - */ -static void -rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) -{ - cleanVariableCurrentState(variable); - /* Remove variable if it was created in rolled back transaction */ - if (dlist_is_empty(&variable->data)) - { - bool found; - hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); - deleteFromChangedVars(variable); - } -} - /* * Create a new history point of variable and copy value from * previous state @@ -1882,24 +1771,206 @@ releaseSavepoint(HashVariableEntry *variable) } /* - * Possible actions on variables + * Rollback variable to previous state and remove current value + */ +static void +rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) +{ + cleanVariableCurrentState(variable); + /* Remove variable if it was created in rolled back transaction */ + if (dlist_is_empty(&variable->data)) + { + bool found; + hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); + } +} + +/* + * Check if variable was changed in current transaction level + */ +static bool +isVarChangedInTrans(HashVariableEntry *variable) +{ + dlist_iter iter; + dlist_head *changedVars; + if (!changedVarsStack) + return false; + changedVars = get_actual_changed_vars_list(); + dlist_foreach(iter, changedVars) + { + ChangedVarsNode *cvn; + cvn = dlist_container(ChangedVarsNode, node, iter.cur); + if (cvn->variable == variable) + return true; + } + return false; +} + +/* + * Create new list of variables, changed in current transaction level + */ +static void +pushChangedVarsStack() +{ + MemoryContext oldcxt; + ChangedVarsStackNode *cvsn; + char child_context_name[BUFSIZ]; + /* + * Initialize changedVarsStack and create MemoryContext for it + * if not done before. + */ + if(!changedVarsContext) + { + char top_context_name[BUFSIZ]; + sprintf(top_context_name, "Memory context for changedVarsStack"); +#if PG_VERSION_NUM >= 110000 + changedVarsContext = AllocSetContextCreateExtended(ModuleContext, + top_context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#else + changedVarsContext = AllocSetContextCreate(ModuleContext, + top_context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#endif + } + oldcxt = MemoryContextSwitchTo(changedVarsContext); + if (!changedVarsStack) + { + changedVarsStack = palloc0(sizeof(dlist_head)); + dlist_init(changedVarsStack); + } + cvsn = palloc0(sizeof(ChangedVarsStackNode)); + cvsn->changedVarsList = palloc0(sizeof(dlist_head)); + sprintf(child_context_name, + "Memory context for changedVars list in %d xact level", + GetCurrentTransactionNestLevel()); +#if PG_VERSION_NUM >= 110000 + cvsn->ctx = AllocSetContextCreateExtended(changedVarsContext, + child_context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#else + cvsn->ctx = AllocSetContextCreate(changedVarsContext, + child_context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#endif + dlist_init(cvsn->changedVarsList); + dlist_push_head(changedVarsStack, &cvsn->node); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * Remove list of variables, changed in current transaction level + */ +static void +popChangedVarsStack() +{ + if (changedVarsStack) + { + ChangedVarsStackNode *cvse; + Assert(!dlist_is_empty(changedVarsStack)); + cvse = dlist_container(ChangedVarsStackNode, node, + dlist_pop_head_node(changedVarsStack)); + MemoryContextDelete(cvse->ctx); + if (dlist_is_empty(changedVarsStack)) + { + MemoryContextDelete(changedVarsContext); + changedVarsStack = NULL; + changedVarsContext = NULL; + } + } +} + +/* + * Add a variable to list of changed vars in current transaction level + */ +static void +addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) +{ + MemoryContext oldcxt; + ChangedVarsStackNode *cvsn; + if (!changedVarsStack) + { + int level = GetCurrentTransactionNestLevel(); + while(level -- > 0) + { + pushChangedVarsStack(); + } + } + Assert(changedVarsStack && changedVarsContext); + + if (!isVarChangedInTrans(variable)) + { + ChangedVarsNode *cvn; + cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); + oldcxt = MemoryContextSwitchTo(cvsn->ctx); + cvn = palloc0(sizeof(ChangedVarsNode)); + cvn->package = package; + cvn->variable = variable; + dlist_push_head(cvsn->changedVarsList, &cvn->node); + MemoryContextSwitchTo(oldcxt); + } +} + +/* + * If variable was chenged in some subtransaction, it is considered that it was + * changed in parent transaction. So it is important to add this variable to + * list of changes of parent transaction. But if var was already changed in + * upper level, it has savepoint there, so we need to release it. + */ +static void +lelevUpOrRelease() +{ + if (changedVarsStack) + { + dlist_iter iter; + ChangedVarsStackNode *bottom_list; + /* List removed from stack but we still can use it */ + bottom_list = dlist_container(ChangedVarsStackNode, node, + dlist_pop_head_node(changedVarsStack)); + Assert(!dlist_is_empty(changedVarsStack)); + dlist_foreach(iter, bottom_list->changedVarsList) + { + ChangedVarsNode *cvn; + cvn = dlist_container(ChangedVarsNode, node, iter.cur); + if(isVarChangedInTrans(cvn->variable)) + releaseSavepoint(cvn->variable); + else + addToChangedVars(cvn->package, cvn->variable); + } + MemoryContextDelete(bottom_list->ctx); + } +} + +/* + * Possible actions on variables. + * Savepoints are created in setters so we don't need a CREATE_SAVEPOINT action. */ typedef enum Action { - CREATE_SAVEPOINT, RELEASE_SAVEPOINT, ROLLBACK_TO_SAVEPOINT } Action; /* - * Iterate variables from 'changedVars' list and + * Iterate variables from list of changes and * apply corresponding action on them */ static void applyActionOnChangedVars(Action action) { dlist_mutable_iter miter; - Assert(changedVars); + dlist_head *changedVars; + Assert(changedVarsStack); + changedVars = get_actual_changed_vars_list(); dlist_foreach_modify(miter, changedVars) { ChangedVarsNode *cvn = dlist_container(ChangedVarsNode, node, miter.cur); @@ -1911,9 +1982,6 @@ applyActionOnChangedVars(Action action) case ROLLBACK_TO_SAVEPOINT: rollbackSavepoint(cvn->package, cvn->variable); break; - case CREATE_SAVEPOINT: - createSavepoint(cvn->package, cvn->variable); - break; } } } @@ -1925,18 +1993,19 @@ static void pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { - if (changedVars) + if (changedVarsStack) { switch (event) { case SUBXACT_EVENT_START_SUB: - applyActionOnChangedVars(CREATE_SAVEPOINT); + pushChangedVarsStack(); break; case SUBXACT_EVENT_COMMIT_SUB: - applyActionOnChangedVars(RELEASE_SAVEPOINT); + lelevUpOrRelease(); break; case SUBXACT_EVENT_ABORT_SUB: applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); + popChangedVarsStack(); break; case SUBXACT_EVENT_PRE_COMMIT_SUB: break; @@ -1950,26 +2019,29 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, static void pgvTransCallback(XactEvent event, void *arg) { - if (changedVars) + if (changedVarsStack) { switch (event) { case XACT_EVENT_PRE_COMMIT: applyActionOnChangedVars(RELEASE_SAVEPOINT); + popChangedVarsStack(); break; case XACT_EVENT_ABORT: applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); + popChangedVarsStack(); break; case XACT_EVENT_PARALLEL_PRE_COMMIT: applyActionOnChangedVars(RELEASE_SAVEPOINT); + popChangedVarsStack(); break; case XACT_EVENT_PARALLEL_ABORT: applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); + popChangedVarsStack(); break; default: break; } - freeChangedVars(); } } diff --git a/pg_variables.h b/pg_variables.h index 53b5fe1..eb4ce63 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -110,6 +110,14 @@ typedef struct ChangedVarsNode HashVariableEntry *variable; } ChangedVarsNode; +/* Element of stack with 'changedVars' list heads*/ +typedef struct ChangedVarsStackNode +{ + dlist_node node; + dlist_head *changedVarsList; + MemoryContext ctx; +} ChangedVarsStackNode; + extern void init_attributes(HashVariableEntry* variable, TupleDesc tupdesc, MemoryContext topctx); extern void check_attributes(HashVariableEntry *variable, TupleDesc tupdesc); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index a533d13..976beef 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -199,45 +199,17 @@ SELECT * FROM pgv_list() order by package, name; SELECT pgv_free(); -- VARIABLES DECLARED IN SUBTRANSACTION SHOULD BE DESTROYED AFTER ROLLBACK TO SAVEPOINT - +-- For better readability we don't use deprecated api functions in test below BEGIN; SAVEPOINT sp_to_rollback; SELECT pgv_set('vars', 'any1', 'text value'::text, true); SELECT pgv_set('vars', 'any2', 'text value'::text); -SELECT pgv_set_int('vars', 'int1', 401, true); -SELECT pgv_set_int('vars', 'int2', 402); -SELECT pgv_set_text('vars', 'str1', 's401', true); -SELECT pgv_set_text('vars', 'str2', 's402'); -SELECT pgv_set_numeric('vars', 'num1', 4.01, true); -SELECT pgv_set_numeric('vars', 'num2', 4.02); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); -SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); -SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); -SELECT pgv_set_date('vars', 'd2', '2016-04-30'); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); ROLLBACK TO sp_to_rollback; COMMIT; SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); SELECT pgv_select('vars3', 'r1'); SELECT pgv_select('vars3', 'r2'); @@ -247,170 +219,43 @@ SELECT pgv_free(); -- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION BEGIN; SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); -SELECT pgv_set_int('vars', 'int1', 100, true); -SELECT pgv_set_text('vars', 'str1', 's100', true); -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - SAVEPOINT sp1; SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); -SELECT pgv_set_int('vars', 'int1', 101, true); -SELECT pgv_set_text('vars', 'str1', 's101', true); -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); - SAVEPOINT sp2; SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); -SELECT pgv_set_int('vars', 'int1', 102, true); -SELECT pgv_set_text('vars', 'str1', 's102', true); -SELECT pgv_set_numeric('vars', 'num1', 1.02, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); - RELEASE sp2; SELECT pgv_get('vars', 'any1',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_jsonb('vars', 'j1'); - ROLLBACK TO sp1; SELECT pgv_get('vars', 'any1',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_jsonb('vars', 'j1'); - COMMIT; - BEGIN; SAVEPOINT sp1; SAVEPOINT sp2; SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); -SELECT pgv_set_int('vars2', 'int1', 102, true); -SELECT pgv_set_text('vars2', 'str1', 's102', true); -SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); -SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); -SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); -SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - RELEASE sp2; SELECT pgv_get('vars2', 'any1',NULL::text); -SELECT pgv_get_int('vars2', 'int1'); -SELECT pgv_get_text('vars2', 'str1'); -SELECT pgv_get_numeric('vars2', 'num1'); -SELECT pgv_get_timestamp('vars2', 'ts1'); -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -SELECT pgv_get_date('vars2', 'd1'); -SELECT pgv_get_jsonb('vars2', 'j1'); - ROLLBACK TO sp1; COMMIT; SELECT pgv_get('vars2', 'any1',NULL::text); -SELECT pgv_get_int('vars2', 'int1'); -SELECT pgv_get_text('vars2', 'str1'); -SELECT pgv_get_numeric('vars2', 'num1'); -SELECT pgv_get_timestamp('vars2', 'ts1'); -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -SELECT pgv_get_date('vars2', 'd1'); -SELECT pgv_get_jsonb('vars2', 'j1'); - SELECT pgv_free(); --CHECK TRANSACTION COMMIT -- Declare variables SELECT pgv_set('vars', 'any1', 'some value'::text, true); SELECT pgv_set('vars', 'any2', 'some value'::text); -SELECT pgv_set_int('vars', 'int1', 101, true); -SELECT pgv_set_int('vars', 'int2', 102); -SELECT pgv_set_int('vars', 'intNULL', NULL, true); -SELECT pgv_set_text('vars', 'str1', 's101', true); -SELECT pgv_set_text('vars', 'str2', 's102'); -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); -SELECT pgv_set_numeric('vars', 'num2', 1.02); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); -SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); -SELECT pgv_set_date('vars', 'd2', '2016-03-30'); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); BEGIN; -- Set new values SELECT pgv_set('vars', 'any1', 'another value'::text, true); SELECT pgv_set('vars', 'any2', 'another value'::text); -SELECT pgv_set_int('vars', 'int1', 103, true); -SELECT pgv_set_int('vars', 'int2', 103); -SELECT pgv_set_int('vars', 'intNULL', 104, true); -SELECT pgv_set_text('vars', 'str1', 's103', true); -SELECT pgv_set_text('vars', 'str2', 's103'); -SELECT pgv_set_numeric('vars', 'num1', 1.03, true); -SELECT pgv_set_numeric('vars', 'num2', 1.03); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 12:00:00', true); -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 12:00:00'); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 12:00:00 GMT+03', true); -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 12:00:00 GMT+03'); -SELECT pgv_set_date('vars', 'd1', '2016-04-02', true); -SELECT pgv_set_date('vars', 'd2', '2016-04-02'); -SELECT pgv_set_jsonb('vars2', 'j1', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}', true); -SELECT pgv_set_jsonb('vars2', 'j2', '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'); - -- Check values before committing transaction SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_int('vars', 'intNULL'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); - -- Check values after committing transaction COMMIT; SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_int('vars', 'intNULL'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; @@ -431,61 +276,15 @@ BEGIN; -- Set new values SELECT pgv_set('vars', 'any1', 'one more value'::text, true); SELECT pgv_set('vars', 'any2', 'one more value'::text); -SELECT pgv_set_int('vars', 'int1', 101, true); -SELECT pgv_set_int('vars', 'int2', 102); -SELECT pgv_set_int('vars', 'intNULL', NULL, true); -SELECT pgv_set_text('vars', 'str1', 's101', true); -SELECT pgv_set_text('vars', 'str2', 's102'); -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); -SELECT pgv_set_numeric('vars', 'num2', 1.02); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-03-30 10:00:00', true); -SELECT pgv_set_timestamp('vars', 'ts2', '2016-03-30 11:00:00'); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-03-30 10:00:00 GMT+01', true); -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-03-30 11:00:00 GMT+02'); -SELECT pgv_set_date('vars', 'd1', '2016-03-29', true); -SELECT pgv_set_date('vars', 'd2', '2016-03-30'); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz", "balance": 7.77, "active": false}'); -- Check values before rollback SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_int('vars', 'intNULL'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); -- Check values after rollback ROLLBACK; SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_int('vars', 'intNULL'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); - -- Record variables BEGIN; @@ -504,145 +303,93 @@ SELECT pgv_free(); BEGIN; SELECT pgv_set('vars', 'any1', 'text value'::text, true); SELECT pgv_set('vars', 'any2', 'text value'::text); -SELECT pgv_set_int('vars', 'int1', 401, true); -SELECT pgv_set_int('vars', 'int2', 402); -SELECT pgv_set_text('vars', 'str1', 's401', true); -SELECT pgv_set_text('vars', 'str2', 's402'); -SELECT pgv_set_numeric('vars', 'num1', 4.01, true); -SELECT pgv_set_numeric('vars', 'num2', 4.02); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-04-30 20:00:00', true); -SELECT pgv_set_timestamp('vars', 'ts2', '2016-04-30 21:00:00'); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-04-30 20:00:00 GMT+01', true); -SELECT pgv_set_timestamptz('vars', 'tstz2', '2016-04-30 21:00:00 GMT+02'); -SELECT pgv_set_date('vars', 'd1', '2016-04-29', true); -SELECT pgv_set_date('vars', 'd2', '2016-04-30'); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo4", null]', true); -SELECT pgv_set_jsonb('vars2', 'j2', '{"bar": "baz4", "balance": 4.44, "active": false}'); -SELECT pgv_insert('vars3', 'r1', row(6 :: integer, 'str44' :: varchar), true); -SELECT pgv_insert('vars3', 'r2', row(6 :: integer, 'str44' :: varchar)); +SELECT pgv_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); +SELECT pgv_insert('vars', 'r2', row(6 :: integer, 'str44' :: varchar)); ROLLBACK; SELECT pgv_get('vars', 'any1',NULL::text); SELECT pgv_get('vars', 'any2',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_int('vars', 'int2'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_text('vars', 'str2'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_numeric('vars', 'num2'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamp('vars', 'ts2'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_timestamptz('vars', 'tstz2'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_date('vars', 'd2'); -SELECT pgv_get_jsonb('vars2', 'j1'); -SELECT pgv_get_jsonb('vars2', 'j2'); -SELECT pgv_select('vars3', 'r1'); -SELECT pgv_select('vars3', 'r2'); +SELECT pgv_select('vars', 'r1'); +SELECT pgv_select('vars', 'r2'); -SELECT pgv_free(); +SELECT pgv_remove('vars'); -- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); -SELECT pgv_set_int('vars', 'int1', 100, true); -SELECT pgv_set_text('vars', 'str1', 's100', true); -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - BEGIN; SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); -SELECT pgv_set_int('vars', 'int1', 100, true); -SELECT pgv_set_text('vars', 'str1', 's100', true); -SELECT pgv_set_numeric('vars', 'num1', 1.00, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 10:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 10:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-01', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 0, "foo", null]', true); - SAVEPOINT sp1; SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); -SELECT pgv_set_int('vars', 'int1', 101, true); -SELECT pgv_set_text('vars', 'str1', 's101', true); -SELECT pgv_set_numeric('vars', 'num1', 1.01, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 11:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 11:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-11', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 1, "foo", null]', true); - SAVEPOINT sp2; SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); -SELECT pgv_set_int('vars', 'int1', 102, true); -SELECT pgv_set_text('vars', 'str1', 's102', true); -SELECT pgv_set_numeric('vars', 'num1', 1.02, true); -SELECT pgv_set_timestamp('vars', 'ts1', '2016-01-01 12:00:00', true); -SELECT pgv_set_timestamptz('vars', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); -SELECT pgv_set_date('vars', 'd1', '2016-01-21', true); -SELECT pgv_set_jsonb('vars', 'j1', '[1, 2, "foo", null]', true); - RELEASE sp2; SELECT pgv_get('vars', 'any1',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_jsonb('vars', 'j1'); - ROLLBACK TO sp1; SELECT pgv_get('vars', 'any1',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_jsonb('vars', 'j1'); - ROLLBACK; SELECT pgv_get('vars', 'any1',NULL::text); -SELECT pgv_get_int('vars', 'int1'); -SELECT pgv_get_text('vars', 'str1'); -SELECT pgv_get_numeric('vars', 'num1'); -SELECT pgv_get_timestamp('vars', 'ts1'); -SELECT pgv_get_timestamptz('vars', 'tstz1'); -SELECT pgv_get_date('vars', 'd1'); -SELECT pgv_get_jsonb('vars', 'j1'); - BEGIN; SAVEPOINT sp1; SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); -SELECT pgv_set_int('vars2', 'int1', 102, true); -SELECT pgv_set_text('vars2', 'str1', 's102', true); -SELECT pgv_set_numeric('vars2', 'num1', 1.02, true); -SELECT pgv_set_timestamp('vars2', 'ts1', '2016-01-01 12:00:00', true); -SELECT pgv_set_timestamptz('vars2', 'tstz1', '2016-01-01 12:00:00 GMT+01', true); -SELECT pgv_set_date('vars2', 'd1', '2016-01-21', true); -SELECT pgv_set_jsonb('vars2', 'j1', '[1, 2, "foo", null]', true); - RELEASE sp1; SELECT pgv_get('vars2', 'any1',NULL::text); -SELECT pgv_get_int('vars2', 'int1'); -SELECT pgv_get_text('vars2', 'str1'); -SELECT pgv_get_numeric('vars2', 'num1'); -SELECT pgv_get_timestamp('vars2', 'ts1'); -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -SELECT pgv_get_date('vars2', 'd1'); -SELECT pgv_get_jsonb('vars2', 'j1'); - ROLLBACK; SELECT pgv_get('vars2', 'any1',NULL::text); -SELECT pgv_get_int('vars2', 'int1'); -SELECT pgv_get_text('vars2', 'str1'); -SELECT pgv_get_numeric('vars2', 'num1'); -SELECT pgv_get_timestamp('vars2', 'ts1'); -SELECT pgv_get_timestamptz('vars2', 'tstz1'); -SELECT pgv_get_date('vars2', 'd1'); -SELECT pgv_get_jsonb('vars2', 'j1'); SELECT pgv_free(); + +--Additional tests +SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; +BEGIN; +SELECT pgv_insert('vars3', 'r1', row(5 :: integer, 'before savepoint sp1' :: varchar),true); +SAVEPOINT sp1; +SELECT pgv_update('vars3', 'r1', row(5 :: integer, 'after savepoint sp1' :: varchar)); +SAVEPOINT sp2; +SELECT pgv_insert('vars3', 'r1', row(7 :: integer, 'row after sp2 to remove in sp4' :: varchar),true); +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_delete('vars3', 'r1', 7); +SAVEPOINT sp5; +SELECT pgv_select('vars3', 'r1'); +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); +RELEASE sp2; +SELECT pgv_select('vars3', 'r1'); +ROLLBACK TO sp1; +SELECT pgv_select('vars3', 'r1'); +COMMIT; +SELECT pgv_select('vars3', 'r1'); + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); +ROLLBACK; +SELECT pgv_get('vars', 'any1',NULL::text); + +BEGIN; +SELECT pgv_set('vars', 'any1', 'wrong type'::varchar, true); +COMMIT; From 9126f9c0ef66e959945ad66f089d2534da79d7e6 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 23 Apr 2018 16:34:21 +0300 Subject: [PATCH 6/8] Fix removing icons from README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0f4e335..d017a32 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # pg_variables - session variables with various types +[![Build Status](https://travis-ci.org/postgrespro/pg_variables.svg?branch=master)](https://travis-ci.org/postgrespro/pg_variables) +[![codecov](https://codecov.io/gh/postgrespro/pg_variables/branch/master/graph/badge.svg)](https://codecov.io/gh/postgrespro/pg_variables) +[![GitHub license](https://img.shields.io/badge/license-PostgreSQL-blue.svg)](https://raw.githubusercontent.com/postgrespro/pg_variables/master/README.md) + ## Introduction The **pg_variables** module provides functions to work with variables of various From e7c12d94f76380c233bab202f92e426804f11767 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 23 Apr 2018 19:40:57 +0300 Subject: [PATCH 7/8] Fix macro --- pg_variables.c | 6 +++--- pg_variables.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 2fed54a..c9fad28 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -110,8 +110,8 @@ static dlist_head *changedVars = NULL; static MemoryContext changedVarsContext = NULL; static dlist_head *changedVarsStack = NULL; #define get_actual_changed_vars_list() \ - (dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ - changedVarsList + ((dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ + changedVarsList) /* @@ -981,7 +981,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = CreateTupleDescCopy( - *get_actual_value_record(variable).tupdesc); + (*get_actual_value_record(variable)).tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); diff --git a/pg_variables.h b/pg_variables.h index eb4ce63..fb67aa2 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -136,9 +136,9 @@ extern void insert_savepoint(HashVariableEntry *variable, /* Internal macros to manage with dlist structure */ #define get_actual_value_scalar(variable) \ - &((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.scalar) + (&((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.scalar)) #define get_actual_value_record(variable) \ - &((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.record) + (&((dlist_head_element(ValueHistoryEntry, node, &variable->data))->value.record)) #define get_history_entry(node_ptr) \ dlist_container(ValueHistoryEntry, node, node_ptr) From a3cefd3affbc08df4f13c1a7d9d5b9a9691ddafa Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 24 Apr 2018 10:49:37 +0300 Subject: [PATCH 8/8] Optimization of memory context proceedeng. Typos corrected --- expected/pg_variables_trans.out | 4 ++-- pg_variables.c | 24 ++++++++++-------------- pg_variables_record.c | 2 +- sql/pg_variables_trans.sql | 4 ++-- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 4b3109f..8d83c6a 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -979,7 +979,7 @@ SELECT pgv_free(); (1 row) --- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION BEGIN; SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); pgv_set @@ -1319,7 +1319,7 @@ SELECT pgv_remove('vars'); (1 row) --- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); pgv_set --------- diff --git a/pg_variables.c b/pg_variables.c index c9fad28..329f73e 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -127,7 +127,6 @@ variable_set(text *package_name, text *var_name, MemoryContext oldcxt; package = getPackageByName(package_name, true, false); - oldcxt = MemoryContextSwitchTo(package->hctx); variable = createVariableInternal(package, var_name, typid, is_transactional); @@ -138,10 +137,13 @@ variable_set(text *package_name, text *var_name, scalar->is_null = is_null; if (!scalar->is_null) + { + oldcxt = MemoryContextSwitchTo(package->hctx); scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); + MemoryContextSwitchTo(oldcxt); + } else scalar->value = 0; - MemoryContextSwitchTo(oldcxt); } static Datum @@ -604,12 +606,9 @@ variable_insert(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(package->hctx); variable = createVariableInternal(package, var_name, RECORDOID, is_transactional); LastVariable = variable; - MemoryContextSwitchTo(oldcxt); } else { @@ -981,7 +980,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = CreateTupleDescCopy( - (*get_actual_value_record(variable)).tupdesc); + get_actual_value_record(variable)->tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -1675,11 +1674,11 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, if (variable) { ValueHistoryEntry *historyEntry; - memset(&variable->data, 0, sizeof(variable->data)); variable->typid = typid; variable->is_transactional = is_transactional; dlist_init(&(variable->data)); - historyEntry = palloc0(sizeof(ValueHistoryEntry)); + historyEntry = MemoryContextAllocZero(package->hctx, + sizeof(ValueHistoryEntry)); dlist_push_head(&variable->data, &historyEntry->node); if (typid != RECORDOID) { @@ -1895,7 +1894,6 @@ popChangedVarsStack() static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { - MemoryContext oldcxt; ChangedVarsStackNode *cvsn; if (!changedVarsStack) { @@ -1911,12 +1909,10 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { ChangedVarsNode *cvn; cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - oldcxt = MemoryContextSwitchTo(cvsn->ctx); - cvn = palloc0(sizeof(ChangedVarsNode)); + cvn = MemoryContextAllocZero(cvsn->ctx, sizeof(ChangedVarsNode)); cvn->package = package; cvn->variable = variable; dlist_push_head(cvsn->changedVarsList, &cvn->node); - MemoryContextSwitchTo(oldcxt); } } @@ -1927,7 +1923,7 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) * upper level, it has savepoint there, so we need to release it. */ static void -lelevUpOrRelease() +levelUpOrRelease() { if (changedVarsStack) { @@ -2001,7 +1997,7 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, pushChangedVarsStack(); break; case SUBXACT_EVENT_COMMIT_SUB: - lelevUpOrRelease(); + levelUpOrRelease(); break; case SUBXACT_EVENT_ABORT_SUB: applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); diff --git a/pg_variables_record.c b/pg_variables_record.c index 3de8aa7..bd9fff1 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -361,7 +361,7 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) Assert(variable->typid == RECORDOID); - /* Create new hstory entry */ + /* Create new history entry */ record_prev = get_actual_value_record(variable); oldcxt = MemoryContextSwitchTo(packageContext); history_entry_new = palloc0(sizeof(ValueHistoryEntry)); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 976beef..7a7caeb 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -216,7 +216,7 @@ SELECT pgv_select('vars3', 'r2'); SELECT pgv_free(); --- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION BEGIN; SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); SAVEPOINT sp1; @@ -314,7 +314,7 @@ SELECT pgv_select('vars', 'r2'); SELECT pgv_remove('vars'); --- CHECK ROLLBACK AFTER COMMITING SUBTRANSACTION +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); BEGIN; SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true);