From 8829bc249562295bf212ce1021bf6a41da6fe0c7 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 25 Jan 2018 15:18:39 +0300 Subject: [PATCH 001/147] Fix building for 434e6e1484418c55561914600de9e180fc408378 --- pg_variables.c | 15 +++++++++++++++ pg_variables.h | 9 +++++++++ pg_variables_record.c | 18 ++++++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index ef3386a..975f826 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -1385,11 +1385,17 @@ ensurePackagesHashExists() if (packagesHash) return; +#if PG_VERSION_NUM >= 110000 + ModuleContext = AllocSetContextCreate(CacheMemoryContext, + "pg_variables memory context", + ALLOCSET_DEFAULT_SIZES); +#else ModuleContext = AllocSetContextCreate(CacheMemoryContext, "pg_variables memory context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); +#endif ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(HashPackageEntry); @@ -1441,11 +1447,20 @@ getPackageByName(text* name, bool create, bool strict) sprintf(hash_name, "Variables hash for package \"%s\"", key); +#if PG_VERSION_NUM >= 110000 + package->hctx = AllocSetContextCreateExtended(ModuleContext, + hash_name, 0, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#else package->hctx = AllocSetContextCreate(ModuleContext, hash_name, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); +#endif + oldcxt = MemoryContextSwitchTo(package->hctx); ctl.keysize = NAMEDATALEN; diff --git a/pg_variables.h b/pg_variables.h index 4b09fdb..abe69f8 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -10,6 +10,8 @@ #ifndef __PG_VARIABLES_H__ #define __PG_VARIABLES_H__ +#include "pg_config.h" + #include "access/htup.h" #include "access/tupdesc.h" #include "datatype/timestamp.h" @@ -18,6 +20,13 @@ #include "utils/numeric.h" #include "utils/jsonb.h" +/* Accessor for the i'th attribute of tupdesc. */ +#if PG_VERSION_NUM > 100000 +#define GetTupleDescAttr(tupdesc, i) (TupleDescAttr(tupdesc, i)) +#else +#define GetTupleDescAttr(tupdesc, i) ((tupdesc)->attrs[(i)]) +#endif + /* initial number of packages hashes */ #define NUMPACKAGES 8 #define NUMVARIABLES 16 diff --git a/pg_variables_record.c b/pg_variables_record.c index 5dcda5d..0c91b6d 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -79,11 +79,21 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, sprintf(hash_name, "Records hash for variable \"%s\"", variable->name); record = &(variable->value.record); + +#if PG_VERSION_NUM >= 110000 + record->hctx = AllocSetContextCreateExtended(topctx, + hash_name, 0, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#else record->hctx = AllocSetContextCreate(topctx, hash_name, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); +#endif + oldcxt = MemoryContextSwitchTo(record->hctx); record->tupdesc = CreateTupleDescCopyConstr(tupdesc); @@ -99,7 +109,7 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, HASH_FUNCTION | HASH_COMPARE); /* Get hash and match functions for key type. */ - keyid = record->tupdesc->attrs[0]->atttypid; + keyid = GetTupleDescAttr(record->tupdesc, 0)->atttypid; typentry = lookup_type_cache(keyid, TYPECACHE_HASH_PROC_FINFO | TYPECACHE_CMP_PROC_FINFO); @@ -142,8 +152,8 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) { - Form_pg_attribute attr1 = variable->value.record.tupdesc->attrs[i], - attr2 = tupdesc->attrs[i]; + Form_pg_attribute attr1 = GetTupleDescAttr(variable->value.record.tupdesc, i), + attr2 = GetTupleDescAttr(tupdesc, i); if ((attr1->atttypid != attr2->atttypid) || (attr1->attndims != attr2->attndims) @@ -163,7 +173,7 @@ check_record_key(HashVariableEntry *variable, Oid typid) { Assert(variable->typid == RECORDOID); - if (variable->value.record.tupdesc->attrs[0]->atttypid != typid) + if (GetTupleDescAttr(variable->value.record.tupdesc, 0)->atttypid != typid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested value type differs from variable \"%s\" " 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 002/147] 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 1f8601587ebbdd4820ea83bc7b091e4b998b97b9 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 16 Apr 2018 18:19:59 +0300 Subject: [PATCH 003/147] basic integration with Travis CI --- .dockerignore | 4 ++++ .gitignore | 2 ++ .travis.yml | 23 ++++++++++++++++++++ Dockerfile.tmpl | 26 +++++++++++++++++++++++ docker-compose.yml | 2 ++ mk_dockerfile.sh | 2 ++ run_tests.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 112 insertions(+) create mode 100644 .dockerignore create mode 100644 .travis.yml create mode 100644 Dockerfile.tmpl create mode 100644 docker-compose.yml create mode 100755 mk_dockerfile.sh create mode 100755 run_tests.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0642fd8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +*.gcno +*.gcda +*.gcov +*.so diff --git a/.gitignore b/.gitignore index cbf8d79..965c36b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ lib*.pc /Debug/ /Release/ /tmp_install/ + +Dockerfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..78cd07d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +sudo: required + +language: c + +services: + - docker + +install: + - ./mk_dockerfile.sh + - docker-compose build + +script: + - docker-compose run $(bash <(curl -s https://codecov.io/env)) tests + +notifications: + email: + on_success: change + on_failure: always + +env: + - PG_VERSION=10 + - PG_VERSION=9.6 + - PG_VERSION=9.5 diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl new file mode 100644 index 0000000..4b47679 --- /dev/null +++ b/Dockerfile.tmpl @@ -0,0 +1,26 @@ +FROM postgres:${PG_VERSION}-alpine + +# Install dependencies +RUN apk add --no-cache clang-analyzer clang perl make musl-dev gcc curl; + +# Environment +ENV LANG=C.UTF-8 PGDATA=/pg/data + +# Make directories +RUN mkdir -p ${PGDATA} && \ + mkdir -p /pg/testdir + +# Grant privileges +RUN chown postgres:postgres ${PGDATA} && \ + chown postgres:postgres /pg/testdir && \ + chmod a+rwx /usr/local/lib/postgresql && \ + chmod a+rwx /usr/local/share/postgresql/extension + +COPY run_tests.sh /run.sh +RUN chmod 755 /run.sh + +ADD . /pg/testdir +WORKDIR /pg/testdir + +USER postgres +ENTRYPOINT /run.sh diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..471ab77 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,2 @@ +tests: + build: . diff --git a/mk_dockerfile.sh b/mk_dockerfile.sh new file mode 100755 index 0000000..ae1473a --- /dev/null +++ b/mk_dockerfile.sh @@ -0,0 +1,2 @@ +set -eu +sed -e 's/${PG_VERSION}/'${PG_VERSION}/g Dockerfile.tmpl > Dockerfile diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..411d913 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright (c) 2018, Postgres Professional + + +set -ux + + +status=0 + +# perform static analyzis +scan-build --status-bugs make USE_PGXS=1 || status=$? + +# something's wrong, exit now! +if [ $status -ne 0 ]; then exit 1; fi + +# don't forget to "make clean" +make USE_PGXS=1 clean + +# initialize database +initdb -D $PGDATA + +# build and install extension (using PG_CPPFLAGS and SHLIB_LINK for gcov) +make USE_PGXS=1 PG_CPPFLAGS="-coverage" SHLIB_LINK="-coverage" +make USE_PGXS=1 install + +# restart cluster 'test' +echo "port = 55435" >> $PGDATA/postgresql.conf +pg_ctl start -l /tmp/postgres.log -w || status=$? + +# something's wrong, exit now! +if [ $status -ne 0 ]; then cat /tmp/postgres.log; exit 1; fi + +# run regression tests +export PG_REGRESS_DIFF_OPTS="-w -U3" # for alpine's diff (BusyBox) +PGPORT=55435 make USE_PGXS=1 installcheck || status=$? + +# show diff if it exists +if test -f regression.diffs; then cat regression.diffs; fi + +# something's wrong, exit now! +if [ $status -ne 0 ]; then exit 1; fi + +# generate *.gcov files +rm -f *serialize.{gcda,gcno} +gcov *.c *.h + + +set +ux + + +# send coverage stats to Codecov +bash <(curl -s https://codecov.io/bash) From dcf37f1aba0be52be3afe933f1c513a18bccd94b Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 16 Apr 2018 18:36:21 +0300 Subject: [PATCH 004/147] add Travis CI badge to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ee9d34a..a9d0d61 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 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) + ## Introduction The **pg_variables** module provides functions to work with variables of various From f8f462c7cd9f2a41d2ef899c1e8de81cd0d6360b Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 16 Apr 2018 18:44:37 +0300 Subject: [PATCH 005/147] more badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a9d0d61..39a008d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # 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 From e38097af99d717a6401c15c47e662527e6d5625a Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 16 Apr 2018 18:53:12 +0300 Subject: [PATCH 006/147] show pg_config in tests --- run_tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/run_tests.sh b/run_tests.sh index 411d913..27a89f8 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -8,6 +8,9 @@ set -ux status=0 +# show pg_config just in case +pg_config + # perform static analyzis scan-build --status-bugs make USE_PGXS=1 || status=$? From 493aa8df1c4ce008ba499116f8e609d960cbb131 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Tue, 17 Apr 2018 16:00:19 +0300 Subject: [PATCH 007/147] implement several levels in run_tests.sh --- .travis.yml | 1 + Dockerfile.tmpl | 9 ++++-- mk_dockerfile.sh | 2 +- run_tests.sh | 73 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 78cd07d..3243caf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ notifications: on_failure: always env: + - PG_VERSION=10 LEVEL=hardcore - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 4b47679..a57790a 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -1,7 +1,12 @@ FROM postgres:${PG_VERSION}-alpine # Install dependencies -RUN apk add --no-cache clang-analyzer clang perl make musl-dev gcc curl; +RUN apk add --no-cache \ + curl git \ + make musl-dev gcc bison flex coreutils \ + perl perl-utils perl-ipc-run \ + zlib-dev libedit-dev \ + clang-analyzer valgrind; # Environment ENV LANG=C.UTF-8 PGDATA=/pg/data @@ -23,4 +28,4 @@ ADD . /pg/testdir WORKDIR /pg/testdir USER postgres -ENTRYPOINT /run.sh +ENTRYPOINT LEVEL=${LEVEL} /run.sh diff --git a/mk_dockerfile.sh b/mk_dockerfile.sh index ae1473a..0e0e2fe 100755 --- a/mk_dockerfile.sh +++ b/mk_dockerfile.sh @@ -1,2 +1,2 @@ set -eu -sed -e 's/${PG_VERSION}/'${PG_VERSION}/g Dockerfile.tmpl > Dockerfile +sed -e 's/${PG_VERSION}/'${PG_VERSION}/g -e 's/${LEVEL}/'${LEVEL}/g Dockerfile.tmpl > Dockerfile diff --git a/run_tests.sh b/run_tests.sh index 27a89f8..c866710 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -3,30 +3,85 @@ # Copyright (c) 2018, Postgres Professional -set -ux +# provide a decent default level +if [ -z ${LEVEL+x} ]; then + LEVEL=scan-build +fi +set -ux status=0 + # show pg_config just in case pg_config -# perform static analyzis -scan-build --status-bugs make USE_PGXS=1 || status=$? -# something's wrong, exit now! -if [ $status -ne 0 ]; then exit 1; fi +# perform code checks if asked to +if [ "$LEVEL" = "scan-build" ]; then -# don't forget to "make clean" -make USE_PGXS=1 clean + # perform static analyzis + scan-build --status-bugs make USE_PGXS=1 || status=$? -# initialize database -initdb -D $PGDATA + # something's wrong, exit now! + if [ $status -ne 0 ]; then exit 1; fi + + # don't forget to "make clean" + make USE_PGXS=1 clean +fi + +# build with cassert + valgrind support +if [ "$LEVEL" = "hardcore" ]; then + + set -e + + CUSTOM_PG_PATH=$PWD/pg_bin + + # here PG_VERSION is provided by postgres:X-alpine docker image + wget -O postgresql.tar.bz2 "https://ftp.postgresql.org/pub/source/v$PG_VERSION/postgresql-$PG_VERSION.tar.bz2" + echo "$PG_SHA256 *postgresql.tar.bz2" | sha256sum -c - + + mkdir postgresql + + tar \ + --extract \ + --file postgresql.tar.bz2 \ + --directory postgresql \ + --strip-components 1 + + cd postgresql + + # enable Valgrind support + sed -i.bak "s/\/* #define USE_VALGRIND *\//#define USE_VALGRIND/g" src/include/pg_config_manual.h + + # enable additional options + eval ./configure \ + --with-gnu-ld \ + --enable-debug \ + --enable-cassert \ + --prefix=$CUSTOM_PG_PATH + + # TODO: -j$(nproc) + make -s -j1 && make install + + # override default PostgreSQL instance + export PATH=$CUSTOM_PG_PATH/bin:$PATH + + # show pg_config path (just in case) + which pg_config + + cd - + + set +e +fi # build and install extension (using PG_CPPFLAGS and SHLIB_LINK for gcov) make USE_PGXS=1 PG_CPPFLAGS="-coverage" SHLIB_LINK="-coverage" make USE_PGXS=1 install +# initialize database +initdb -D $PGDATA + # restart cluster 'test' echo "port = 55435" >> $PGDATA/postgresql.conf pg_ctl start -l /tmp/postgres.log -w || status=$? From 9ba3a4fb2b39fc9363e1b4b5b629f1fefb876ba8 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Wed, 18 Apr 2018 13:36:04 +0300 Subject: [PATCH 008/147] add support for valgrind --- mk_dockerfile.sh | 18 +++++++++++-- run_tests.sh | 70 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/mk_dockerfile.sh b/mk_dockerfile.sh index 0e0e2fe..f15433c 100755 --- a/mk_dockerfile.sh +++ b/mk_dockerfile.sh @@ -1,2 +1,16 @@ -set -eu -sed -e 's/${PG_VERSION}/'${PG_VERSION}/g -e 's/${LEVEL}/'${LEVEL}/g Dockerfile.tmpl > Dockerfile +if [ -z ${PG_VERSION+x} ]; then + echo PG_VERSION is not set! + exit 1 +fi + +if [ -z ${LEVEL+x} ]; then + LEVEL=scan-build +fi + +echo PG_VERSION=${PG_VERSION} +echo LEVEL=${LEVEL} + +sed \ + -e 's/${PG_VERSION}/'${PG_VERSION}/g \ + -e 's/${LEVEL}/'${LEVEL}/g \ + Dockerfile.tmpl > Dockerfile diff --git a/run_tests.sh b/run_tests.sh index c866710..e6cb4cd 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,15 +1,16 @@ #!/usr/bin/env bash +# # Copyright (c) 2018, Postgres Professional - - -# provide a decent default level -if [ -z ${LEVEL+x} ]; then - LEVEL=scan-build -fi +# +# supported levels: +# * standard +# * scan-build +# * hardcore +# * nightmare +# set -ux - status=0 @@ -31,41 +32,41 @@ if [ "$LEVEL" = "scan-build" ]; then fi # build with cassert + valgrind support -if [ "$LEVEL" = "hardcore" ]; then +if [ "$LEVEL" = "hardcore" ] || [ "$LEVEL" = "nightmare" ]; then set -e - CUSTOM_PG_PATH=$PWD/pg_bin + CUSTOM_PG_BIN=$PWD/pg_bin + CUSTOM_PG_SRC=$PWD/postgresql # here PG_VERSION is provided by postgres:X-alpine docker image wget -O postgresql.tar.bz2 "https://ftp.postgresql.org/pub/source/v$PG_VERSION/postgresql-$PG_VERSION.tar.bz2" echo "$PG_SHA256 *postgresql.tar.bz2" | sha256sum -c - - mkdir postgresql + mkdir $CUSTOM_PG_SRC tar \ --extract \ --file postgresql.tar.bz2 \ - --directory postgresql \ + --directory $CUSTOM_PG_SRC \ --strip-components 1 - cd postgresql + cd $CUSTOM_PG_SRC # enable Valgrind support sed -i.bak "s/\/* #define USE_VALGRIND *\//#define USE_VALGRIND/g" src/include/pg_config_manual.h # enable additional options - eval ./configure \ - --with-gnu-ld \ + ./configure \ --enable-debug \ --enable-cassert \ - --prefix=$CUSTOM_PG_PATH + --prefix=$CUSTOM_PG_BIN - # TODO: -j$(nproc) - make -s -j1 && make install + make -s -j$(nproc) && make -s install # override default PostgreSQL instance - export PATH=$CUSTOM_PG_PATH/bin:$PATH + export PATH=$CUSTOM_PG_BIN/bin:$PATH + export LD_LIBRARY_PATH=$CUSTOM_PG_BIN/lib # show pg_config path (just in case) which pg_config @@ -82,16 +83,43 @@ make USE_PGXS=1 install # initialize database initdb -D $PGDATA +# set appropriate port +export PGPORT=55435 +echo "port = $PGPORT" >> $PGDATA/postgresql.conf + # restart cluster 'test' -echo "port = 55435" >> $PGDATA/postgresql.conf -pg_ctl start -l /tmp/postgres.log -w || status=$? +if [ "$LEVEL" = "nightmare" ]; then + ls $CUSTOM_PG_BIN/bin + + valgrind \ + --leak-check=no \ + --time-stamp=yes \ + --trace-children=yes \ + --gen-suppressions=all \ + --suppressions=$CUSTOM_PG_SRC/src/tools/valgrind.supp \ + --log-file=/tmp/valgrind-%p.log \ + postgres -l /tmp/postgres.log -w || status=$? +else + pg_ctl start -l /tmp/postgres.log -w || status=$? +fi # something's wrong, exit now! if [ $status -ne 0 ]; then cat /tmp/postgres.log; exit 1; fi # run regression tests export PG_REGRESS_DIFF_OPTS="-w -U3" # for alpine's diff (BusyBox) -PGPORT=55435 make USE_PGXS=1 installcheck || status=$? +make USE_PGXS=1 installcheck || status=$? + +# show Valgrind logs if necessary +if [ "$LEVEL" = "nightmare" ]; then + for f in $(find /tmp -name valgrind-*.log); do + if grep -q 'Command: [^ ]*/postgres' $f && grep -q 'ERROR SUMMARY: [1-9]' $f; then + echo "========= Contents of $f" + cat $f + status=1 + fi + done +fi # show diff if it exists if test -f regression.diffs; then cat regression.diffs; fi From af6e719158fd43ce18a09d568158d377ca540b08 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Wed, 18 Apr 2018 14:14:07 +0300 Subject: [PATCH 009/147] remove some deps --- Dockerfile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index a57790a..479ea7d 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -3,8 +3,8 @@ FROM postgres:${PG_VERSION}-alpine # Install dependencies RUN apk add --no-cache \ curl git \ + perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ - perl perl-utils perl-ipc-run \ zlib-dev libedit-dev \ clang-analyzer valgrind; From c3124ed12d59afc42219fb89017d9e1a83f41d28 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Wed, 18 Apr 2018 14:22:34 +0300 Subject: [PATCH 010/147] refactoring, add missing deps --- Dockerfile.tmpl | 2 +- run_tests.sh | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 479ea7d..48ccebb 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -6,7 +6,7 @@ RUN apk add --no-cache \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ zlib-dev libedit-dev \ - clang-analyzer valgrind; + clang clang-analyzer valgrind; # Environment ENV LANG=C.UTF-8 PGDATA=/pg/data diff --git a/run_tests.sh b/run_tests.sh index e6cb4cd..cbb0b75 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -18,21 +18,9 @@ status=0 pg_config -# perform code checks if asked to -if [ "$LEVEL" = "scan-build" ]; then - - # perform static analyzis - scan-build --status-bugs make USE_PGXS=1 || status=$? - - # something's wrong, exit now! - if [ $status -ne 0 ]; then exit 1; fi - - # don't forget to "make clean" - make USE_PGXS=1 clean -fi - -# build with cassert + valgrind support -if [ "$LEVEL" = "hardcore" ] || [ "$LEVEL" = "nightmare" ]; then +# rebuild PostgreSQL with cassert + valgrind support +if [ "$LEVEL" = "hardcore" ] || \ + [ "$LEVEL" = "nightmare" ]; then set -e @@ -76,6 +64,22 @@ if [ "$LEVEL" = "hardcore" ] || [ "$LEVEL" = "nightmare" ]; then set +e fi +# perform code checks if asked to +if [ "$LEVEL" = "scan-build" ] || \ + [ "$LEVEL" = "hardcore" ] || \ + [ "$LEVEL" = "nightmare" ]; then + + # perform static analyzis + scan-build --status-bugs make USE_PGXS=1 || status=$? + + # something's wrong, exit now! + if [ $status -ne 0 ]; then exit 1; fi + + # don't forget to "make clean" + make USE_PGXS=1 clean +fi + + # build and install extension (using PG_CPPFLAGS and SHLIB_LINK for gcov) make USE_PGXS=1 PG_CPPFLAGS="-coverage" SHLIB_LINK="-coverage" make USE_PGXS=1 install From 45948058d0f29ee4298355901e89110c3f5385e4 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Wed, 18 Apr 2018 14:45:12 +0300 Subject: [PATCH 011/147] show build time (hardcore mode) --- run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index cbb0b75..cc04cee 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -50,7 +50,7 @@ if [ "$LEVEL" = "hardcore" ] || \ --enable-cassert \ --prefix=$CUSTOM_PG_BIN - make -s -j$(nproc) && make -s install + time make -s -j$(nproc) && make -s install # override default PostgreSQL instance export PATH=$CUSTOM_PG_BIN/bin:$PATH 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 012/147] 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 67e897068b864ca7b082786e6d2d8e408c1a6586 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Fri, 20 Apr 2018 13:45:00 +0300 Subject: [PATCH 013/147] replace wget with curl --- Dockerfile.tmpl | 2 +- run_tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 48ccebb..0d13eda 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -2,7 +2,7 @@ FROM postgres:${PG_VERSION}-alpine # Install dependencies RUN apk add --no-cache \ - curl git \ + openssl curl \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ zlib-dev libedit-dev \ diff --git a/run_tests.sh b/run_tests.sh index cbb0b75..d3f55c0 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -28,7 +28,7 @@ if [ "$LEVEL" = "hardcore" ] || \ CUSTOM_PG_SRC=$PWD/postgresql # here PG_VERSION is provided by postgres:X-alpine docker image - wget -O postgresql.tar.bz2 "https://ftp.postgresql.org/pub/source/v$PG_VERSION/postgresql-$PG_VERSION.tar.bz2" + curl "https://ftp.postgresql.org/pub/source/v$PG_VERSION/postgresql-$PG_VERSION.tar.bz2" -o postgresql.tar.bz2 echo "$PG_SHA256 *postgresql.tar.bz2" | sha256sum -c - mkdir $CUSTOM_PG_SRC 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 014/147] 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 015/147] 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 016/147] 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 017/147] 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 25ecc76124dca188cf046830f9d11af565ac77d5 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 23 Apr 2018 18:16:15 +0300 Subject: [PATCH 018/147] run builds under Valgrind --- .travis.yml | 5 +++++ Dockerfile.tmpl | 7 ++++++- run_tests.sh | 18 +++++++++--------- valgrind.supp | 0 4 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 valgrind.supp diff --git a/.travis.yml b/.travis.yml index 3243caf..6d40aa6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,12 @@ notifications: on_failure: always env: + - PG_VERSION=10 LEVEL=nightmare - PG_VERSION=10 LEVEL=hardcore - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 + +matrix: + allow_failures: + - env: PG_VERSION=10 LEVEL=nightmare diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 0d13eda..1760377 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -6,7 +6,12 @@ RUN apk add --no-cache \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ zlib-dev libedit-dev \ - clang clang-analyzer valgrind; + clang clang-analyzer; + +# Install fresh valgrind +RUN apk add valgrind \ + --update-cache \ + --repository http://dl-3.alpinelinux.org/alpine/edge/main; # Environment ENV LANG=C.UTF-8 PGDATA=/pg/data diff --git a/run_tests.sh b/run_tests.sh index 8132856..4bf8a09 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -14,10 +14,6 @@ set -ux status=0 -# show pg_config just in case -pg_config - - # rebuild PostgreSQL with cassert + valgrind support if [ "$LEVEL" = "hardcore" ] || \ [ "$LEVEL" = "nightmare" ]; then @@ -46,7 +42,7 @@ if [ "$LEVEL" = "hardcore" ] || \ # enable additional options ./configure \ - --enable-debug \ + CFLAGS='-O0 -ggdb3 -fno-omit-frame-pointer' \ --enable-cassert \ --prefix=$CUSTOM_PG_BIN @@ -64,6 +60,9 @@ if [ "$LEVEL" = "hardcore" ] || \ set +e fi +# show pg_config just in case +pg_config + # perform code checks if asked to if [ "$LEVEL" = "scan-build" ] || \ [ "$LEVEL" = "hardcore" ] || \ @@ -81,8 +80,7 @@ fi # build and install extension (using PG_CPPFLAGS and SHLIB_LINK for gcov) -make USE_PGXS=1 PG_CPPFLAGS="-coverage" SHLIB_LINK="-coverage" -make USE_PGXS=1 install +make USE_PGXS=1 PG_CPPFLAGS="-coverage" SHLIB_LINK="-coverage" install # initialize database initdb -D $PGDATA @@ -96,13 +94,16 @@ if [ "$LEVEL" = "nightmare" ]; then ls $CUSTOM_PG_BIN/bin valgrind \ + --tool=memcheck \ --leak-check=no \ --time-stamp=yes \ + --track-origins=yes \ --trace-children=yes \ --gen-suppressions=all \ --suppressions=$CUSTOM_PG_SRC/src/tools/valgrind.supp \ + --suppressions=$PWD/valgrind.supp \ --log-file=/tmp/valgrind-%p.log \ - postgres -l /tmp/postgres.log -w || status=$? + pg_ctl start -l /tmp/postgres.log -w || status=$? else pg_ctl start -l /tmp/postgres.log -w || status=$? fi @@ -132,7 +133,6 @@ if test -f regression.diffs; then cat regression.diffs; fi if [ $status -ne 0 ]; then exit 1; fi # generate *.gcov files -rm -f *serialize.{gcda,gcno} gcov *.c *.h diff --git a/valgrind.supp b/valgrind.supp new file mode 100644 index 0000000..e69de29 From eb1d7f84c06199e81735d979aeb8a0ff89374a8f Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Mon, 23 Apr 2018 18:21:22 +0300 Subject: [PATCH 019/147] use ./configure ... --quiet --- run_tests.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 4bf8a09..2574328 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -44,7 +44,8 @@ if [ "$LEVEL" = "hardcore" ] || \ ./configure \ CFLAGS='-O0 -ggdb3 -fno-omit-frame-pointer' \ --enable-cassert \ - --prefix=$CUSTOM_PG_BIN + --prefix=$CUSTOM_PG_BIN \ + --quiet time make -s -j$(nproc) && make -s install 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 020/147] 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 021/147] 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); From f807d8fff46dcabe24acb657e0abc33bb503df69 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 27 Apr 2018 19:50:03 +0300 Subject: [PATCH 022/147] Fix code indentation --- pg_variables.c | 431 ++++++++++++++++++++++-------------------- pg_variables.h | 56 +++--- pg_variables_record.c | 97 +++++----- 3 files changed, 307 insertions(+), 277 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 329f73e..349ea1a 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -70,17 +70,14 @@ static void getKeyFromName(text *name, char *key); static void ensurePackagesHashExists(); static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); -static HashVariableEntry * -getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict); -static HashVariableEntry * -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); +static HashVariableEntry *getVariableInternal(HTAB *variables, text *name, + Oid typid, bool strict); +static HashVariableEntry *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 { \ @@ -111,7 +108,7 @@ static MemoryContext changedVarsContext = NULL; static dlist_head *changedVarsStack = NULL; #define get_actual_changed_vars_list() \ ((dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ - changedVarsList) + changedVarsList) /* @@ -121,14 +118,14 @@ static void variable_set(text *package_name, text *var_name, Oid typid, Datum value, bool is_null, bool is_transactional) { - HashPackageEntry *package; - HashVariableEntry *variable; - ScalarVar *scalar; - MemoryContext oldcxt; + HashPackageEntry *package; + HashVariableEntry *variable; + ScalarVar *scalar; + MemoryContext oldcxt; package = getPackageByName(package_name, true, false); variable = createVariableInternal(package, var_name, typid, - is_transactional); + is_transactional); scalar = get_actual_value_scalar(variable); /* Release memory for variable */ @@ -150,9 +147,9 @@ static Datum variable_get(text *package_name, text *var_name, Oid typid, bool *is_null, bool strict) { - HashPackageEntry *package; - HashVariableEntry *variable; - ScalarVar *scalar; + HashPackageEntry *package; + HashVariableEntry *variable; + ScalarVar *scalar; package = getPackageByName(package_name, false, strict); if (package == NULL) @@ -162,7 +159,7 @@ variable_get(text *package_name, text *var_name, } variable = getVariableInternal(package->variablesHash, - var_name, typid, strict); + var_name, typid, strict); if (variable == NULL) { @@ -177,9 +174,9 @@ variable_get(text *package_name, text *var_name, Datum variable_set_any(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - bool is_transactional; + text *package_name; + text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); @@ -226,9 +223,9 @@ variable_get_any(PG_FUNCTION_ARGS) Datum variable_set_int(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - bool is_transactional; + text *package_name; + text *var_name; + bool is_transactional; CHECK_ARGS_FOR_NULL(); @@ -562,12 +559,12 @@ variable_get_jsonb(PG_FUNCTION_ARGS) Datum variable_insert(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - HeapTupleHeader rec; - HashPackageEntry *package; - HashVariableEntry *variable; - bool is_transactional; + text *package_name; + text *var_name; + HeapTupleHeader rec; + HashPackageEntry *package; + HashVariableEntry *variable; + bool is_transactional; Oid tupType; int32 tupTypmod; @@ -616,13 +613,13 @@ variable_insert(PG_FUNCTION_ARGS) variable = LastVariable; else { - char key[NAMEDATALEN]; + 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 "))); + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + key, LastVariable->is_transactional ? "" : "NOT "))); } if (!isVarChangedInTrans(variable) && variable->is_transactional) { @@ -660,12 +657,12 @@ variable_insert(PG_FUNCTION_ARGS) Datum variable_update(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - HeapTupleHeader rec; - HashPackageEntry *package; - HashVariableEntry *variable; - bool res; + text *package_name; + text *var_name; + HeapTupleHeader rec; + HashPackageEntry *package; + HashVariableEntry *variable; + bool res; Oid tupType; int32 tupTypmod; @@ -704,7 +701,7 @@ variable_update(PG_FUNCTION_ARGS) VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + var_name, RECORDOID, true); LastVariable = variable; } else @@ -736,14 +733,14 @@ variable_update(PG_FUNCTION_ARGS) Datum variable_delete(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - Oid value_type; - Datum value; - bool value_is_null = PG_ARGISNULL(2); - HashPackageEntry *package; - HashVariableEntry *variable; - bool res; + text *package_name; + text *var_name; + Oid value_type; + Datum value; + bool value_is_null = PG_ARGISNULL(2); + HashPackageEntry *package; + HashVariableEntry *variable; + bool res; CHECK_ARGS_FOR_NULL(); @@ -782,11 +779,12 @@ variable_delete(PG_FUNCTION_ARGS) VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + var_name, RECORDOID, true); LastVariable = variable; } else variable = LastVariable; + if (variable->is_transactional && !isVarChangedInTrans(variable)) { createSavepoint(package, variable); @@ -808,18 +806,18 @@ variable_delete(PG_FUNCTION_ARGS) Datum variable_select(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - HASH_SEQ_STATUS *rstat; - HashRecordEntry *item; + FuncCallContext *funcctx; + HASH_SEQ_STATUS *rstat; + HashRecordEntry *item; if (SRF_IS_FIRSTCALL()) { - text *package_name; - text *var_name; - HashPackageEntry *package; - HashVariableEntry *variable; - MemoryContext oldcontext; - RecordVar *record; + text *package_name; + text *var_name; + HashPackageEntry *package; + HashVariableEntry *variable; + MemoryContext oldcontext; + RecordVar *record; CHECK_ARGS_FOR_NULL(); @@ -829,7 +827,7 @@ variable_select(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + var_name, RECORDOID, true); record = get_actual_value_record(variable); @@ -870,18 +868,18 @@ variable_select(PG_FUNCTION_ARGS) Datum variable_select_by_value(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - Oid value_type; - Datum value; - bool value_is_null = PG_ARGISNULL(2); - HashPackageEntry *package; - HashVariableEntry *variable; - - HashRecordEntry *item; - RecordVar *record; - HashRecordKey k; - bool found; + text *package_name; + text *var_name; + Oid value_type; + Datum value; + bool value_is_null = PG_ARGISNULL(2); + HashPackageEntry *package; + HashVariableEntry *variable; + + HashRecordEntry *item; + RecordVar *record; + HashRecordKey k; + bool found; CHECK_ARGS_FOR_NULL(); @@ -902,7 +900,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + var_name, RECORDOID, true); if (!value_is_null) check_record_key(variable, value_type); @@ -916,7 +914,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) k.cmp_proc = &record->cmp_proc; item = (HashRecordEntry *) hash_search(record->rhash, &k, - HASH_FIND, &found); + HASH_FIND, &found); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -930,27 +928,27 @@ variable_select_by_value(PG_FUNCTION_ARGS) /* Structure for variable_select_by_values() */ typedef struct { - HashVariableEntry *variable; - ArrayIterator iterator; + HashVariableEntry *variable; + ArrayIterator iterator; } VariableIteratorRec; Datum variable_select_by_values(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - VariableIteratorRec *var; - Datum value; - HashRecordEntry *item; - bool isnull; + FuncCallContext *funcctx; + VariableIteratorRec *var; + Datum value; + HashRecordEntry *item; + bool isnull; if (SRF_IS_FIRSTCALL()) { - text *package_name; - text *var_name; - ArrayType *values; - HashPackageEntry *package; - HashVariableEntry *variable; - MemoryContext oldcontext; + text *package_name; + text *var_name; + ArrayType *values; + HashPackageEntry *package; + HashVariableEntry *variable; + MemoryContext oldcontext; /* Checks */ CHECK_ARGS_FOR_NULL(); @@ -958,7 +956,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) if (PG_ARGISNULL(2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("array argument can not be NULL"))); + errmsg("array argument can not be NULL"))); values = PG_GETARG_ARRAYTYPE_P(2); if (ARR_NDIM(values) > 1) @@ -972,7 +970,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + var_name, RECORDOID, true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -998,9 +996,9 @@ variable_select_by_values(PG_FUNCTION_ARGS) /* Get next array element */ while (array_iterate(var->iterator, &value, &isnull)) { - HashRecordKey k; - bool found; - RecordVar *record; + HashRecordKey k; + bool found; + RecordVar *record; record = get_actual_value_record(var->variable); /* Search a record */ @@ -1010,7 +1008,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; @@ -1032,16 +1030,19 @@ variable_select_by_values(PG_FUNCTION_ARGS) static void cleanVariableCurrentState(HashVariableEntry *variable) { - ValueHistory *history; - ValueHistoryEntry *historyEntryToDelete; + ValueHistory *history; + ValueHistoryEntry *historyEntryToDelete; + if (variable->typid == RECORDOID) clean_records(variable); else { - ScalarVar *scalar = get_actual_value_scalar(variable); + 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); @@ -1068,9 +1069,9 @@ variable_exists(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; - char key[NAMEDATALEN]; - bool found; + HashPackageEntry *package; + char key[NAMEDATALEN]; + bool found; CHECK_ARGS_FOR_NULL(); @@ -1126,10 +1127,10 @@ remove_variable(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; - HashVariableEntry *variable; - bool found; - char key[NAMEDATALEN]; + HashPackageEntry *package; + HashVariableEntry *variable; + bool found; + char key[NAMEDATALEN]; CHECK_ARGS_FOR_NULL(); @@ -1164,9 +1165,9 @@ Datum remove_package(PG_FUNCTION_ARGS) { text *package_name; - HashPackageEntry *package; - bool found; - char key[NAMEDATALEN]; + HashPackageEntry *package; + bool found; + char key[NAMEDATALEN]; if (PG_ARGISNULL(0)) ereport(ERROR, @@ -1217,9 +1218,9 @@ remove_packages(PG_FUNCTION_ARGS) /* All packages and variables will be freed */ MemoryContextDelete(ModuleContext); - packagesHash = NULL; + packagesHash = NULL; ModuleContext = NULL; - changedVars = NULL; + changedVars = NULL; PG_RETURN_VOID(); } @@ -1229,9 +1230,9 @@ remove_packages(PG_FUNCTION_ARGS) */ typedef struct { - char *package; - char *variable; - bool is_transactional; + char *package; + char *variable; + bool is_transactional; } VariableRec; /* @@ -1240,13 +1241,13 @@ typedef struct Datum get_packages_and_variables(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - VariableRec *recs; - MemoryContext oldcontext; + FuncCallContext *funcctx; + VariableRec *recs; + MemoryContext oldcontext; if (SRF_IS_FIRSTCALL()) { - TupleDesc tupdesc; + TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1266,10 +1267,10 @@ get_packages_and_variables(PG_FUNCTION_ARGS) */ if (packagesHash) { - HashPackageEntry *package; - HASH_SEQ_STATUS pstat; - int mRecs = NUMVARIABLES, - nRecs = 0; + HashPackageEntry *package; + HASH_SEQ_STATUS pstat; + int mRecs = NUMVARIABLES, + nRecs = 0; recs = (VariableRec *) palloc0(sizeof(VariableRec) * mRecs); @@ -1278,8 +1279,8 @@ get_packages_and_variables(PG_FUNCTION_ARGS) while ((package = (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) { - HashVariableEntry *variable; - HASH_SEQ_STATUS vstat; + HashVariableEntry *variable; + HASH_SEQ_STATUS vstat; /* Get variables list for package */ hash_seq_init(&vstat, package->variablesHash); @@ -1353,11 +1354,11 @@ getMemoryTotalSpace(MemoryContext context, int level, Size *totalspace) /* Examine the context itself */ memset(&totals, 0, sizeof(totals)); -# if PG_VERSION_NUM >= 110000 +#if PG_VERSION_NUM >= 110000 (*context->methods->stats) (context, NULL, NULL, &totals); -# else +#else (*context->methods->stats) (context, level, false, &totals); -# endif +#endif *totalspace += totals.totalspace; /* @@ -1376,14 +1377,14 @@ getMemoryTotalSpace(MemoryContext context, int level, Size *totalspace) Datum get_packages_stats(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - MemoryContext oldcontext; - HASH_SEQ_STATUS *pstat; - HashPackageEntry *package; + FuncCallContext *funcctx; + MemoryContext oldcontext; + HASH_SEQ_STATUS *pstat; + HashPackageEntry *package; if (SRF_IS_FIRSTCALL()) { - TupleDesc tupdesc; + TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1459,7 +1460,7 @@ get_packages_stats(PG_FUNCTION_ARGS) static void getKeyFromName(text *name, char *key) { - int key_len = VARSIZE_ANY_EXHDR(name); + int key_len = VARSIZE_ANY_EXHDR(name); if (key_len >= NAMEDATALEN - 1) ereport(ERROR, @@ -1502,9 +1503,9 @@ ensurePackagesHashExists() static HashPackageEntry * getPackageByName(text* name, bool create, bool strict) { - HashPackageEntry *package; - char key[NAMEDATALEN]; - bool found; + HashPackageEntry *package; + char key[NAMEDATALEN]; + bool found; getKeyFromName(name, key); @@ -1517,7 +1518,7 @@ getPackageByName(text* name, bool create, bool strict) if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + errmsg("unrecognized package \"%s\"", key))); else return NULL; } @@ -1535,9 +1536,9 @@ getPackageByName(text* name, bool create, bool strict) { if (create) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - MemoryContext oldcxt; + HASHCTL ctl; + char hash_name[BUFSIZ]; + MemoryContext oldcxt; sprintf(hash_name, "Variables hash for package \"%s\"", key); @@ -1583,21 +1584,21 @@ getPackageByName(text* name, bool create, bool strict) static HashVariableEntry * getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) { - HashVariableEntry *variable; - char key[NAMEDATALEN]; - bool found; + HashVariableEntry *variable; + char key[NAMEDATALEN]; + bool found; getKeyFromName(name, key); variable = (HashVariableEntry *) hash_search(variables, - key, HASH_FIND, &found); + 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, @@ -1626,35 +1627,34 @@ static HashVariableEntry * createVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool is_transactional) { - HashVariableEntry *variable; - char key[NAMEDATALEN]; - bool found; + HashVariableEntry *variable; + char key[NAMEDATALEN]; + bool found; getKeyFromName(name, key); variable = (HashVariableEntry *) hash_search(package->variablesHash, - key, HASH_ENTER, &found); + key, HASH_ENTER, &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), - errmsg("variable \"%s\" requires \"%s\" value", - key, var_type))); + 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 "))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + key, variable->is_transactional ? "" : "NOT "))); } /* @@ -1674,15 +1674,18 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, if (variable) { ValueHistoryEntry *historyEntry; + variable->typid = typid; variable->is_transactional = is_transactional; dlist_init(&(variable->data)); historyEntry = MemoryContextAllocZero(package->hctx, sizeof(ValueHistoryEntry)); + 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; @@ -1704,17 +1707,17 @@ static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) { - if(variable->typid == RECORDOID) + if (variable->typid == RECORDOID) { insert_savepoint(variable, package->hctx); } else { - ScalarVar *scalar; - ValueHistory *history; - ValueHistoryEntry *history_entry_new, - *history_entry_prev; - MemoryContext oldcxt; + ScalarVar *scalar; + ValueHistory *history; + ValueHistoryEntry *history_entry_new, + *history_entry_prev; + MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(package->hctx); history = &variable->data; @@ -1726,13 +1729,12 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) if (!scalar->is_null) { - scalar->value = datumCopy( - history_entry_prev->value.scalar.value, - scalar->typbyval, - scalar->typlen); + 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); } @@ -1749,8 +1751,8 @@ releaseSavepoint(HashVariableEntry *variable) history = &variable->data; if (dlist_has_next(history, dlist_head_node(history))) { - ValueHistoryEntry *historyEntryToDelete; - dlist_node *nodeToDelete; + ValueHistoryEntry *historyEntryToDelete; + dlist_node *nodeToDelete; nodeToDelete = dlist_next_node(history, dlist_head_node(history)); historyEntryToDelete = get_history_entry(nodeToDelete); @@ -1761,7 +1763,7 @@ releaseSavepoint(HashVariableEntry *variable) MemoryContextDelete(historyEntryToDelete->value.record.hctx); } else if (historyEntryToDelete->value.scalar.typbyval == false && - historyEntryToDelete->value.scalar.is_null == false) + historyEntryToDelete->value.scalar.is_null == false) pfree(DatumGetPointer(historyEntryToDelete->value.scalar.value)); dlist_delete(nodeToDelete); @@ -1776,10 +1778,12 @@ 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; + bool found; + hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); } } @@ -1792,12 +1796,15 @@ 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; @@ -1813,53 +1820,62 @@ pushChangedVarsStack() { MemoryContext oldcxt; ChangedVarsStackNode *cvsn; - char child_context_name[BUFSIZ]; + char child_context_name[BUFSIZ]; + /* * Initialize changedVarsStack and create MemoryContext for it * if not done before. */ - if(!changedVarsContext) + if (!changedVarsContext) { - char top_context_name[BUFSIZ]; + 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); + 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); + 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); + 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); + child_context_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); #endif + dlist_init(cvsn->changedVarsList); dlist_push_head(changedVarsStack, &cvsn->node); @@ -1875,14 +1891,15 @@ popChangedVarsStack() if (changedVarsStack) { ChangedVarsStackNode *cvse; + Assert(!dlist_is_empty(changedVarsStack)); cvse = dlist_container(ChangedVarsStackNode, node, - dlist_pop_head_node(changedVarsStack)); + dlist_pop_head_node(changedVarsStack)); MemoryContextDelete(cvse->ctx); if (dlist_is_empty(changedVarsStack)) { MemoryContextDelete(changedVarsContext); - changedVarsStack = NULL; + changedVarsStack = NULL; changedVarsContext = NULL; } } @@ -1894,23 +1911,27 @@ popChangedVarsStack() static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { - ChangedVarsStackNode *cvsn; + ChangedVarsStackNode *cvsn; + if (!changedVarsStack) { - int level = GetCurrentTransactionNestLevel(); - while(level -- > 0) + int level = GetCurrentTransactionNestLevel(); + + while (level-- > 0) { pushChangedVarsStack(); } } + Assert(changedVarsStack && changedVarsContext); if (!isVarChangedInTrans(variable)) { ChangedVarsNode *cvn; + cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); cvn = MemoryContextAllocZero(cvsn->ctx, sizeof(ChangedVarsNode)); - cvn->package = package; + cvn->package = package; cvn->variable = variable; dlist_push_head(cvsn->changedVarsList, &cvn->node); } @@ -1927,17 +1948,19 @@ levelUpOrRelease() { if (changedVarsStack) { - dlist_iter iter; + 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)); + 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)) + if (isVarChangedInTrans(cvn->variable)) releaseSavepoint(cvn->variable); else addToChangedVars(cvn->package, cvn->variable); @@ -1965,11 +1988,13 @@ applyActionOnChangedVars(Action action) { dlist_mutable_iter miter; dlist_head *changedVars; + Assert(changedVarsStack); changedVars = get_actual_changed_vars_list(); dlist_foreach_modify(miter, changedVars) { ChangedVarsNode *cvn = dlist_container(ChangedVarsNode, node, miter.cur); + switch(action) { case RELEASE_SAVEPOINT: @@ -1987,7 +2012,7 @@ applyActionOnChangedVars(Action action) */ static void pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, - SubTransactionId parentSubid, void *arg) + SubTransactionId parentSubid, void *arg) { if (changedVarsStack) { @@ -2044,7 +2069,8 @@ pgvTransCallback(XactEvent event, void *arg) /* * Register callback function when module starts */ -void _PG_init(void) +void +_PG_init(void) { RegisterXactCallback(pgvTransCallback, NULL); RegisterSubXactCallback(pgvSubTransCallback, NULL); @@ -2053,7 +2079,8 @@ void _PG_init(void) /* * Unregister callback function when module unloads */ -void _PG_fini(void) +void +_PG_fini(void) { UnregisterXactCallback(pgvTransCallback, NULL); UnregisterSubXactCallback(pgvSubTransCallback, NULL); diff --git a/pg_variables.h b/pg_variables.h index fb67aa2..383e2c7 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -34,40 +34,40 @@ typedef struct HashPackageEntry { - char name[NAMEDATALEN]; - HTAB *variablesHash; + char name[NAMEDATALEN]; + HTAB *variablesHash; /* Memory context for package variables for easy memory release */ - MemoryContext hctx; + MemoryContext hctx; } HashPackageEntry; typedef struct RecordVar { - HTAB *rhash; - TupleDesc tupdesc; + HTAB *rhash; + TupleDesc tupdesc; /* Memory context for records hash table for easy memory release */ - MemoryContext hctx; + MemoryContext hctx; /* Hash function info */ - FmgrInfo hash_proc; + FmgrInfo hash_proc; /* Match function info */ - FmgrInfo cmp_proc; + FmgrInfo cmp_proc; } RecordVar; typedef struct ScalarVar { - Datum value; - bool is_null; - bool typbyval; - int16 typlen; + Datum value; + bool is_null; + bool typbyval; + int16 typlen; } ScalarVar; /* List node that stores one of the variables states */ typedef struct ValueHistoryEntry{ - dlist_node node; + dlist_node node; union { - ScalarVar scalar; - RecordVar record; - } value; + ScalarVar scalar; + RecordVar record; + } value; } ValueHistoryEntry; typedef dlist_head ValueHistory; @@ -75,15 +75,15 @@ typedef dlist_head ValueHistory; /* Variable by itself */ typedef struct HashVariableEntry { - char name[NAMEDATALEN]; + char name[NAMEDATALEN]; /* Entry point to list with states of value */ - ValueHistory data; - Oid typid; + ValueHistory data; + Oid typid; /* * The flag determines the further behavior of the variable. * Can be specified only when creating a variable. */ - bool is_transactional; + bool is_transactional; } HashVariableEntry; typedef struct HashRecordKey @@ -98,24 +98,24 @@ typedef struct HashRecordKey typedef struct HashRecordEntry { - HashRecordKey key; - HeapTuple tuple; + HashRecordKey key; + HeapTuple tuple; } HashRecordEntry; /* Element of list with variables, changed within transaction */ typedef struct ChangedVarsNode { - dlist_node node; - HashPackageEntry *package; - HashVariableEntry *variable; + dlist_node node; + HashPackageEntry *package; + HashVariableEntry *variable; } ChangedVarsNode; /* Element of stack with 'changedVars' list heads*/ typedef struct ChangedVarsStackNode { - dlist_node node; - dlist_head *changedVarsList; - MemoryContext ctx; + dlist_node node; + dlist_head *changedVarsList; + MemoryContext ctx; } ChangedVarsStackNode; extern void init_attributes(HashVariableEntry* variable, TupleDesc tupdesc, diff --git a/pg_variables_record.c b/pg_variables_record.c index bd9fff1..7e82e7b 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -28,8 +28,8 @@ static uint32 record_hash(const void *key, Size keysize) { - HashRecordKey k = *((const HashRecordKey *) key); - Datum h; + HashRecordKey k = *((const HashRecordKey *) key); + Datum h; if (k.is_null) return 0; @@ -44,9 +44,9 @@ record_hash(const void *key, Size keysize) static int record_match(const void *key1, const void *key2, Size keysize) { - HashRecordKey k1 = *((const HashRecordKey *) key1); - HashRecordKey k2 = *((const HashRecordKey *) key2); - Datum c; + HashRecordKey k1 = *((const HashRecordKey *) key1); + HashRecordKey k2 = *((const HashRecordKey *) key2); + Datum c; if (k1.is_null) { @@ -69,10 +69,10 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, { HASHCTL ctl; char hash_name[BUFSIZ]; - MemoryContext oldcxt; - RecordVar *record; - TypeCacheEntry *typentry; - Oid keyid; + MemoryContext oldcxt; + RecordVar *record; + TypeCacheEntry *typentry; + Oid keyid; Assert(variable->typid == RECORDOID); @@ -105,8 +105,8 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, ctl.match = record_match; record->rhash = hash_create(hash_name, NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT | - HASH_FUNCTION | HASH_COMPARE); + HASH_ELEM | HASH_CONTEXT | + HASH_FUNCTION | HASH_COMPARE); /* Get hash and match functions for key type. */ keyid = GetTupleDescAttr(record->tupdesc, 0)->atttypid; @@ -154,8 +154,8 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) { - Form_pg_attribute attr1 = GetTupleDescAttr(record->tupdesc, i), - attr2 = GetTupleDescAttr(tupdesc, i); + Form_pg_attribute attr1 = GetTupleDescAttr(record->tupdesc, i), + attr2 = GetTupleDescAttr(tupdesc, i); if ((attr1->atttypid != attr2->atttypid) || (attr1->attndims != attr2->attndims) @@ -174,6 +174,7 @@ void check_record_key(HashVariableEntry *variable, Oid typid) { RecordVar *record; + Assert(variable->typid == RECORDOID); record = get_actual_value_record(variable); @@ -190,16 +191,16 @@ check_record_key(HashVariableEntry *variable, Oid typid) void insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) { - TupleDesc tupdesc; - HeapTuple tuple; - int tuple_len; - Datum value; - bool isnull; - RecordVar *record; - HashRecordKey k; - HashRecordEntry *item; - bool found; - MemoryContext oldcxt; + TupleDesc tupdesc; + HeapTuple tuple; + int tuple_len; + Datum value; + bool isnull; + RecordVar *record; + HashRecordKey k; + HashRecordEntry *item; + bool found; + MemoryContext oldcxt; Assert(variable->typid == RECORDOID); @@ -250,16 +251,16 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) bool update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) { - TupleDesc tupdesc; - HeapTuple tuple; - int tuple_len; - Datum value; - bool isnull; - RecordVar *record; - HashRecordKey k; - HashRecordEntry *item; - bool found; - MemoryContext oldcxt; + TupleDesc tupdesc; + HeapTuple tuple; + int tuple_len; + Datum value; + bool isnull; + RecordVar *record; + HashRecordKey k; + HashRecordEntry *item; + bool found; + MemoryContext oldcxt; Assert(variable->typid == RECORDOID); @@ -306,10 +307,10 @@ update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) bool delete_record(HashVariableEntry *variable, Datum value, bool is_null) { - HashRecordKey k; - HashRecordEntry *item; - bool found; - RecordVar *record; + HashRecordKey k; + HashRecordEntry *item; + bool found; + RecordVar *record; Assert(variable->typid == RECORDOID); @@ -335,7 +336,8 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) void clean_records(HashVariableEntry *variable) { - RecordVar *record; + RecordVar *record; + Assert(variable->typid == RECORDOID); record = get_actual_value_record(variable); @@ -350,14 +352,14 @@ clean_records(HashVariableEntry *variable) 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; + 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); @@ -375,9 +377,10 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) 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); + HASH_ENTER, &found); item_new->tuple = heap_copytuple(item_prev->tuple); } MemoryContextSwitchTo(oldcxt); From d888a73c8fea73f56820cebaca1150d7e23b6336 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 2 May 2018 21:36:01 +0300 Subject: [PATCH 023/147] Optimization of isVarChangedInTrans() --- pg_variables.c | 78 +++++++++++++++++++++++++++++++++----------------- pg_variables.h | 4 +++ 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 349ea1a..828a1a5 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -76,7 +76,7 @@ static HashVariableEntry *createVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool is_transactional); static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); -static bool isVarChangedInTrans(HashVariableEntry *variable); +static bool isVarChangedInCurrentTrans(HashVariableEntry *variable); static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ @@ -100,12 +100,11 @@ static HashPackageEntry *LastPackage = NULL; static HashVariableEntry *LastVariable = NULL; /* - * List of variables, changed in top level transaction. Used to limit + * Stack of lists of variables, changed in each transaction level. 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; +static MemoryContext changedVarsContext = NULL; #define get_actual_changed_vars_list() \ ((dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ changedVarsList) @@ -621,7 +620,7 @@ variable_insert(PG_FUNCTION_ARGS) errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, LastVariable->is_transactional ? "" : "NOT "))); } - if (!isVarChangedInTrans(variable) && variable->is_transactional) + if (!isVarChangedInCurrentTrans(variable) && variable->is_transactional) { createSavepoint(package, variable); addToChangedVars(package, variable); @@ -707,7 +706,7 @@ variable_update(PG_FUNCTION_ARGS) else variable = LastVariable; - if (variable->is_transactional && !isVarChangedInTrans(variable)) + if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { createSavepoint(package, variable); addToChangedVars(package, variable); @@ -785,7 +784,7 @@ variable_delete(PG_FUNCTION_ARGS) else variable = LastVariable; - if (variable->is_transactional && !isVarChangedInTrans(variable)) + if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { createSavepoint(package, variable); addToChangedVars(package, variable); @@ -1220,7 +1219,7 @@ remove_packages(PG_FUNCTION_ARGS) packagesHash = NULL; ModuleContext = NULL; - changedVars = NULL; + changedVarsStack = NULL; PG_RETURN_VOID(); } @@ -1663,7 +1662,7 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, * For each transaction level there should be own savepoint. * New value should be stored in a last state. */ - if (variable->is_transactional && !isVarChangedInTrans(variable)) + if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { createSavepoint(package, variable); } @@ -1769,6 +1768,7 @@ releaseSavepoint(HashVariableEntry *variable) dlist_delete(nodeToDelete); pfree(historyEntryToDelete); } + (get_actual_value(variable)->level)--; } /* @@ -1792,24 +1792,35 @@ rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) * Check if variable was changed in current transaction level */ static bool -isVarChangedInTrans(HashVariableEntry *variable) +isVarChangedInCurrentTrans(HashVariableEntry *variable) { - dlist_iter iter; - dlist_head *changedVars; + ValueHistoryEntry *var_state; if (!changedVarsStack) return false; - changedVars = get_actual_changed_vars_list(); - dlist_foreach(iter, changedVars) - { - ChangedVarsNode *cvn; + var_state = get_actual_value(variable); + return (var_state->level == GetCurrentTransactionNestLevel()); +} - cvn = dlist_container(ChangedVarsNode, node, iter.cur); - if (cvn->variable == variable) - return true; +/* + * Check if variable was changed in parent transaction level + */ +static bool +isVarChangedInUpperTrans(HashVariableEntry *variable) +{ + ValueHistoryEntry *var_state, + *var_prev_state; + + var_state = get_actual_value(variable); + + if(dlist_has_next(&variable->data, &var_state->node)) + { + var_prev_state = get_history_entry(var_state->node.next); + return (var_prev_state->level == (GetCurrentTransactionNestLevel() - 1)); } - return false; + else + return false; } /* @@ -1925,7 +1936,7 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) Assert(changedVarsStack && changedVarsContext); - if (!isVarChangedInTrans(variable)) + if (!isVarChangedInCurrentTrans(variable)) { ChangedVarsNode *cvn; @@ -1934,6 +1945,7 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) cvn->package = package; cvn->variable = variable; dlist_push_head(cvsn->changedVarsList, &cvn->node); + get_actual_value(cvn->variable)->level = GetCurrentTransactionNestLevel(); } } @@ -1957,13 +1969,27 @@ levelUpOrRelease() Assert(!dlist_is_empty(changedVarsStack)); dlist_foreach(iter, bottom_list->changedVarsList) { - ChangedVarsNode *cvn; + ChangedVarsNode *cvn_old; - cvn = dlist_container(ChangedVarsNode, node, iter.cur); - if (isVarChangedInTrans(cvn->variable)) - releaseSavepoint(cvn->variable); + cvn_old = dlist_container(ChangedVarsNode, node, iter.cur); + if (isVarChangedInUpperTrans(cvn_old->variable)) + releaseSavepoint(cvn_old->variable); else - addToChangedVars(cvn->package, cvn->variable); + { + ChangedVarsNode *cvn_new; + ChangedVarsStackNode *cvsn; + + /* + * Impossible to push in upper list existing node because + * it was created in another context + */ + cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); + cvn_new = MemoryContextAllocZero(cvsn->ctx, sizeof(ChangedVarsNode)); + cvn_new->package = cvn_old->package; + cvn_new->variable = cvn_old->variable; + dlist_push_head(cvsn->changedVarsList, &cvn_new->node); + (get_actual_value(cvn_new->variable)->level)--; + } } MemoryContextDelete(bottom_list->ctx); } diff --git a/pg_variables.h b/pg_variables.h index 383e2c7..1357244 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -68,6 +68,8 @@ typedef struct ValueHistoryEntry{ ScalarVar scalar; RecordVar record; } value; + /* Transaction nest level of current entry */ + int level; } ValueHistoryEntry; typedef dlist_head ValueHistory; @@ -139,6 +141,8 @@ extern void insert_savepoint(HashVariableEntry *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_actual_value(variable) \ + (dlist_head_element(ValueHistoryEntry, node, &variable->data)) #define get_history_entry(node_ptr) \ dlist_container(ValueHistoryEntry, node, node_ptr) From 0055ca39b5bfa6034600f4c09e8be9008998f72a Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 2 May 2018 21:39:57 +0300 Subject: [PATCH 024/147] Add clarification in README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d017a32..862153a 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,15 @@ SELECT pgv_get('pack','var_int', NULL::int); ERROR: unrecognized variable "var_int" COMMIT; ``` +Also you cannot undo removing variable by `ROLLBACK`: +```sql +SELECT pgv_set('pack', 'var_int', 122, true); +BEGIN; +SELECT pgv_free(); +ROLLBACK; +SELECT pgv_get('pack', 'var_int', NULL::int); +ERROR: unrecognized package "pack" +``` 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 From bf7c4206726ae3cf746e1540bff1e9c67b2fc677 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 4 May 2018 13:37:18 +0300 Subject: [PATCH 025/147] Refactored --- pg_variables.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 828a1a5..1fc79a2 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -77,6 +77,7 @@ static HashVariableEntry *createVariableInternal(HashPackageEntry *package, bool is_transactional); static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); static bool isVarChangedInCurrentTrans(HashVariableEntry *variable); +static bool isVarChangedInUpperTrans(HashVariableEntry *variable); static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ @@ -1768,6 +1769,10 @@ releaseSavepoint(HashVariableEntry *variable) dlist_delete(nodeToDelete); pfree(historyEntryToDelete); } + /* + * If variable was changed in subtransaction, so it is considered it + * was changed in parent transaction. + */ (get_actual_value(variable)->level)--; } @@ -1819,8 +1824,7 @@ isVarChangedInUpperTrans(HashVariableEntry *variable) var_prev_state = get_history_entry(var_state->node.next); return (var_prev_state->level == (GetCurrentTransactionNestLevel() - 1)); } - else - return false; + return false; } /* @@ -1901,12 +1905,12 @@ popChangedVarsStack() { if (changedVarsStack) { - ChangedVarsStackNode *cvse; + ChangedVarsStackNode *cvsn; Assert(!dlist_is_empty(changedVarsStack)); - cvse = dlist_container(ChangedVarsStackNode, node, + cvsn = dlist_container(ChangedVarsStackNode, node, dlist_pop_head_node(changedVarsStack)); - MemoryContextDelete(cvse->ctx); + MemoryContextDelete(cvsn->ctx); if (dlist_is_empty(changedVarsStack)) { MemoryContextDelete(changedVarsContext); @@ -1916,6 +1920,20 @@ popChangedVarsStack() } } +/* + * Initialize an instance of ChangedVarsNode datatype + */ +static inline ChangedVarsNode * +initChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable) +{ + ChangedVarsNode *cvn; + + cvn = MemoryContextAllocZero(ctx, sizeof(ChangedVarsNode)); + cvn->package = package; + cvn->variable = variable; + return cvn; +} + /* * Add a variable to list of changed vars in current transaction level */ @@ -1941,16 +1959,14 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) ChangedVarsNode *cvn; cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn = MemoryContextAllocZero(cvsn->ctx, sizeof(ChangedVarsNode)); - cvn->package = package; - cvn->variable = variable; + cvn = initChangedVarsNode(cvsn->ctx, package, variable); dlist_push_head(cvsn->changedVarsList, &cvn->node); get_actual_value(cvn->variable)->level = GetCurrentTransactionNestLevel(); } } /* - * If variable was chenged in some subtransaction, it is considered that it was + * If variable was changed 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. @@ -1984,9 +2000,7 @@ levelUpOrRelease() * it was created in another context */ cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn_new = MemoryContextAllocZero(cvsn->ctx, sizeof(ChangedVarsNode)); - cvn_new->package = cvn_old->package; - cvn_new->variable = cvn_old->variable; + cvn_new = initChangedVarsNode(cvsn->ctx, cvn_old->package, cvn_old->variable); dlist_push_head(cvsn->changedVarsList, &cvn_new->node); (get_actual_value(cvn_new->variable)->level)--; } From 6e337420c8b2525d952e4014714e59aa268ed8bf Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Fri, 4 May 2018 16:10:21 +0300 Subject: [PATCH 026/147] small refactoring, fixes for PG 11 --- pg_variables.c | 717 +++++++++++++++---------------------------------- 1 file changed, 211 insertions(+), 506 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 1fc79a2..7407993 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -30,22 +30,6 @@ PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(variable_set_any); PG_FUNCTION_INFO_V1(variable_get_any); -/* Deprecated scalar variables functions */ -PG_FUNCTION_INFO_V1(variable_set_int); -PG_FUNCTION_INFO_V1(variable_get_int); -PG_FUNCTION_INFO_V1(variable_set_text); -PG_FUNCTION_INFO_V1(variable_get_text); -PG_FUNCTION_INFO_V1(variable_set_numeric); -PG_FUNCTION_INFO_V1(variable_get_numeric); -PG_FUNCTION_INFO_V1(variable_set_timestamp); -PG_FUNCTION_INFO_V1(variable_get_timestamp); -PG_FUNCTION_INFO_V1(variable_set_timestamptz); -PG_FUNCTION_INFO_V1(variable_get_timestamptz); -PG_FUNCTION_INFO_V1(variable_set_date); -PG_FUNCTION_INFO_V1(variable_get_date); -PG_FUNCTION_INFO_V1(variable_set_jsonb); -PG_FUNCTION_INFO_V1(variable_get_jsonb); - /* Functions to work with records */ PG_FUNCTION_INFO_V1(variable_insert); PG_FUNCTION_INFO_V1(variable_update); @@ -66,19 +50,27 @@ PG_FUNCTION_INFO_V1(get_packages_stats); extern void _PG_init(void); extern void _PG_fini(void); +static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); -static void ensurePackagesHashExists(); -static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); +static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); static HashVariableEntry *getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict); static HashVariableEntry *createVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool is_transactional); + +static void releaseSavepoint(HashVariableEntry *variable); +static void rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable); static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); + +static void mergeChangedVarsStack(void); +static void pushChangedVarsStack(void); +static void popChangedVarsStack(void); +static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); + static bool isVarChangedInCurrentTrans(HashVariableEntry *variable); static bool isVarChangedInUpperTrans(HashVariableEntry *variable); -static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -100,15 +92,35 @@ static HashPackageEntry *LastPackage = NULL; /* Recent variable */ static HashVariableEntry *LastVariable = NULL; -/* - * Stack of lists of variables, changed in each transaction level. Used to limit - * number of proceeded variables on start of transaction. - */ + +/* This stack contains lists of changed variables per each subxact level */ static dlist_head *changedVarsStack = NULL; static MemoryContext changedVarsContext = NULL; + +/* Returns a list of of vars changed at current subxact level */ #define get_actual_changed_vars_list() \ - ((dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \ - changedVarsList) + ( \ + AssertMacro(changedVarsStack != NULL), \ + (dlist_head_element(ChangedVarsStackNode, \ + node, changedVarsStack))->changedVarsList \ + ) + + +#define PGV_MCXT_MAIN "pg_variables: main memory context" +#define PGV_MCXT_VARS "pg_variables: variables hash" +#define PGV_MCXT_STACK "pg_variables: changedVarsStack" +#define PGV_MCXT_STACK_NODE "pg_variables: changedVarsStackNode" + + +#ifndef ALLOCSET_DEFAULT_SIZES +#define ALLOCSET_DEFAULT_SIZES \ + ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE +#endif + +#ifndef ALLOCSET_START_SMALL_SIZES +#define ALLOCSET_START_SMALL_SIZES \ + ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE +#endif /* @@ -128,6 +140,7 @@ variable_set(text *package_name, text *var_name, is_transactional); scalar = get_actual_value_scalar(variable); + /* Release memory for variable */ if (scalar->typbyval == false && scalar->is_null == false) pfree(DatumGetPointer(scalar->value)); @@ -171,297 +184,82 @@ variable_get(text *package_name, text *var_name, return scalar->value; } -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_any(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(3); - - value = variable_get(package_name, var_name, - get_fn_expr_argtype(fcinfo->flinfo, 2), - &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_int(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - - value = variable_get(package_name, var_name, - INT4OID, &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_text(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - - value = variable_get(package_name, var_name, - TEXTOID, &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_numeric(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - - value = variable_get(package_name, var_name, - NUMERICOID, &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_timestamp(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - - value = variable_get(package_name, var_name, - TIMESTAMPOID, &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} +#define VARIABLE_GET_TEMPLATE(type, typid) \ + PG_FUNCTION_INFO_V1(variable_get_##type); \ + Datum \ + variable_get_##type(PG_FUNCTION_ARGS) \ + { \ + text *package_name; \ + text *var_name; \ + bool strict; \ + bool isnull; \ + Datum value; \ + \ + CHECK_ARGS_FOR_NULL(); \ + \ + package_name = PG_GETARG_TEXT_PP(0); \ + var_name = PG_GETARG_TEXT_PP(1); \ + strict = PG_GETARG_BOOL(2); \ + \ + value = variable_get(package_name, var_name, \ + (typid), &isnull, strict); \ + \ + PG_FREE_IF_COPY(package_name, 0); \ + PG_FREE_IF_COPY(var_name, 1); \ + \ + if (!isnull) \ + PG_RETURN_DATUM(value); \ + else \ + PG_RETURN_NULL(); \ + } -Datum -variable_get_timestamptz(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - CHECK_ARGS_FOR_NULL(); +VARIABLE_GET_TEMPLATE(int, INT4OID) +VARIABLE_GET_TEMPLATE(text, TEXTOID) +VARIABLE_GET_TEMPLATE(numeric, NUMERICOID) +VARIABLE_GET_TEMPLATE(timestamp, TIMESTAMPOID) +VARIABLE_GET_TEMPLATE(timestamptz, TIMESTAMPTZOID) +VARIABLE_GET_TEMPLATE(date, DATEOID) +VARIABLE_GET_TEMPLATE(jsonb, JSONBOID) + + +#define VARIABLE_SET_TEMPLATE(type, typid) \ + PG_FUNCTION_INFO_V1(variable_set_##type); \ + Datum \ + variable_set_##type(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, (typid), \ + PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), \ + PG_ARGISNULL(2), is_transactional); \ + \ + PG_FREE_IF_COPY(package_name, 0); \ + PG_FREE_IF_COPY(var_name, 1); \ + PG_RETURN_VOID(); \ + } - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - value = variable_get(package_name, var_name, - TIMESTAMPTZOID, &is_null, strict); +VARIABLE_SET_TEMPLATE(int, INT4OID) +VARIABLE_SET_TEMPLATE(text, TEXTOID) +VARIABLE_SET_TEMPLATE(numeric, NUMERICOID) +VARIABLE_SET_TEMPLATE(timestamp, TIMESTAMPOID) +VARIABLE_SET_TEMPLATE(timestamptz, TIMESTAMPTZOID) +VARIABLE_SET_TEMPLATE(date, DATEOID) +VARIABLE_SET_TEMPLATE(jsonb, JSONBOID) - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} Datum -variable_set_date(PG_FUNCTION_ARGS) +variable_set_any(PG_FUNCTION_ARGS) { text *package_name; text *var_name; @@ -473,7 +271,7 @@ variable_set_date(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); is_transactional = PG_GETARG_BOOL(3); - variable_set(package_name, var_name, DATEOID, + variable_set(package_name, var_name, get_fn_expr_argtype(fcinfo->flinfo, 2), PG_ARGISNULL(2) ? 0 : PG_GETARG_DATUM(2), PG_ARGISNULL(2), is_transactional); @@ -483,7 +281,7 @@ variable_set_date(PG_FUNCTION_ARGS) } Datum -variable_get_date(PG_FUNCTION_ARGS) +variable_get_any(PG_FUNCTION_ARGS) { text *package_name; text *var_name; @@ -495,10 +293,11 @@ variable_get_date(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); + strict = PG_GETARG_BOOL(3); value = variable_get(package_name, var_name, - DATEOID, &is_null, strict); + get_fn_expr_argtype(fcinfo->flinfo, 2), + &is_null, strict); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -508,53 +307,6 @@ variable_get_date(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_jsonb(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(2); - - value = variable_get(package_name, var_name, - JSONBOID, &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} Datum variable_insert(PG_FUNCTION_ARGS) @@ -1473,31 +1225,24 @@ getKeyFromName(text *name, char *key) } static void -ensurePackagesHashExists() +ensurePackagesHashExists(void) { - HASHCTL ctl; + HASHCTL ctl; if (packagesHash) return; -#if PG_VERSION_NUM >= 110000 ModuleContext = AllocSetContextCreate(CacheMemoryContext, - "pg_variables memory context", + PGV_MCXT_MAIN, ALLOCSET_DEFAULT_SIZES); -#else - ModuleContext = AllocSetContextCreate(CacheMemoryContext, - "pg_variables memory context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#endif ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(HashPackageEntry); ctl.hcxt = ModuleContext; packagesHash = hash_create("Packages hash", - NUMPACKAGES, &ctl, HASH_ELEM | HASH_CONTEXT); + NUMPACKAGES, &ctl, + HASH_ELEM | HASH_CONTEXT); } static HashPackageEntry * @@ -1519,44 +1264,29 @@ getPackageByName(text* name, bool create, bool strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized package \"%s\"", key))); - else - return NULL; + + return NULL; } } - if (create) - package = (HashPackageEntry *) hash_search(packagesHash, - key, HASH_ENTER, &found); - else - package = (HashPackageEntry *) hash_search(packagesHash, - key, HASH_FIND, &found); + /* Find or create a package entry */ + package = (HashPackageEntry *) hash_search(packagesHash, key, + (create ? HASH_ENTER : HASH_FIND), + &found); /* Package entry was created, so we need create hash table for variables. */ if (!found) { if (create) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - MemoryContext oldcxt; - - sprintf(hash_name, "Variables hash for package \"%s\"", key); + HASHCTL ctl; + char hash_name[BUFSIZ]; -#if PG_VERSION_NUM >= 110000 - package->hctx = AllocSetContextCreateExtended(ModuleContext, - hash_name, - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#else package->hctx = AllocSetContextCreate(ModuleContext, - hash_name, - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#endif + PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); - oldcxt = MemoryContextSwitchTo(package->hctx); + sprintf(hash_name, "Variables hash for package \"%s\"", key); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(HashVariableEntry); @@ -1564,8 +1294,6 @@ getPackageByName(text* name, bool create, bool strict) package->variablesHash = hash_create(hash_name, NUMVARIABLES, &ctl, HASH_ELEM | HASH_CONTEXT); - - MemoryContextSwitchTo(oldcxt); } else if (strict) ereport(ERROR, @@ -1649,7 +1377,8 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } - if (variable->is_transactional!=is_transactional) + + if (variable->is_transactional != is_transactional) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1660,7 +1389,7 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, /* * Savepoint must be created when variable changed in current * transaction. - * For each transaction level there should be own savepoint. + * For each transaction level there should be a corresponding savepoint. * New value should be stored in a last state. */ if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) @@ -1721,6 +1450,7 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) 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); @@ -1764,16 +1494,16 @@ releaseSavepoint(HashVariableEntry *variable) } else if (historyEntryToDelete->value.scalar.typbyval == false && historyEntryToDelete->value.scalar.is_null == false) + { pfree(DatumGetPointer(historyEntryToDelete->value.scalar.value)); + } dlist_delete(nodeToDelete); pfree(historyEntryToDelete); } - /* - * If variable was changed in subtransaction, so it is considered it - * was changed in parent transaction. - */ - (get_actual_value(variable)->level)--; + + /* Change subxact level due to release */ + get_actual_value(variable)->level--; } /* @@ -1793,6 +1523,20 @@ rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) } } +/* + * Initialize an instance of ChangedVarsNode datatype + */ +static inline ChangedVarsNode * +makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable) +{ + ChangedVarsNode *cvn; + + cvn = MemoryContextAllocZero(ctx, sizeof(ChangedVarsNode)); + cvn->package = package; + cvn->variable = variable; + return cvn; +} + /* * Check if variable was changed in current transaction level */ @@ -1805,7 +1549,7 @@ isVarChangedInCurrentTrans(HashVariableEntry *variable) return false; var_state = get_actual_value(variable); - return (var_state->level == GetCurrentTransactionNestLevel()); + return var_state->level == GetCurrentTransactionNestLevel(); } /* @@ -1819,48 +1563,32 @@ isVarChangedInUpperTrans(HashVariableEntry *variable) var_state = get_actual_value(variable); - if(dlist_has_next(&variable->data, &var_state->node)) + if (dlist_has_next(&variable->data, &var_state->node)) { var_prev_state = get_history_entry(var_state->node.next); - return (var_prev_state->level == (GetCurrentTransactionNestLevel() - 1)); + return var_prev_state->level == GetCurrentTransactionNestLevel() - 1; } + return false; } /* - * Create new list of variables, changed in current transaction level + * Create a new list of variables, changed in current transaction level */ static void -pushChangedVarsStack() +pushChangedVarsStack(void) { 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 - } + PGV_MCXT_STACK, + ALLOCSET_START_SMALL_SIZES); oldcxt = MemoryContextSwitchTo(changedVarsContext); @@ -1873,23 +1601,9 @@ pushChangedVarsStack() 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 + PGV_MCXT_STACK_NODE, + ALLOCSET_START_SMALL_SIZES); dlist_init(cvsn->changedVarsList); dlist_push_head(changedVarsStack, &cvsn->node); @@ -1898,10 +1612,10 @@ pushChangedVarsStack() } /* - * Remove list of variables, changed in current transaction level + * Remove current list of variables, changed in current transaction level */ static void -popChangedVarsStack() +popChangedVarsStack(void) { if (changedVarsStack) { @@ -1921,17 +1635,52 @@ popChangedVarsStack() } /* - * Initialize an instance of ChangedVarsNode datatype + * Pop current list of variables and add missing changed vars to upper list */ -static inline ChangedVarsNode * -initChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable) +static void +mergeChangedVarsStack(void) { - ChangedVarsNode *cvn; + if (changedVarsStack) + { + dlist_iter iter; + ChangedVarsStackNode *bottom_list; - cvn = MemoryContextAllocZero(ctx, sizeof(ChangedVarsNode)); - cvn->package = package; - cvn->variable = variable; - return cvn; + /* List removed from stack but we still can use it */ + bottom_list = dlist_container(ChangedVarsStackNode, node, + dlist_pop_head_node(changedVarsStack)); + + /* There must be at least one parent level */ + Assert(!dlist_is_empty(changedVarsStack)); + + dlist_foreach(iter, bottom_list->changedVarsList) + { + ChangedVarsNode *cvn_old = dlist_container(ChangedVarsNode, node, iter.cur); + + /* Did this variable change at parent level? */ + if (isVarChangedInUpperTrans(cvn_old->variable)) + { + /* We just have to drop this state */ + releaseSavepoint(cvn_old->variable); + } + else + { + ChangedVarsNode *cvn_new; + ChangedVarsStackNode *cvsn; + + /* + * Impossible to push in upper list existing node because + * it was created in another context + */ + cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); + cvn_new = makeChangedVarsNode(cvsn->ctx, cvn_old->package, cvn_old->variable); + dlist_push_head(cvsn->changedVarsList, &cvn_new->node); + + /* Change subxact level due to release */ + get_actual_value(cvn_new->variable)->level--; + } + } + MemoryContextDelete(bottom_list->ctx); + } } /* @@ -1944,7 +1693,7 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) if (!changedVarsStack) { - int level = GetCurrentTransactionNestLevel(); + int level = GetCurrentTransactionNestLevel(); while (level-- > 0) { @@ -1959,53 +1708,11 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) ChangedVarsNode *cvn; cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn = initChangedVarsNode(cvsn->ctx, package, variable); + cvn = makeChangedVarsNode(cvsn->ctx, package, variable); dlist_push_head(cvsn->changedVarsList, &cvn->node); - get_actual_value(cvn->variable)->level = GetCurrentTransactionNestLevel(); - } -} - -/* - * If variable was changed 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 -levelUpOrRelease() -{ - 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_old; - - cvn_old = dlist_container(ChangedVarsNode, node, iter.cur); - if (isVarChangedInUpperTrans(cvn_old->variable)) - releaseSavepoint(cvn_old->variable); - else - { - ChangedVarsNode *cvn_new; - ChangedVarsStackNode *cvsn; - /* - * Impossible to push in upper list existing node because - * it was created in another context - */ - cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn_new = initChangedVarsNode(cvsn->ctx, cvn_old->package, cvn_old->variable); - dlist_push_head(cvsn->changedVarsList, &cvn_new->node); - (get_actual_value(cvn_new->variable)->level)--; - } - } - MemoryContextDelete(bottom_list->ctx); + /* Give this variable current subxact level */ + get_actual_value(cvn->variable)->level = GetCurrentTransactionNestLevel(); } } @@ -2026,11 +1733,9 @@ typedef enum Action static void applyActionOnChangedVars(Action action) { + dlist_head *changedVars = get_actual_changed_vars_list(); dlist_mutable_iter miter; - dlist_head *changedVars; - Assert(changedVarsStack); - changedVars = get_actual_changed_vars_list(); dlist_foreach_modify(miter, changedVars) { ChangedVarsNode *cvn = dlist_container(ChangedVarsNode, node, miter.cur); @@ -2062,7 +1767,7 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, pushChangedVarsStack(); break; case SUBXACT_EVENT_COMMIT_SUB: - levelUpOrRelease(); + mergeChangedVarsStack(); break; case SUBXACT_EVENT_ABORT_SUB: applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); From e1d6ad7c16292b7f827fe5147fb2e92849f25e61 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Fri, 4 May 2018 16:36:16 +0300 Subject: [PATCH 027/147] remove special cases for variable_{get,set}_any --- .gitignore | 1 + pg_variables.c | 85 +++++++++++--------------------------------------- 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/.gitignore b/.gitignore index 0f55eec..09586c9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ objfiles.txt *.gcov.out lcov.info coverage/ +results/ *.vcproj *.vcxproj win32ver.rc diff --git a/pg_variables.c b/pg_variables.c index 7407993..6302152 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -26,10 +26,6 @@ PG_MODULE_MAGIC; -/* Scalar variables functions */ -PG_FUNCTION_INFO_V1(variable_set_any); -PG_FUNCTION_INFO_V1(variable_get_any); - /* Functions to work with records */ PG_FUNCTION_INFO_V1(variable_insert); PG_FUNCTION_INFO_V1(variable_update); @@ -185,7 +181,7 @@ variable_get(text *package_name, text *var_name, } -#define VARIABLE_GET_TEMPLATE(type, typid) \ +#define VARIABLE_GET_TEMPLATE(pkg_arg, var_arg, strict_arg, type, typid) \ PG_FUNCTION_INFO_V1(variable_get_##type); \ Datum \ variable_get_##type(PG_FUNCTION_ARGS) \ @@ -198,15 +194,15 @@ variable_get(text *package_name, text *var_name, \ CHECK_ARGS_FOR_NULL(); \ \ - package_name = PG_GETARG_TEXT_PP(0); \ - var_name = PG_GETARG_TEXT_PP(1); \ - strict = PG_GETARG_BOOL(2); \ + package_name = PG_GETARG_TEXT_PP(pkg_arg); \ + var_name = PG_GETARG_TEXT_PP(var_arg); \ + strict = PG_GETARG_BOOL(strict_arg); \ \ value = variable_get(package_name, var_name, \ (typid), &isnull, strict); \ \ - PG_FREE_IF_COPY(package_name, 0); \ - PG_FREE_IF_COPY(var_name, 1); \ + PG_FREE_IF_COPY(package_name, pkg_arg); \ + PG_FREE_IF_COPY(var_name, var_arg); \ \ if (!isnull) \ PG_RETURN_DATUM(value); \ @@ -214,14 +210,17 @@ variable_get(text *package_name, text *var_name, PG_RETURN_NULL(); \ } +/* deprecated functions */ +VARIABLE_GET_TEMPLATE(0, 1, 2, int, INT4OID) +VARIABLE_GET_TEMPLATE(0, 1, 2, text, TEXTOID) +VARIABLE_GET_TEMPLATE(0, 1, 2, numeric, NUMERICOID) +VARIABLE_GET_TEMPLATE(0, 1, 2, timestamp, TIMESTAMPOID) +VARIABLE_GET_TEMPLATE(0, 1, 2, timestamptz, TIMESTAMPTZOID) +VARIABLE_GET_TEMPLATE(0, 1, 2, date, DATEOID) +VARIABLE_GET_TEMPLATE(0, 1, 2, jsonb, JSONBOID) -VARIABLE_GET_TEMPLATE(int, INT4OID) -VARIABLE_GET_TEMPLATE(text, TEXTOID) -VARIABLE_GET_TEMPLATE(numeric, NUMERICOID) -VARIABLE_GET_TEMPLATE(timestamp, TIMESTAMPOID) -VARIABLE_GET_TEMPLATE(timestamptz, TIMESTAMPTZOID) -VARIABLE_GET_TEMPLATE(date, DATEOID) -VARIABLE_GET_TEMPLATE(jsonb, JSONBOID) +/* current API */ +VARIABLE_GET_TEMPLATE(0, 1, 3, any, get_fn_expr_argtype(fcinfo->flinfo, 2)) #define VARIABLE_SET_TEMPLATE(type, typid) \ @@ -249,6 +248,7 @@ VARIABLE_GET_TEMPLATE(jsonb, JSONBOID) } +/* deprecated functions */ VARIABLE_SET_TEMPLATE(int, INT4OID) VARIABLE_SET_TEMPLATE(text, TEXTOID) VARIABLE_SET_TEMPLATE(numeric, NUMERICOID) @@ -257,55 +257,8 @@ VARIABLE_SET_TEMPLATE(timestamptz, TIMESTAMPTZOID) VARIABLE_SET_TEMPLATE(date, DATEOID) VARIABLE_SET_TEMPLATE(jsonb, JSONBOID) - -Datum -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), is_transactional); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_VOID(); -} - -Datum -variable_get_any(PG_FUNCTION_ARGS) -{ - text *package_name; - text *var_name; - bool strict; - bool is_null; - Datum value; - - CHECK_ARGS_FOR_NULL(); - - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); - strict = PG_GETARG_BOOL(3); - - value = variable_get(package_name, var_name, - get_fn_expr_argtype(fcinfo->flinfo, 2), - &is_null, strict); - - PG_FREE_IF_COPY(package_name, 0); - PG_FREE_IF_COPY(var_name, 1); - if (!is_null) - PG_RETURN_DATUM(value); - else - PG_RETURN_NULL(); -} +/* current API */ +VARIABLE_SET_TEMPLATE(any, get_fn_expr_argtype(fcinfo->flinfo, 2)) Datum From 22a4cc14f79b547d1a2eb0ede46fadbdd2a93e21 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 5 May 2018 09:24:38 +0300 Subject: [PATCH 028/147] The removal of a variable can be undone on rollback --- expected/pg_variables_trans.out | 57 ++++++++++++++++++++++++- pg_variables.c | 75 ++++++++++++++++++++++----------- pg_variables.h | 1 + pg_variables_record.c | 1 + sql/pg_variables_trans.sql | 18 +++++++- 5 files changed, 126 insertions(+), 26 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 8d83c6a..89b5fcb 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1392,7 +1392,7 @@ SELECT pgv_free(); (1 row) ---Additional tests +-- Additional tests SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; pgv_insert ------------ @@ -1598,3 +1598,58 @@ BEGIN; SELECT pgv_set('vars', 'any1', 'wrong type'::varchar, true); ERROR: variable "any1" requires "text" value COMMIT; +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +COMMIT; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized variable "any1" diff --git a/pg_variables.c b/pg_variables.c index 1fc79a2..94f4d98 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -621,7 +621,7 @@ variable_insert(PG_FUNCTION_ARGS) errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, LastVariable->is_transactional ? "" : "NOT "))); } - if (!isVarChangedInCurrentTrans(variable) && variable->is_transactional) + if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { createSavepoint(package, variable); addToChangedVars(package, variable); @@ -1030,7 +1030,6 @@ variable_select_by_values(PG_FUNCTION_ARGS) static void cleanVariableCurrentState(HashVariableEntry *variable) { - ValueHistory *history; ValueHistoryEntry *historyEntryToDelete; if (variable->typid == RECORDOID) @@ -1043,8 +1042,7 @@ cleanVariableCurrentState(HashVariableEntry *variable) pfree(DatumGetPointer(scalar->value)); } - history = &variable->data; - historyEntryToDelete = get_history_entry(dlist_pop_head_node(history)); + historyEntryToDelete = get_history_entry(dlist_pop_head_node(&variable->data)); pfree(historyEntryToDelete); } @@ -1069,7 +1067,8 @@ variable_exists(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; + HashPackageEntry *package; + HashVariableEntry *variable; char key[NAMEDATALEN]; bool found; @@ -1089,12 +1088,13 @@ variable_exists(PG_FUNCTION_ARGS) getKeyFromName(var_name, key); - hash_search(package->variablesHash, key, HASH_FIND, &found); + variable = (HashVariableEntry *) hash_search(package->variablesHash, + key, HASH_FIND, &found); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found); + PG_RETURN_BOOL(found?get_actual_value(variable)->is_valid:found); } /* @@ -1141,16 +1141,29 @@ remove_variable(PG_FUNCTION_ARGS) getKeyFromName(var_name, key); variable = (HashVariableEntry *) hash_search(package->variablesHash, - key, HASH_REMOVE, &found); + key, HASH_FIND, &found); if (!found) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized variable \"%s\"", key))); - - /* Remove variable from cache */ - LastVariable = NULL; - - cleanVariableAllStates(variable); + errmsg("unrecognized variable \"%s\"", key))); + else + { + if (variable->is_transactional) + { + createSavepoint(package, variable); + addToChangedVars(package, variable); + get_actual_value(variable)->is_valid = false; + } + else + { + /* Remove variable from package */ + hash_search(package->variablesHash, key, HASH_REMOVE, &found); + /* Free allocated memory */ + cleanVariableAllStates(variable); + } + /* Remove variable from cache */ + LastVariable = NULL; + } PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -1606,6 +1619,10 @@ getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } + if (!get_actual_value(variable)->is_valid && strict) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized variable \"%s\"", key))); } else { @@ -1684,12 +1701,13 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, dlist_push_head(&variable->data, &historyEntry->node); if (typid != RECORDOID) { - ScalarVar *scalar = get_actual_value_scalar(variable); + ScalarVar *scalar = &(historyEntry->value.scalar); get_typlenbyval(variable->typid, &scalar->typlen, &scalar->typbyval); - scalar->is_null = true; + historyEntry->value.scalar.is_null = true; } + historyEntry->is_valid = true; } } /* If it is necessary, put variable to changedVars */ @@ -1734,7 +1752,7 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) } else scalar->value = 0; - + history_entry_new->is_valid = history_entry_prev->is_valid; dlist_push_head(history, &history_entry_new->node); MemoryContextSwitchTo(oldcxt); } @@ -1744,9 +1762,10 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) * Remove previous state of variable */ static void -releaseSavepoint(HashVariableEntry *variable) +releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable) { - ValueHistory *history; + ValueHistory *history; + ValueHistoryEntry *historyEntry; history = &variable->data; if (dlist_has_next(history, dlist_head_node(history))) @@ -1773,7 +1792,15 @@ releaseSavepoint(HashVariableEntry *variable) * If variable was changed in subtransaction, so it is considered it * was changed in parent transaction. */ - (get_actual_value(variable)->level)--; + historyEntry = get_actual_value(variable); + historyEntry->level--; + if (!historyEntry->is_valid && + !dlist_has_next(history, dlist_head_node(history))) + { + bool found; + + hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); + } } /* @@ -1805,7 +1832,7 @@ isVarChangedInCurrentTrans(HashVariableEntry *variable) return false; var_state = get_actual_value(variable); - return (var_state->level == GetCurrentTransactionNestLevel()); + return var_state->level == GetCurrentTransactionNestLevel(); } /* @@ -1822,7 +1849,7 @@ isVarChangedInUpperTrans(HashVariableEntry *variable) if(dlist_has_next(&variable->data, &var_state->node)) { var_prev_state = get_history_entry(var_state->node.next); - return (var_prev_state->level == (GetCurrentTransactionNestLevel() - 1)); + return var_prev_state->level == (GetCurrentTransactionNestLevel() - 1); } return false; } @@ -1989,7 +2016,7 @@ levelUpOrRelease() cvn_old = dlist_container(ChangedVarsNode, node, iter.cur); if (isVarChangedInUpperTrans(cvn_old->variable)) - releaseSavepoint(cvn_old->variable); + releaseSavepoint(cvn_old->package, cvn_old->variable); else { ChangedVarsNode *cvn_new; @@ -2038,7 +2065,7 @@ applyActionOnChangedVars(Action action) switch(action) { case RELEASE_SAVEPOINT: - releaseSavepoint(cvn->variable); + releaseSavepoint(cvn->package, cvn->variable); break; case ROLLBACK_TO_SAVEPOINT: rollbackSavepoint(cvn->package, cvn->variable); diff --git a/pg_variables.h b/pg_variables.h index 1357244..3457fea 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -70,6 +70,7 @@ typedef struct ValueHistoryEntry{ } value; /* Transaction nest level of current entry */ int level; + bool is_valid; } ValueHistoryEntry; typedef dlist_head ValueHistory; diff --git a/pg_variables_record.c b/pg_variables_record.c index 7e82e7b..af5bf41 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -367,6 +367,7 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) record_prev = get_actual_value_record(variable); oldcxt = MemoryContextSwitchTo(packageContext); history_entry_new = palloc0(sizeof(ValueHistoryEntry)); + history_entry_new->is_valid = true; record_new = &(history_entry_new->value.record); dlist_push_head(&variable->data, &history_entry_new->node); init_attributes(variable, record_prev->tupdesc, packageContext); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 7a7caeb..ed3a524 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -339,7 +339,7 @@ SELECT pgv_get('vars2', 'any1',NULL::text); SELECT pgv_free(); ---Additional tests +-- 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); @@ -393,3 +393,19 @@ SELECT pgv_get('vars', 'any1',NULL::text); BEGIN; SELECT pgv_set('vars', 'any1', 'wrong type'::varchar, true); COMMIT; + +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); +BEGIN; +SELECT pgv_remove('vars', 'any1'); +SELECT pgv_exists('vars', 'any1'); +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); +SELECT pgv_get('vars', 'any1',NULL::text); + +BEGIN; +SELECT pgv_remove('vars', 'any1'); +SELECT pgv_exists('vars', 'any1'); +COMMIT; +SELECT pgv_exists('vars', 'any1'); +SELECT pgv_get('vars', 'any1',NULL::text); From 5b8b4bc7e3d403addedb3164834b162134e1566d Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 22 May 2018 18:20:09 +0300 Subject: [PATCH 029/147] Add support of package removal rollback --- README.md | 26 +- expected/pg_variables_trans.out | 149 ++++++ pg_variables.c | 853 ++++++++++++++++++++++---------- pg_variables.h | 41 +- sql/pg_variables_trans.sql | 43 ++ 5 files changed, 837 insertions(+), 275 deletions(-) diff --git a/README.md b/README.md index 862153a..84499fb 100644 --- a/README.md +++ b/README.md @@ -335,14 +335,32 @@ SELECT pgv_get('pack','var_int', NULL::int); ERROR: unrecognized variable "var_int" COMMIT; ``` -Also you cannot undo removing variable by `ROLLBACK`: +You can undo removal of a transactional variable by `ROLLBACK`, but if you remove +a whole package, all regular variables will be removed permanently: ```sql -SELECT pgv_set('pack', 'var_int', 122, true); +SELECT pgv_set('pack', 'var_reg', 123); +SELECT pgv_set('pack', 'var_trans', 456, true); BEGIN; SELECT pgv_free(); +SELECT * FROM pgv_list(); + package | name | is_transactional +---------+------+------------------ +(0 rows) + +-- Memory is allocated yet +SELECT * FROM pgv_stats(); + package | allocated_memory +---------+------------------ + pack | 24576 +(1 row) + ROLLBACK; -SELECT pgv_get('pack', 'var_int', NULL::int); -ERROR: unrecognized package "pack" +SELECT * FROM pgv_list(); + package | name | is_transactional +---------+-----------+------------------ + pack | var_trans | t +(1 row) + ``` 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()`, diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 89b5fcb..27a7452 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1653,3 +1653,152 @@ SELECT pgv_exists('vars', 'any1'); SELECT pgv_get('vars', 'any1',NULL::text); ERROR: unrecognized variable "any1" +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans2 | t +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp4; +SAVEPOINT sp5; +--SELECT pgv_remove('vars2'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_stats(); + pgv_stats +--------------- + (vars2,24576) + (vars,24576) +(2 rows) + +SELECT pgv_list(); + pgv_list +---------- +(0 rows) + +RELEASE sp5; +SELECT pgv_stats(); + pgv_stats +--------------- + (vars2,24576) + (vars,24576) +(2 rows) + +SELECT pgv_list(); + pgv_list +---------- +(0 rows) + +RELEASE sp4; +SELECT pgv_stats(); + pgv_stats +-------------- + (vars,24576) +(1 row) + +SELECT pgv_list(); + pgv_list +---------- +(0 rows) + +COMMIT; +SELECT pgv_stats(); + pgv_stats +----------- +(0 rows) + diff --git a/pg_variables.c b/pg_variables.c index e11c1cd..429e210 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -50,21 +50,35 @@ static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); -static HashVariableEntry *getVariableInternal(HTAB *variables, text *name, - Oid typid, bool strict); +static HashVariableEntry *getVariableInternal(HashPackageEntry *package, + text *name, Oid typid, + bool strict); static HashVariableEntry *createVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool is_transactional); +static void removePackageInternal(HashPackageEntry *package); -static void releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable); -static void rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable); -static void createSavepoint(HashPackageEntry *package, HashVariableEntry *variable); +static void releaseSavepointPack(HashPackageEntry *package); +static void rollbackSavepointPack(HashPackageEntry *package); +static void createSavepointPack(HashPackageEntry *package); +static void releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); +static void rollbackSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); +static void createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); -static void mergeChangedVarsStack(void); -static void pushChangedVarsStack(void); -static void popChangedVarsStack(void); +static void pushChangesStack(void); +static void addToChangedPacks(HashPackageEntry *package); static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); +static void removeFromChangedVars(HashPackageEntry *package); +static inline ChangedPacksNode * +makeChangedPacksNode(MemoryContext ctx, HashPackageEntry *package); +static inline ChangedVarsNode * +makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable); +static void +makePackHTAB(HashPackageEntry *package, bool is_trans); + +static bool isPackChangedInCurrentTrans(HashPackageEntry *package); +static bool isPackChangedInUpperTrans(HashPackageEntry *package); static bool isVarChangedInCurrentTrans(HashVariableEntry *variable); static bool isVarChangedInUpperTrans(HashVariableEntry *variable); @@ -89,23 +103,23 @@ static HashPackageEntry *LastPackage = NULL; static HashVariableEntry *LastVariable = NULL; -/* This stack contains lists of changed variables per each subxact level */ -static dlist_head *changedVarsStack = NULL; -static MemoryContext changedVarsContext = NULL; +/* This stack contains lists of changed variables and packages per each subxact level */ +static dlist_head *changesStack = NULL; +static MemoryContext changesStackContext = NULL; /* Returns a list of of vars changed at current subxact level */ #define get_actual_changed_vars_list() \ ( \ - AssertMacro(changedVarsStack != NULL), \ - (dlist_head_element(ChangedVarsStackNode, \ - node, changedVarsStack))->changedVarsList \ + AssertMacro(changesStack != NULL), \ + (dlist_head_element(ChangesStackNode, \ + node, changesStack))->changedVarsList \ ) #define PGV_MCXT_MAIN "pg_variables: main memory context" #define PGV_MCXT_VARS "pg_variables: variables hash" -#define PGV_MCXT_STACK "pg_variables: changedVarsStack" -#define PGV_MCXT_STACK_NODE "pg_variables: changedVarsStackNode" +#define PGV_MCXT_STACK "pg_variables: changesStack" +#define PGV_MCXT_STACK_NODE "pg_variables: changesStackNode" #ifndef ALLOCSET_DEFAULT_SIZES @@ -144,7 +158,9 @@ variable_set(text *package_name, text *var_name, scalar->is_null = is_null; if (!scalar->is_null) { - oldcxt = MemoryContextSwitchTo(package->hctx); + oldcxt = MemoryContextSwitchTo(is_transactional ? + package->hctxTransact : + package->hctxRegular); scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); MemoryContextSwitchTo(oldcxt); } @@ -167,8 +183,7 @@ variable_get(text *package_name, text *var_name, return 0; } - variable = getVariableInternal(package->variablesHash, - var_name, typid, strict); + variable = getVariableInternal(package, var_name, typid, strict); if (variable == NULL) { @@ -328,7 +343,7 @@ variable_insert(PG_FUNCTION_ARGS) } if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { - createSavepoint(package, variable); + createSavepointVar(package, variable); addToChangedVars(package, variable); } } @@ -343,7 +358,8 @@ variable_insert(PG_FUNCTION_ARGS) /* * This is the first record for the var_name. Initialize attributes. */ - init_attributes(variable, tupdesc, package->hctx); + init_attributes(variable, tupdesc, is_transactional ? + package->hctxTransact : package->hctxRegular); } else check_attributes(variable, tupdesc); @@ -405,8 +421,7 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); LastVariable = variable; } else @@ -414,7 +429,7 @@ variable_update(PG_FUNCTION_ARGS) if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { - createSavepoint(package, variable); + createSavepointVar(package, variable); addToChangedVars(package, variable); } @@ -483,8 +498,7 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), LastVariable->name, VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); LastVariable = variable; } else @@ -492,7 +506,7 @@ variable_delete(PG_FUNCTION_ARGS) if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) { - createSavepoint(package, variable); + createSavepointVar(package, variable); addToChangedVars(package, variable); } @@ -531,8 +545,7 @@ variable_select(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); record = get_actual_value_record(variable); @@ -604,8 +617,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) } package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); if (!value_is_null) check_record_key(variable, value_type); @@ -674,8 +686,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package->variablesHash, - var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -747,7 +758,7 @@ cleanVariableCurrentState(HashVariableEntry *variable) pfree(DatumGetPointer(scalar->value)); } - historyEntryToDelete = get_history_entry(dlist_pop_head_node(&variable->data)); + historyEntryToDelete = get_var_history_entry(dlist_pop_head_node(&variable->data)); pfree(historyEntryToDelete); } @@ -793,13 +804,16 @@ variable_exists(PG_FUNCTION_ARGS) getKeyFromName(var_name, key); - variable = (HashVariableEntry *) hash_search(package->variablesHash, + variable = (HashVariableEntry *) hash_search(package->varHashRegular, key, HASH_FIND, &found); + if (!found) + variable = (HashVariableEntry *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found?get_actual_value(variable)->is_valid:found); + PG_RETURN_BOOL(found ? get_actual_var_state(variable)->is_valid : found); } /* @@ -845,31 +859,32 @@ remove_variable(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); getKeyFromName(var_name, key); - variable = (HashVariableEntry *) hash_search(package->variablesHash, - key, HASH_FIND, &found); - if (!found) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized variable \"%s\"", key))); + variable = (HashVariableEntry *) hash_search(package->varHashRegular, + key, HASH_REMOVE, &found); + if (found) + /* Regular variable */ + cleanVariableAllStates(variable); else { - if (variable->is_transactional) + variable = (HashVariableEntry *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); + /* Variable doesn't exist in both HTAB */ + if (!found) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized variable \"%s\"", key))); + /* Transactional variable */ + if (!isVarChangedInCurrentTrans(variable)) { - createSavepoint(package, variable); + createSavepointVar(package, variable); addToChangedVars(package, variable); - get_actual_value(variable)->is_valid = false; - } - else - { - /* Remove variable from package */ - hash_search(package->variablesHash, key, HASH_REMOVE, &found); - /* Free allocated memory */ - cleanVariableAllStates(variable); } - /* Remove variable from cache */ - LastVariable = NULL; + get_actual_var_state(variable)->is_valid = false; } + /* Remove variable from cache */ + LastVariable = NULL; + PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -882,9 +897,8 @@ remove_variable(PG_FUNCTION_ARGS) Datum remove_package(PG_FUNCTION_ARGS) { + HashPackageEntry *package; text *package_name; - HashPackageEntry *package; - bool found; char key[NAMEDATALEN]; if (PG_ARGISNULL(0)) @@ -893,53 +907,66 @@ remove_package(PG_FUNCTION_ARGS) errmsg("package name can not be NULL"))); package_name = PG_GETARG_TEXT_PP(0); - getKeyFromName(package_name, key); - - if (!packagesHash) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); - package = (HashPackageEntry *) hash_search(packagesHash, key, - HASH_REMOVE, &found); - if (!found) + package = getPackageByName(package_name, false, true); + if (package) + removePackageInternal(package); + else + { + getKeyFromName(package_name, key); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized package \"%s\"", key))); + } /* Remove package and variable from cache */ LastPackage = NULL; LastVariable = NULL; - /* All variables will be freed */ - MemoryContextDelete(package->hctx); - PG_FREE_IF_COPY(package_name, 0); - PG_RETURN_VOID(); } +static void +removePackageInternal(HashPackageEntry *package) +{ + /* All regular variables will be freed */ + MemoryContextDelete(package->hctxRegular); + + /* Add to changes list */ + if (!isPackChangedInCurrentTrans(package)) + { + createSavepointPack(package); + addToChangedPacks(package); + } + get_actual_pack_state(package)->is_valid = false; +} + /* * Remove all packages and variables. + * Memory context will be released after committing. */ Datum remove_packages(PG_FUNCTION_ARGS) { - /* There is not any packages and variables */ + HashPackageEntry *package; + HASH_SEQ_STATUS pstat; + /* There is no any packages and variables */ if (packagesHash == NULL) PG_RETURN_VOID(); + + /* Get packages list */ + hash_seq_init(&pstat, packagesHash); + while ((package = + (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) + { + removePackageInternal(package); + } /* Remove package and variable from cache */ LastPackage = NULL; LastVariable = NULL; - /* All packages and variables will be freed */ - MemoryContextDelete(ModuleContext); - - packagesHash = NULL; - ModuleContext = NULL; - changedVarsStack = NULL; - PG_RETURN_VOID(); } @@ -1000,23 +1027,32 @@ get_packages_and_variables(PG_FUNCTION_ARGS) HashVariableEntry *variable; HASH_SEQ_STATUS vstat; + /* Skip packages marked as deleted */ + if (!get_actual_pack_state(package)->is_valid) + continue; /* Get variables list for package */ - hash_seq_init(&vstat, package->variablesHash); - while ((variable = - (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) + for(int i=0; i < 2; i++) { - /* Resize recs if necessary */ - if (nRecs >= mRecs) + hash_seq_init(&vstat, i ? package->varHashTransact : + package->varHashRegular); + while ((variable = + (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) { - mRecs *= 2; - recs = (VariableRec *) repalloc(recs, - sizeof(VariableRec) * mRecs); + if (!get_actual_var_state(variable)->is_valid) + continue; + /* Resize recs if necessary */ + if (nRecs >= mRecs) + { + mRecs *= 2; + recs = (VariableRec *) repalloc(recs, + sizeof(VariableRec) * mRecs); + } + + recs[nRecs].package = package->name; + recs[nRecs].variable = variable->name; + recs[nRecs].is_transactional = variable->is_transactional; + nRecs++; } - - recs[nRecs].package = package->name; - recs[nRecs].variable = variable->name; - recs[nRecs].is_transactional = variable->is_transactional; - nRecs++; } } @@ -1148,15 +1184,19 @@ get_packages_stats(PG_FUNCTION_ARGS) bool nulls[2]; HeapTuple tuple; Datum result; - Size totalspace = 0; + Size totalSpace = 0, + regularSpace = 0, + transactSpace = 0; memset(nulls, 0, sizeof(nulls)); /* Fill data */ values[0] = PointerGetDatum(cstring_to_text(package->name)); - getMemoryTotalSpace(package->hctx, 0, &totalspace); - values[1] = Int64GetDatum(totalspace); + getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); + getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); + totalSpace = regularSpace + transactSpace; + values[1] = Int64GetDatum(totalSpace); /* Data are ready */ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); @@ -1211,10 +1251,44 @@ ensurePackagesHashExists(void) HASH_ELEM | HASH_CONTEXT); } +/* + * Initialize a hash table with proper vars type + */ +static void +makePackHTAB(HashPackageEntry *package, bool is_trans) +{ + HASHCTL ctl; + char key[NAMEDATALEN], + hash_name[BUFSIZ]; + + if (is_trans) + package->hctxTransact = AllocSetContextCreate(ModuleContext, + PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); + else + package->hctxRegular = AllocSetContextCreate(ModuleContext, + PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); + sprintf(hash_name, "%s variables hash for package \"%s\"", + is_trans ? "Transactional" : "Regular", key); + ctl.keysize = NAMEDATALEN; + ctl.entrysize = sizeof(HashVariableEntry); + ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); + if (is_trans) + package->varHashTransact = hash_create(hash_name, + NUMVARIABLES, &ctl, + HASH_ELEM | HASH_CONTEXT); + else + package->varHashRegular = hash_create(hash_name, + NUMVARIABLES, &ctl, + HASH_ELEM | HASH_CONTEXT); +} + static HashPackageEntry * getPackageByName(text* name, bool create, bool strict) { HashPackageEntry *package; + PackHistoryEntry *historyEntry; char key[NAMEDATALEN]; bool found; @@ -1237,36 +1311,68 @@ getPackageByName(text* name, bool create, bool strict) /* Find or create a package entry */ package = (HashPackageEntry *) hash_search(packagesHash, key, - (create ? HASH_ENTER : HASH_FIND), + create ? HASH_ENTER : HASH_FIND, &found); - /* Package entry was created, so we need create hash table for variables. */ - if (!found) + if (found) { - if (create) + if (get_actual_pack_state(package)->is_valid) + return package; + else if (create) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - - package->hctx = AllocSetContextCreate(ModuleContext, - PGV_MCXT_VARS, - ALLOCSET_DEFAULT_SIZES); - - sprintf(hash_name, "Variables hash for package \"%s\"", key); - - ctl.keysize = NAMEDATALEN; - ctl.entrysize = sizeof(HashVariableEntry); - ctl.hcxt = package->hctx; - package->variablesHash = hash_create(hash_name, - NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); + HASH_SEQ_STATUS vstat; + HashVariableEntry *variable; + /* Make new history entry of package */ + if (!isPackChangedInCurrentTrans(package)) + { + createSavepointPack(package); + addToChangedPacks(package); + } + get_actual_pack_state(package)->is_valid = true; + /* Restore previously removed HTAB for regular variables */ + makePackHTAB(package, false); + /* Mark all transactional variables in package as removed */ + hash_seq_init(&vstat, package->varHashTransact); + while ((variable = + (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) + { + if (!isVarChangedInCurrentTrans(variable)) + { + createSavepointVar(package, variable); + addToChangedVars(package, variable); + } + get_actual_var_state(variable)->is_valid = false; + } + return package; } - else if (strict) + if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + errmsg("unrecognized package \"%s\"", key))); + else + return NULL; + } + if (!create) + { + if (strict) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized package \"%s\"", key))); + else + return package; } + /* Package entry was created, so we need create hash table for variables. */ + makePackHTAB(package, false); + makePackHTAB(package, true); + /* Initialize history */ + dlist_init(&package->packHistory); + historyEntry = MemoryContextAllocZero(ModuleContext, + sizeof(PackHistoryEntry)); + dlist_push_head(&package->packHistory, &historyEntry->node); + historyEntry->is_valid = true; + /* Add to changes list */ + addToChangedPacks(package); return package; } @@ -1276,7 +1382,7 @@ getPackageByName(text* name, bool create, bool strict) * flag 'is_transactional' of this variable is unknown. */ static HashVariableEntry * -getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) +getVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool strict) { HashVariableEntry *variable; char key[NAMEDATALEN]; @@ -1284,8 +1390,11 @@ getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) getKeyFromName(name, key); - variable = (HashVariableEntry *) hash_search(variables, + variable = (HashVariableEntry *) hash_search(package->varHashRegular, key, HASH_FIND, &found); + if (!found) + variable = (HashVariableEntry *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); /* Check variable type */ if (found) @@ -1300,7 +1409,7 @@ getVariableInternal(HTAB *variables, text *name, Oid typid, bool strict) errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } - if (!get_actual_value(variable)->is_valid && strict) + if (!get_actual_var_state(variable)->is_valid && strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); @@ -1331,7 +1440,17 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, getKeyFromName(name, key); - variable = (HashVariableEntry *) hash_search(package->variablesHash, + hash_search(is_transactional ? package->varHashRegular : package->varHashTransact, + key, HASH_FIND, &found); + if (found) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + key, is_transactional ? "NOT " : ""))); + + variable = (HashVariableEntry *) hash_search(is_transactional ? + package->varHashTransact : + package->varHashRegular, key, HASH_ENTER, &found); /* Check variable type */ @@ -1348,49 +1467,41 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, 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 "))); - } - /* * Savepoint must be created when variable changed in current * transaction. * For each transaction level there should be a corresponding savepoint. * New value should be stored in a last state. */ - if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) + if (is_transactional && !isVarChangedInCurrentTrans(variable)) { - createSavepoint(package, variable); + createSavepointVar(package, variable); } } else { + ValueHistoryEntry *historyEntry; + /* Variable entry was created, so initialize new variable. */ - if (variable) + Assert(variable); + variable->typid = typid; + variable->is_transactional = is_transactional; + dlist_init(&variable->data); + historyEntry = MemoryContextAllocZero(is_transactional ? + package->hctxTransact : + package->hctxRegular, + sizeof(ValueHistoryEntry)); + + dlist_push_head(&variable->data, &historyEntry->node); + if (typid != RECORDOID) { - ValueHistoryEntry *historyEntry; + ScalarVar *scalar = &(historyEntry->value.scalar); - variable->typid = typid; - variable->is_transactional = is_transactional; - dlist_init(&(variable->data)); - historyEntry = MemoryContextAllocZero(package->hctx, - sizeof(ValueHistoryEntry)); - - dlist_push_head(&variable->data, &historyEntry->node); - if (typid != RECORDOID) - { - ScalarVar *scalar = &(historyEntry->value.scalar); - - get_typlenbyval(variable->typid, &scalar->typlen, - &scalar->typbyval); - historyEntry->value.scalar.is_null = true; - } - historyEntry->is_valid = true; + get_typlenbyval(variable->typid, &scalar->typlen, + &scalar->typbyval); + historyEntry->value.scalar.is_null = true; } + historyEntry->is_valid = true; } /* If it is necessary, put variable to changedVars */ if (is_transactional) @@ -1404,12 +1515,12 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, * previous state */ static void -createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) +createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) { if (variable->typid == RECORDOID) { - insert_savepoint(variable, package->hctx); + insert_savepoint(variable, package->hctxTransact); } else { @@ -1419,7 +1530,7 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) *history_entry_prev; MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(package->hctx); + oldcxt = MemoryContextSwitchTo(package->hctxTransact); history = &variable->data; /* Release memory for variable */ @@ -1445,7 +1556,7 @@ createSavepoint(HashPackageEntry *package, HashVariableEntry *variable) * Remove previous state of variable */ static void -releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable) +releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) { ValueHistory *history; ValueHistoryEntry *historyEntry; @@ -1457,7 +1568,7 @@ releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable) dlist_node *nodeToDelete; nodeToDelete = dlist_next_node(history, dlist_head_node(history)); - historyEntryToDelete = get_history_entry(nodeToDelete); + historyEntryToDelete = get_var_history_entry(nodeToDelete); if (variable->typid == RECORDOID) { @@ -1474,14 +1585,14 @@ releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable) pfree(historyEntryToDelete); } /* Change subxact level due to release */ - historyEntry = get_actual_value(variable); + historyEntry = get_actual_var_state(variable); historyEntry->level--; - if (!historyEntry->is_valid && + if (!historyEntry->is_valid && !dlist_has_next(history, dlist_head_node(history))) { bool found; - hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); + hash_search(package->varHashTransact, variable->name, HASH_REMOVE, &found); } } @@ -1489,7 +1600,7 @@ releaseSavepoint(HashPackageEntry *package, HashVariableEntry *variable) * Rollback variable to previous state and remove current value */ static void -rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) +rollbackSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) { cleanVariableCurrentState(variable); @@ -1498,15 +1609,118 @@ rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable) { bool found; - hash_search(package->variablesHash, variable->name, HASH_REMOVE, &found); + hash_search(package->varHashTransact, variable->name, HASH_REMOVE, &found); + } +} + +/* + * Create a new history point of variable and copy value from + * previous state + */ +static void +createSavepointPack(HashPackageEntry *package) +{ + PackHistory *history; + PackHistoryEntry *history_entry_new, + *history_entry_prev; + + history = &package->packHistory; + history_entry_new = MemoryContextAllocZero(ModuleContext, sizeof(PackHistoryEntry)); + history_entry_prev = dlist_head_element(PackHistoryEntry, node, history); + history_entry_new->is_valid = history_entry_prev->is_valid; + dlist_push_head(history, &history_entry_new->node); +} + +/* + * Remove previous state of variable + */ +static void +releaseSavepointPack(HashPackageEntry *package) +{ + PackHistory *history; + PackHistoryEntry *historyEntry; + + Assert(get_actual_pack_state(package)->level == + GetCurrentTransactionNestLevel()); + history = &package->packHistory; + + /* Package existed in parent transaction */ + if (dlist_has_next(history, dlist_head_node(history))) + { + PackHistoryEntry *historyEntryToDelete; + dlist_node *nodeToDelete; + + nodeToDelete = dlist_next_node(history, dlist_head_node(history)); + historyEntryToDelete = dlist_container(PackHistoryEntry, node, nodeToDelete); + + dlist_delete(nodeToDelete); + pfree(historyEntryToDelete); + } + /* Package has no previous states and can be completely removed if necessary*/ + if (!get_actual_pack_state(package)->is_valid && + !dlist_has_next(history, dlist_head_node(history))) + { + bool found; + /* Regular variables had already removed */ + MemoryContextDelete(package->hctxTransact); + /* Remove package from packagesHash */ + hash_search(packagesHash, package->name, HASH_REMOVE, &found); + /* + *Delete a variable from the change history of the overlying + *transaction level. + */ + if (!dlist_is_empty(changesStack)) + removeFromChangedVars(package); + } + + /* Change subxact level due to release */ + else + { + historyEntry = get_actual_pack_state(package); + historyEntry->level--; } } +/* + * Rollback variable to previous state and remove current value + */ +static void +rollbackSavepointPack(HashPackageEntry *package) +{ + PackHistoryEntry *historyEntryToDelete; + + historyEntryToDelete = get_actual_pack_state(package); + if (historyEntryToDelete->is_valid) + releaseSavepointPack(package); + else + { + dlist_pop_head_node(&package->packHistory); + /* Create regular vars HTAB if it was removed */ + if (!historyEntryToDelete->is_valid) + makePackHTAB(package, false); + pfree(historyEntryToDelete); + } +} + +/* + * Initialize an instance of ChangedPacksNode datatype + */ +static inline ChangedPacksNode * +makeChangedPacksNode(MemoryContext ctx, HashPackageEntry *package) +{ + ChangedPacksNode *cpn; + + cpn = MemoryContextAllocZero(ctx, sizeof(ChangedPacksNode)); + cpn->package = package; + return cpn; +} + /* * Initialize an instance of ChangedVarsNode datatype */ static inline ChangedVarsNode * -makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable) +makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, + HashVariableEntry *variable) { ChangedVarsNode *cvn; @@ -1516,6 +1730,41 @@ makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEn return cvn; } +/* + * Check if package was changed in current transaction level + */ +static bool +isPackChangedInCurrentTrans(HashPackageEntry *package) +{ + PackHistoryEntry *pack_state; + + if (!changesStack) + return false; + + pack_state = get_actual_pack_state(package); + return pack_state->level == GetCurrentTransactionNestLevel(); +} + +/* + * Check if variable was changed in parent transaction level + */ +static bool +isPackChangedInUpperTrans(HashPackageEntry *package) +{ + PackHistoryEntry *state, + *prev_state; + + state = get_actual_pack_state(package); + + if (dlist_has_next(&package->packHistory, &state->node)) + { + prev_state = get_pack_history_entry(state->node.next); + return prev_state->level == GetCurrentTransactionNestLevel() - 1; + } + + return false; +} + /* * Check if variable was changed in current transaction level */ @@ -1524,10 +1773,10 @@ isVarChangedInCurrentTrans(HashVariableEntry *variable) { ValueHistoryEntry *var_state; - if (!changedVarsStack) + if (!changesStack) return false; - var_state = get_actual_value(variable); + var_state = get_actual_var_state(variable); return var_state->level == GetCurrentTransactionNestLevel(); } @@ -1540,11 +1789,11 @@ isVarChangedInUpperTrans(HashVariableEntry *variable) ValueHistoryEntry *var_state, *var_prev_state; - var_state = get_actual_value(variable); + var_state = get_actual_var_state(variable); if (dlist_has_next(&variable->data, &var_state->node)) { - var_prev_state = get_history_entry(var_state->node.next); + var_prev_state = get_var_history_entry(var_state->node.next); return var_prev_state->level == GetCurrentTransactionNestLevel() - 1; } @@ -1555,110 +1804,73 @@ isVarChangedInUpperTrans(HashVariableEntry *variable) * Create a new list of variables, changed in current transaction level */ static void -pushChangedVarsStack(void) +pushChangesStack(void) { - MemoryContext oldcxt; - ChangedVarsStackNode *cvsn; + MemoryContext oldcxt; + ChangesStackNode *csn; /* - * Initialize changedVarsStack and create MemoryContext for it + * Initialize changesStack and create MemoryContext for it * if not done before. */ - if (!changedVarsContext) - changedVarsContext = AllocSetContextCreate(ModuleContext, + if (!changesStackContext) + changesStackContext = AllocSetContextCreate(ModuleContext, PGV_MCXT_STACK, ALLOCSET_START_SMALL_SIZES); - oldcxt = MemoryContextSwitchTo(changedVarsContext); + oldcxt = MemoryContextSwitchTo(changesStackContext); - if (!changedVarsStack) + if (!changesStack) { - changedVarsStack = palloc0(sizeof(dlist_head)); - dlist_init(changedVarsStack); + changesStack = palloc0(sizeof(dlist_head)); + dlist_init(changesStack); } - cvsn = palloc0(sizeof(ChangedVarsStackNode)); - cvsn->changedVarsList = palloc0(sizeof(dlist_head)); + csn = palloc0(sizeof(ChangesStackNode)); + csn->changedVarsList = palloc0(sizeof(dlist_head)); + csn->changedPacksList = palloc0(sizeof(dlist_head)); - cvsn->ctx = AllocSetContextCreate(changedVarsContext, + csn->ctx = AllocSetContextCreate(changesStackContext, PGV_MCXT_STACK_NODE, ALLOCSET_START_SMALL_SIZES); - dlist_init(cvsn->changedVarsList); - dlist_push_head(changedVarsStack, &cvsn->node); + dlist_init(csn->changedVarsList); + dlist_init(csn->changedPacksList); + dlist_push_head(changesStack, &csn->node); MemoryContextSwitchTo(oldcxt); } /* - * Remove current list of variables, changed in current transaction level + * Add a package to list of created or removed packs in current transaction level */ static void -popChangedVarsStack(void) +addToChangedPacks(HashPackageEntry *package) { - if (changedVarsStack) + ChangesStackNode *csn; + + if (!changesStack) { - ChangedVarsStackNode *cvsn; + int level = GetCurrentTransactionNestLevel(); - Assert(!dlist_is_empty(changedVarsStack)); - cvsn = dlist_container(ChangedVarsStackNode, node, - dlist_pop_head_node(changedVarsStack)); - MemoryContextDelete(cvsn->ctx); - if (dlist_is_empty(changedVarsStack)) + while (level-- > 0) { - MemoryContextDelete(changedVarsContext); - changedVarsStack = NULL; - changedVarsContext = NULL; + pushChangesStack(); } } -} - -/* - * Pop current list of variables and add missing changed vars to upper list - */ -static void -mergeChangedVarsStack(void) -{ - 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(changesStack && changesStackContext); - /* There must be at least one parent level */ - Assert(!dlist_is_empty(changedVarsStack)); + if (!isPackChangedInCurrentTrans(package)) + { + ChangedPacksNode *cpn; - dlist_foreach(iter, bottom_list->changedVarsList) - { - ChangedVarsNode *cvn_old = dlist_container(ChangedVarsNode, node, iter.cur); + csn = dlist_head_element(ChangesStackNode, node, changesStack); + cpn = makeChangedPacksNode(csn->ctx, package); + dlist_push_head(csn->changedPacksList, &cpn->node); - /* Did this variable change at parent level? */ - if (isVarChangedInUpperTrans(cvn_old->variable)) - { - /* We just have to drop this state */ - releaseSavepoint(cvn_old->package, cvn_old->variable); - } - else - { - ChangedVarsNode *cvn_new; - ChangedVarsStackNode *cvsn; - - /* - * Impossible to push in upper list existing node because - * it was created in another context - */ - cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn_new = makeChangedVarsNode(cvsn->ctx, cvn_old->package, cvn_old->variable); - dlist_push_head(cvsn->changedVarsList, &cvn_new->node); - - /* Change subxact level due to release */ - get_actual_value(cvn_new->variable)->level--; - } - } - MemoryContextDelete(bottom_list->ctx); + /* Give this package current subxact level */ + get_actual_pack_state(cpn->package)->level = GetCurrentTransactionNestLevel(); } } @@ -1668,30 +1880,48 @@ mergeChangedVarsStack(void) static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { - ChangedVarsStackNode *cvsn; + ChangesStackNode *csn; - if (!changedVarsStack) + if (!changesStack) { int level = GetCurrentTransactionNestLevel(); while (level-- > 0) { - pushChangedVarsStack(); + pushChangesStack(); } } - Assert(changedVarsStack && changedVarsContext); + Assert(changesStack && changesStackContext); if (!isVarChangedInCurrentTrans(variable)) { ChangedVarsNode *cvn; - cvsn = dlist_head_element(ChangedVarsStackNode, node, changedVarsStack); - cvn = makeChangedVarsNode(cvsn->ctx, package, variable); - dlist_push_head(cvsn->changedVarsList, &cvn->node); + csn = dlist_head_element(ChangesStackNode, node, changesStack); + cvn = makeChangedVarsNode(csn->ctx, package, variable); + dlist_push_head(csn->changedVarsList, &cvn->node); /* Give this variable current subxact level */ - get_actual_value(cvn->variable)->level = GetCurrentTransactionNestLevel(); + get_actual_var_state(cvn->variable)->level = GetCurrentTransactionNestLevel(); + } +} + +/* + * Remove from the changes list the variables of the deleted package + */ +static void +removeFromChangedVars(HashPackageEntry *package) +{ + dlist_mutable_iter var_miter; + dlist_head *changedVarsList; + + changedVarsList = get_actual_changed_vars_list(); + dlist_foreach_modify(var_miter, changedVarsList) + { + ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, var_miter.cur); + if (cvn_cur->package == package) + dlist_delete(&cvn_cur->node); } } @@ -1706,29 +1936,131 @@ typedef enum Action } Action; /* - * Iterate variables from list of changes and + * Iterate variables and packages from list of changes and * apply corresponding action on them */ static void -applyActionOnChangedVars(Action action) +proceedChanges(Action action) { - dlist_head *changedVars = get_actual_changed_vars_list(); - dlist_mutable_iter miter; - dlist_foreach_modify(miter, changedVars) + ChangesStackNode *bottom_list; + dlist_iter var_iter, + pack_iter; + + Assert(changesStack && changesStackContext); + /* List removed from stack but we still can use it */ + bottom_list = dlist_container(ChangesStackNode, node, + dlist_pop_head_node(changesStack)); + + /* Proceed variables first */ + dlist_foreach(var_iter, bottom_list->changedVarsList) { - ChangedVarsNode *cvn = dlist_container(ChangedVarsNode, node, miter.cur); + ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, var_iter.cur); - switch(action) + switch (action) { + case ROLLBACK_TO_SAVEPOINT: + rollbackSavepointVar(cvn_cur->package, cvn_cur->variable); + break; case RELEASE_SAVEPOINT: - releaseSavepoint(cvn->package, cvn->variable); + /* + * If package was removed in current transaction level + * mark var as removed. + * We do not check pack_state->level, because var cannot get in + * list of changes until pack is removed. + */ + if (!get_actual_pack_state(cvn_cur->package)->is_valid) + get_actual_var_state(cvn_cur->variable)->is_valid = false; + + /* Did this variable change at parent level? */ + if (dlist_is_empty(changesStack) || + isVarChangedInUpperTrans(cvn_cur->variable)) + { + /* We just have to drop previous state */ + releaseSavepointVar(cvn_cur->package, cvn_cur->variable); + } + else + { + ChangedVarsNode *cvn_new; + ChangesStackNode *csn; + + /* + * Impossible to push in upper list existing node because + * it was created in another context + */ + csn = dlist_head_element(ChangesStackNode, node, changesStack); + cvn_new = makeChangedVarsNode(csn->ctx, cvn_cur->package, + cvn_cur->variable); + dlist_push_head(csn->changedVarsList, &cvn_new->node); + + /* Change subxact level due to release */ + get_actual_var_state(cvn_new->variable)->level--; + } break; + } + } + + /* Proceed packages */ + dlist_foreach(pack_iter, bottom_list->changedPacksList) + { + ChangedPacksNode *cpn_cur = dlist_container(ChangedPacksNode, node, + pack_iter.cur); + + switch (action) + { case ROLLBACK_TO_SAVEPOINT: - rollbackSavepoint(cvn->package, cvn->variable); + if (!get_actual_pack_state(cpn_cur->package)->is_valid) + { + rollbackSavepointPack(cpn_cur->package); + break; + } + case RELEASE_SAVEPOINT: + /* Did this package change at parent level? */ + if (dlist_is_empty(changesStack) || + isPackChangedInUpperTrans(cpn_cur->package)) + { + /* We just have to drop previous state */ + releaseSavepointPack(cpn_cur->package); + } + else + { + ChangedPacksNode *cpn_new; + ChangesStackNode *csn; + + /* + * Impossible to push in upper list existing node because + * it was created in another context + */ + csn = dlist_head_element(ChangesStackNode, node, changesStack); + cpn_new = makeChangedPacksNode(csn->ctx, cpn_cur->package); + dlist_push_head(csn->changedPacksList, &cpn_new->node); + + /* Change subxact level due to release */ + get_actual_pack_state(cpn_new->package)->level--; + } break; } } + + /* Remove changes list of current level */ + MemoryContextDelete(bottom_list->ctx); + /* Remove the stack if it is empty */ + if (dlist_is_empty(changesStack)) + { + MemoryContextDelete(changesStackContext); + changesStack = NULL; + changesStackContext = NULL; + } + if (!hash_get_num_entries(packagesHash)) + { + MemoryContextDelete(ModuleContext); + packagesHash = NULL; + ModuleContext = NULL; + LastPackage = NULL; + LastVariable = NULL; + changesStack = NULL; + changesStackContext = NULL; + } } /* @@ -1738,19 +2070,18 @@ static void pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { - if (changedVarsStack) + if (changesStack) { switch (event) { case SUBXACT_EVENT_START_SUB: - pushChangedVarsStack(); + pushChangesStack(); break; case SUBXACT_EVENT_COMMIT_SUB: - mergeChangedVarsStack(); + proceedChanges(RELEASE_SAVEPOINT); break; case SUBXACT_EVENT_ABORT_SUB: - applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); - popChangedVarsStack(); + proceedChanges(ROLLBACK_TO_SAVEPOINT); break; case SUBXACT_EVENT_PRE_COMMIT_SUB: break; @@ -1764,25 +2095,21 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, static void pgvTransCallback(XactEvent event, void *arg) { - if (changedVarsStack) + if (changesStack) { switch (event) { case XACT_EVENT_PRE_COMMIT: - applyActionOnChangedVars(RELEASE_SAVEPOINT); - popChangedVarsStack(); + proceedChanges(RELEASE_SAVEPOINT); break; case XACT_EVENT_ABORT: - applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); - popChangedVarsStack(); + proceedChanges(ROLLBACK_TO_SAVEPOINT); break; case XACT_EVENT_PARALLEL_PRE_COMMIT: - applyActionOnChangedVars(RELEASE_SAVEPOINT); - popChangedVarsStack(); + proceedChanges(RELEASE_SAVEPOINT); break; case XACT_EVENT_PARALLEL_ABORT: - applyActionOnChangedVars(ROLLBACK_TO_SAVEPOINT); - popChangedVarsStack(); + proceedChanges(ROLLBACK_TO_SAVEPOINT); break; default: break; diff --git a/pg_variables.h b/pg_variables.h index 3457fea..2a3ab2b 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -32,12 +32,25 @@ #define NUMPACKAGES 8 #define NUMVARIABLES 16 +/* List node that stores one of the package states */ +typedef struct PackHistoryEntry{ + dlist_node node; + bool is_valid; + int level; +} PackHistoryEntry; + +typedef dlist_head PackHistory; + typedef struct HashPackageEntry { char name[NAMEDATALEN]; - HTAB *variablesHash; + HTAB *varHashRegular, + *varHashTransact; /* Memory context for package variables for easy memory release */ - MemoryContext hctx; + MemoryContext hctxRegular, + hctxTransact; + PackHistory packHistory; + } HashPackageEntry; typedef struct RecordVar @@ -113,13 +126,21 @@ typedef struct ChangedVarsNode HashVariableEntry *variable; } ChangedVarsNode; -/* Element of stack with 'changedVars' list heads*/ -typedef struct ChangedVarsStackNode +/* Element of list with packages, removed within transaction */ +typedef struct ChangedPacksNode +{ + dlist_node node; + HashPackageEntry *package; +} ChangedPacksNode; + +/* Element of stack with 'changedVars' and 'changedPacks' list heads*/ +typedef struct ChangesStackNode { dlist_node node; - dlist_head *changedVarsList; + dlist_head *changedVarsList; + dlist_head *changedPacksList; MemoryContext ctx; -} ChangedVarsStackNode; +} ChangesStackNode; extern void init_attributes(HashVariableEntry* variable, TupleDesc tupdesc, MemoryContext topctx); @@ -142,9 +163,13 @@ extern void insert_savepoint(HashVariableEntry *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_actual_value(variable) \ +#define get_actual_var_state(variable) \ (dlist_head_element(ValueHistoryEntry, node, &variable->data)) -#define get_history_entry(node_ptr) \ +#define get_var_history_entry(node_ptr) \ dlist_container(ValueHistoryEntry, node, node_ptr) +#define get_actual_pack_state(package) \ + (dlist_head_element(PackHistoryEntry, node, &package->packHistory)) +#define get_pack_history_entry(node_ptr) \ + dlist_container(PackHistoryEntry, node, node_ptr) #endif /* __PG_VARIABLES_H__ */ diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index ed3a524..b135202 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -409,3 +409,46 @@ SELECT pgv_exists('vars', 'any1'); COMMIT; SELECT pgv_exists('vars', 'any1'); SELECT pgv_get('vars', 'any1',NULL::text); + +SELECT * FROM pgv_list() ORDER BY package, name; +BEGIN; +SELECT pgv_free(); +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + +BEGIN; +SELECT pgv_free(); +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); +BEGIN; +SELECT pgv_free(); +SELECT * FROM pgv_list() ORDER BY package, name; +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); +SELECT * FROM pgv_list() ORDER BY package, name; +SELECT pgv_remove('vars'); +SELECT * FROM pgv_list() ORDER BY package, name; +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); +SAVEPOINT sp4; +SAVEPOINT sp5; +--SELECT pgv_remove('vars2'); +SELECT pgv_free(); +SELECT pgv_stats(); +SELECT pgv_list(); +RELEASE sp5; +SELECT pgv_stats(); +SELECT pgv_list(); +RELEASE sp4; +SELECT pgv_stats(); +SELECT pgv_list(); +COMMIT; +SELECT pgv_stats(); From f5726baa064680aaaf388ed8c849c7d39142a869 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 24 May 2018 11:32:15 +0300 Subject: [PATCH 030/147] Add text variable example --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 862153a..670d192 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ Function | Returns | Description `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, 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. +`pgv_stats()` | `table(package text, allocated_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. @@ -188,7 +188,7 @@ It is easy to use functions to work with scalar variables: ```sql SELECT pgv_set('vars', 'int1', 101); -SELECT pgv_set('vars', 'int2', 102); +SELECT pgv_set('vars', 'text1', 'text variable'::text); SELECT pgv_get('vars', 'int1', NULL::int); pgv_get_int @@ -196,10 +196,10 @@ SELECT pgv_get('vars', 'int1', NULL::int); 101 (1 row) -SELECT pgv_get('vars', 'int2', NULL::int); - pgv_get_int -------------- - 102 +SELECT SELECT pgv_get('vars', 'text1', NULL::text); + pgv_get +--------------- + text variable (1 row) ``` @@ -254,11 +254,11 @@ You can list packages and variables: ```sql SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | f - vars | int2 | f - vars | r1 | f + package | name | is_transactional +---------+-------+------------------ + vars | int1 | f + vars | r1 | f + vars | text1 | f (3 rows) ``` @@ -266,9 +266,9 @@ And get used memory in bytes: ```sql SELECT * FROM pgv_stats() order by package; - package | used_memory ----------+------------- - vars | 16736 + package | allocated_memory +---------+------------------ + vars | 32768 (1 row) ``` From bc4971b068daa624013b411881569ee803bde816 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 24 May 2018 11:40:07 +0300 Subject: [PATCH 031/147] Remove unnecessary row notes from examples --- README.md | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 670d192..6c6d53f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ SELECT * FROM pgv_list() order by package, name; ---------+------+------------------ vars | int1 | f vars | int2 | f -(2 rows) ``` But if variable created with flag **is_transactional**: @@ -34,11 +33,9 @@ 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 @@ -194,13 +191,11 @@ SELECT pgv_get('vars', 'int1', NULL::int); pgv_get_int ------------- 101 -(1 row) SELECT SELECT pgv_get('vars', 'text1', NULL::text); pgv_get --------------- text variable -(1 row) ``` Let's assume we have a **tab** table: @@ -220,26 +215,22 @@ SELECT pgv_select('vars', 'r1'); ------------ (1,str11) (0,str00) -(2 rows) SELECT pgv_select('vars', 'r1', 1); pgv_select ------------ (1,str11) -(1 row) SELECT pgv_select('vars', 'r1', 0); pgv_select ------------ (0,str00) -(1 row) SELECT pgv_select('vars', 'r1', ARRAY[1, 0]); pgv_select ------------ (1,str11) (0,str00) -(2 rows) SELECT pgv_delete('vars', 'r1', 1); @@ -247,7 +238,6 @@ SELECT pgv_select('vars', 'r1'); pgv_select ------------ (0,str00) -(1 row) ``` You can list packages and variables: @@ -259,7 +249,6 @@ SELECT * FROM pgv_list() order by package, name; vars | int1 | f vars | r1 | f vars | text1 | f -(3 rows) ``` And get used memory in bytes: @@ -269,7 +258,6 @@ SELECT * FROM pgv_stats() order by package; package | allocated_memory ---------+------------------ vars | 32768 -(1 row) ``` You can delete variables or whole packages: @@ -288,6 +276,7 @@ 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; @@ -307,17 +296,17 @@ 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` statements and than rollback to previous state - variable will not be exist: + ```sql BEGIN; SAVEPOINT sp1; @@ -328,14 +317,15 @@ 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; ``` + Also you cannot undo removing variable by `ROLLBACK`: + ```sql SELECT pgv_set('pack', 'var_int', 122, true); BEGIN; @@ -344,18 +334,17 @@ ROLLBACK; SELECT pgv_get('pack', 'var_int', NULL::int); ERROR: unrecognized package "pack" ``` + 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. From 9d3e18269f5765450f97248e13c88e7bd655fb3a Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Thu, 24 May 2018 18:09:27 +0300 Subject: [PATCH 032/147] update .dockerignore --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index 0642fd8..ce3c9e6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ *.gcda *.gcov *.so +*.o From 0c7a8503fd2557c87716486b3590c2da3ed9a93f Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 25 May 2018 15:16:28 +0300 Subject: [PATCH 033/147] Fix pgv_stats() segfaults --- expected/pg_variables_trans.out | 53 ++++++++++++++++----------------- pg_variables.c | 49 +++++++++++++++++++----------- sql/pg_variables_trans.sql | 15 +++++----- 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 27a7452..5d95c23 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1752,53 +1752,52 @@ SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); SAVEPOINT sp4; SAVEPOINT sp5; ---SELECT pgv_remove('vars2'); SELECT pgv_free(); pgv_free ---------- (1 row) -SELECT pgv_stats(); - pgv_stats ---------------- - (vars2,24576) - (vars,24576) +SELECT package FROM pgv_stats(); + package +--------- + vars2 + vars (2 rows) -SELECT pgv_list(); - pgv_list ----------- +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ (0 rows) RELEASE sp5; -SELECT pgv_stats(); - pgv_stats ---------------- - (vars2,24576) - (vars,24576) +SELECT package FROM pgv_stats(); + package +--------- + vars2 + vars (2 rows) -SELECT pgv_list(); - pgv_list ----------- +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ (0 rows) RELEASE sp4; -SELECT pgv_stats(); - pgv_stats --------------- - (vars,24576) +SELECT package FROM pgv_stats(); + package +--------- + vars (1 row) -SELECT pgv_list(); - pgv_list ----------- +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ (0 rows) COMMIT; -SELECT pgv_stats(); - pgv_stats ------------ +SELECT package FROM pgv_stats(); + package +--------- (0 rows) diff --git a/pg_variables.c b/pg_variables.c index 429e210..d4a5423 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -107,12 +107,11 @@ static HashVariableEntry *LastVariable = NULL; static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; -/* Returns a list of of vars changed at current subxact level */ -#define get_actual_changed_vars_list() \ +/* Returns a lists of packages and variables changed at current subxact level */ +#define get_actual_changes_list() \ ( \ AssertMacro(changesStack != NULL), \ - (dlist_head_element(ChangesStackNode, \ - node, changesStack))->changedVarsList \ + (dlist_head_element(ChangesStackNode, node, changesStack)) \ ) @@ -1192,8 +1191,8 @@ get_packages_stats(PG_FUNCTION_ARGS) /* Fill data */ values[0] = PointerGetDatum(cstring_to_text(package->name)); - - getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); + if (get_actual_pack_state(package)->is_valid) + getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); totalSpace = regularSpace + transactSpace; values[1] = Int64GetDatum(totalSpace); @@ -1535,7 +1534,7 @@ createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) /* Release memory for variable */ history_entry_new = palloc0(sizeof(ValueHistoryEntry)); - history_entry_prev = dlist_head_element(ValueHistoryEntry, node, history); + history_entry_prev = get_actual_var_state(variable); scalar = &history_entry_new->value.scalar; *scalar = history_entry_prev->value.scalar; @@ -1626,7 +1625,7 @@ createSavepointPack(HashPackageEntry *package) history = &package->packHistory; history_entry_new = MemoryContextAllocZero(ModuleContext, sizeof(PackHistoryEntry)); - history_entry_prev = dlist_head_element(PackHistoryEntry, node, history); + history_entry_prev = get_actual_pack_state(package); history_entry_new->is_valid = history_entry_prev->is_valid; dlist_push_head(history, &history_entry_new->node); } @@ -1666,8 +1665,8 @@ releaseSavepointPack(HashPackageEntry *package) /* Remove package from packagesHash */ hash_search(packagesHash, package->name, HASH_REMOVE, &found); /* - *Delete a variable from the change history of the overlying - *transaction level. + * Delete a variable from the change history of the overlying + * transaction level (head of 'changesStack' at this point) */ if (!dlist_is_empty(changesStack)) removeFromChangedVars(package); @@ -1865,7 +1864,7 @@ addToChangedPacks(HashPackageEntry *package) { ChangedPacksNode *cpn; - csn = dlist_head_element(ChangesStackNode, node, changesStack); + csn = get_actual_changes_list(); cpn = makeChangedPacksNode(csn->ctx, package); dlist_push_head(csn->changedPacksList, &cpn->node); @@ -1898,7 +1897,7 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { ChangedVarsNode *cvn; - csn = dlist_head_element(ChangesStackNode, node, changesStack); + csn = get_actual_changes_list(); cvn = makeChangedVarsNode(csn->ctx, package, variable); dlist_push_head(csn->changedVarsList, &cvn->node); @@ -1908,21 +1907,37 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) } /* - * Remove from the changes list the variables of the deleted package + * Remove from the changes list a deleted package */ static void removeFromChangedVars(HashPackageEntry *package) { - dlist_mutable_iter var_miter; - dlist_head *changedVarsList; + dlist_mutable_iter var_miter, + pack_miter; + dlist_head *changedVarsList, + *changedPacksList; - changedVarsList = get_actual_changed_vars_list(); + /* First remove corresponding variables from changedVarsList */ + changedVarsList = get_actual_changes_list()->changedVarsList; dlist_foreach_modify(var_miter, changedVarsList) { - ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, var_miter.cur); + ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, + var_miter.cur); if (cvn_cur->package == package) dlist_delete(&cvn_cur->node); } + /* Now remove package itself from changedPacksList */ + changedPacksList = get_actual_changes_list()->changedPacksList; + dlist_foreach_modify(pack_miter, changedPacksList) + { + ChangedPacksNode *cpn_cur = dlist_container(ChangedPacksNode, node, + pack_miter.cur); + if (cpn_cur->package == package) + { + dlist_delete(&cpn_cur->node); + break; + } + } } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index b135202..cf7a28f 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -440,15 +440,14 @@ SAVEPOINT sp3; SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); SAVEPOINT sp4; SAVEPOINT sp5; ---SELECT pgv_remove('vars2'); SELECT pgv_free(); -SELECT pgv_stats(); -SELECT pgv_list(); +SELECT package FROM pgv_stats(); +SELECT * FROM pgv_list() ORDER BY package, name; RELEASE sp5; -SELECT pgv_stats(); -SELECT pgv_list(); +SELECT package FROM pgv_stats(); +SELECT * FROM pgv_list() ORDER BY package, name; RELEASE sp4; -SELECT pgv_stats(); -SELECT pgv_list(); +SELECT package FROM pgv_stats(); +SELECT * FROM pgv_list() ORDER BY package, name; COMMIT; -SELECT pgv_stats(); +SELECT package FROM pgv_stats(); From d136d842103417a987d53a1719695230661fc67d Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 2 Jun 2018 16:02:36 +0300 Subject: [PATCH 034/147] Fixed pull request issues --- pg_variables.c | 77 ++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index d4a5423..e4fd7c9 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -113,7 +113,10 @@ static MemoryContext changesStackContext = NULL; AssertMacro(changesStack != NULL), \ (dlist_head_element(ChangesStackNode, node, changesStack)) \ ) - +#define pack_hctx(pack, is_trans) \ + (is_trans ? pack->hctxTransact : pack->hctxRegular) +#define pack_htab(pack, is_trans) \ + (is_trans ? pack->varHashTransact : pack->varHashRegular) #define PGV_MCXT_MAIN "pg_variables: main memory context" #define PGV_MCXT_VARS "pg_variables: variables hash" @@ -157,9 +160,7 @@ variable_set(text *package_name, text *var_name, scalar->is_null = is_null; if (!scalar->is_null) { - oldcxt = MemoryContextSwitchTo(is_transactional ? - package->hctxTransact : - package->hctxRegular); + oldcxt = MemoryContextSwitchTo(pack_hctx(package, is_transactional)); scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); MemoryContextSwitchTo(oldcxt); } @@ -357,8 +358,7 @@ variable_insert(PG_FUNCTION_ARGS) /* * This is the first record for the var_name. Initialize attributes. */ - init_attributes(variable, tupdesc, is_transactional ? - package->hctxTransact : package->hctxRegular); + init_attributes(variable, tupdesc, pack_hctx(package, is_transactional)); } else check_attributes(variable, tupdesc); @@ -1025,12 +1025,13 @@ get_packages_and_variables(PG_FUNCTION_ARGS) { HashVariableEntry *variable; HASH_SEQ_STATUS vstat; + int i; /* Skip packages marked as deleted */ if (!get_actual_pack_state(package)->is_valid) continue; /* Get variables list for package */ - for(int i=0; i < 2; i++) + for (i=0; i < 2; i++) { hash_seq_init(&vstat, i ? package->varHashTransact : package->varHashRegular); @@ -1268,7 +1269,7 @@ makePackHTAB(HashPackageEntry *package, bool is_trans) package->hctxRegular = AllocSetContextCreate(ModuleContext, PGV_MCXT_VARS, ALLOCSET_DEFAULT_SIZES); - sprintf(hash_name, "%s variables hash for package \"%s\"", + snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", is_trans ? "Transactional" : "Regular", key); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(HashVariableEntry); @@ -1447,9 +1448,8 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, is_transactional ? "NOT " : ""))); - variable = (HashVariableEntry *) hash_search(is_transactional ? - package->varHashTransact : - package->varHashRegular, + variable = (HashVariableEntry *) hash_search( + pack_htab(package, is_transactional), key, HASH_ENTER, &found); /* Check variable type */ @@ -1486,9 +1486,7 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, variable->typid = typid; variable->is_transactional = is_transactional; dlist_init(&variable->data); - historyEntry = MemoryContextAllocZero(is_transactional ? - package->hctxTransact : - package->hctxRegular, + historyEntry = MemoryContextAllocZero(pack_hctx(package, is_transactional), sizeof(ValueHistoryEntry)); dlist_push_head(&variable->data, &historyEntry->node); @@ -1816,7 +1814,7 @@ pushChangesStack(void) changesStackContext = AllocSetContextCreate(ModuleContext, PGV_MCXT_STACK, ALLOCSET_START_SMALL_SIZES); - + Assert(changesStackContext); oldcxt = MemoryContextSwitchTo(changesStackContext); if (!changesStack) @@ -1824,7 +1822,7 @@ pushChangesStack(void) changesStack = palloc0(sizeof(dlist_head)); dlist_init(changesStack); } - + Assert(changesStack); csn = palloc0(sizeof(ChangesStackNode)); csn->changedVarsList = palloc0(sizeof(dlist_head)); csn->changedPacksList = palloc0(sizeof(dlist_head)); @@ -1841,13 +1839,11 @@ pushChangesStack(void) } /* - * Add a package to list of created or removed packs in current transaction level + * Create a changesStack with the required depth. */ static void -addToChangedPacks(HashPackageEntry *package) +prepareChangesStack(void) { - ChangesStackNode *csn; - if (!changesStack) { int level = GetCurrentTransactionNestLevel(); @@ -1857,11 +1853,18 @@ addToChangedPacks(HashPackageEntry *package) pushChangesStack(); } } +} - Assert(changesStack && changesStackContext); - +/* + * Add a package to list of created or removed packs in current transaction level + */ +static void +addToChangedPacks(HashPackageEntry *package) +{ + prepareChangesStack(); if (!isPackChangedInCurrentTrans(package)) { + ChangesStackNode *csn; ChangedPacksNode *cpn; csn = get_actual_changes_list(); @@ -1879,22 +1882,10 @@ addToChangedPacks(HashPackageEntry *package) static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) { - ChangesStackNode *csn; - - if (!changesStack) - { - int level = GetCurrentTransactionNestLevel(); - - while (level-- > 0) - { - pushChangesStack(); - } - } - - Assert(changesStack && changesStackContext); - + prepareChangesStack(); if (!isVarChangedInCurrentTrans(variable)) { + ChangesStackNode *csn; ChangedVarsNode *cvn; csn = get_actual_changes_list(); @@ -1955,7 +1946,7 @@ typedef enum Action * apply corresponding action on them */ static void -proceedChanges(Action action) +processChanges(Action action) { ChangesStackNode *bottom_list; @@ -2093,10 +2084,10 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, pushChangesStack(); break; case SUBXACT_EVENT_COMMIT_SUB: - proceedChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT); break; case SUBXACT_EVENT_ABORT_SUB: - proceedChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT); break; case SUBXACT_EVENT_PRE_COMMIT_SUB: break; @@ -2115,16 +2106,16 @@ pgvTransCallback(XactEvent event, void *arg) switch (event) { case XACT_EVENT_PRE_COMMIT: - proceedChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT); break; case XACT_EVENT_ABORT: - proceedChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT); break; case XACT_EVENT_PARALLEL_PRE_COMMIT: - proceedChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT); break; case XACT_EVENT_PARALLEL_ABORT: - proceedChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT); break; default: break; From cc26887ffe7885c2c3b667c81b01145bf2fd61f3 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sun, 3 Jun 2018 22:10:17 +0300 Subject: [PATCH 035/147] Removed duplicated code --- pg_variables.c | 360 ++++++++++++++++++------------------------ pg_variables.h | 30 ++-- pg_variables_record.c | 22 +-- 3 files changed, 180 insertions(+), 232 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index e4fd7c9..db272d3 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -151,7 +151,7 @@ variable_set(text *package_name, text *var_name, variable = createVariableInternal(package, var_name, typid, is_transactional); - scalar = get_actual_value_scalar(variable); + scalar = getActualValueScalar(variable); /* Release memory for variable */ if (scalar->typbyval == false && scalar->is_null == false) @@ -190,7 +190,7 @@ variable_get(text *package_name, text *var_name, *is_null = true; return 0; } - scalar = get_actual_value_scalar(variable); + scalar = getActualValueScalar(variable); *is_null = scalar->is_null; return scalar->value; } @@ -353,7 +353,7 @@ variable_insert(PG_FUNCTION_ARGS) tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - if (!(get_actual_value_record(variable))->tupdesc) + if (!(getActualValueRecord(variable))->tupdesc) { /* * This is the first record for the var_name. Initialize attributes. @@ -546,7 +546,7 @@ variable_select(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); variable = getVariableInternal(package, var_name, RECORDOID, true); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -621,7 +621,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) if (!value_is_null) check_record_key(variable, value_type); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); /* Search a record */ k.value = value; @@ -693,7 +693,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); + getActualValueRecord(variable)->tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -715,7 +715,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) bool found; RecordVar *record; - record = get_actual_value_record(var->variable); + record = getActualValueRecord(var->variable); /* Search a record */ k.value = value; k.is_null = isnull; @@ -745,19 +745,19 @@ variable_select_by_values(PG_FUNCTION_ARGS) static void cleanVariableCurrentState(HashVariableEntry *variable) { - ValueHistoryEntry *historyEntryToDelete; + VarHistoryEntry *historyEntryToDelete; if (variable->typid == RECORDOID) clean_records(variable); else { - ScalarVar *scalar = get_actual_value_scalar(variable); + ScalarVar *scalar = getActualValueScalar(variable); if (scalar->typbyval == false && scalar->is_null == false) pfree(DatumGetPointer(scalar->value)); } - historyEntryToDelete = get_var_history_entry(dlist_pop_head_node(&variable->data)); + historyEntryToDelete = getHistoryEntryOfVar(dlist_pop_head_node(&variable->history)); pfree(historyEntryToDelete); } @@ -768,7 +768,7 @@ cleanVariableCurrentState(HashVariableEntry *variable) static void cleanVariableAllStates(HashVariableEntry *variable) { - while(!dlist_is_empty(&variable->data)) + while(!dlist_is_empty(&variable->history)) { cleanVariableCurrentState(variable); } @@ -812,7 +812,7 @@ variable_exists(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found ? get_actual_var_state(variable)->is_valid : found); + PG_RETURN_BOOL(found ? getActualStateOfVar(variable)->is_valid : found); } /* @@ -878,7 +878,7 @@ remove_variable(PG_FUNCTION_ARGS) createSavepointVar(package, variable); addToChangedVars(package, variable); } - get_actual_var_state(variable)->is_valid = false; + getActualStateOfVar(variable)->is_valid = false; } /* Remove variable from cache */ @@ -938,7 +938,7 @@ removePackageInternal(HashPackageEntry *package) createSavepointPack(package); addToChangedPacks(package); } - get_actual_pack_state(package)->is_valid = false; + getActualStateOfPack(package)->is_valid = false; } /* @@ -1028,7 +1028,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) int i; /* Skip packages marked as deleted */ - if (!get_actual_pack_state(package)->is_valid) + if (!getActualStateOfPack(package)->is_valid) continue; /* Get variables list for package */ for (i=0; i < 2; i++) @@ -1038,7 +1038,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) while ((variable = (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) { - if (!get_actual_var_state(variable)->is_valid) + if (!getActualStateOfVar(variable)->is_valid) continue; /* Resize recs if necessary */ if (nRecs >= mRecs) @@ -1192,7 +1192,7 @@ get_packages_stats(PG_FUNCTION_ARGS) /* Fill data */ values[0] = PointerGetDatum(cstring_to_text(package->name)); - if (get_actual_pack_state(package)->is_valid) + if (getActualStateOfPack(package)->is_valid) getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); totalSpace = regularSpace + transactSpace; @@ -1316,7 +1316,7 @@ getPackageByName(text* name, bool create, bool strict) if (found) { - if (get_actual_pack_state(package)->is_valid) + if (getActualStateOfPack(package)->is_valid) return package; else if (create) { @@ -1328,7 +1328,7 @@ getPackageByName(text* name, bool create, bool strict) createSavepointPack(package); addToChangedPacks(package); } - get_actual_pack_state(package)->is_valid = true; + getActualStateOfPack(package)->is_valid = true; /* Restore previously removed HTAB for regular variables */ makePackHTAB(package, false); /* Mark all transactional variables in package as removed */ @@ -1341,7 +1341,7 @@ getPackageByName(text* name, bool create, bool strict) createSavepointVar(package, variable); addToChangedVars(package, variable); } - get_actual_var_state(variable)->is_valid = false; + getActualStateOfVar(variable)->is_valid = false; } return package; } @@ -1366,10 +1366,10 @@ getPackageByName(text* name, bool create, bool strict) makePackHTAB(package, false); makePackHTAB(package, true); /* Initialize history */ - dlist_init(&package->packHistory); + dlist_init(&package->history); historyEntry = MemoryContextAllocZero(ModuleContext, sizeof(PackHistoryEntry)); - dlist_push_head(&package->packHistory, &historyEntry->node); + dlist_push_head(&package->history, &historyEntry->node); historyEntry->is_valid = true; /* Add to changes list */ addToChangedPacks(package); @@ -1409,7 +1409,7 @@ getVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool stric errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } - if (!get_actual_var_state(variable)->is_valid && strict) + if (!getActualStateOfVar(variable)->is_valid && strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); @@ -1479,17 +1479,17 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, } else { - ValueHistoryEntry *historyEntry; + VarHistoryEntry *historyEntry; /* Variable entry was created, so initialize new variable. */ Assert(variable); variable->typid = typid; variable->is_transactional = is_transactional; - dlist_init(&variable->data); + dlist_init(&variable->history); historyEntry = MemoryContextAllocZero(pack_hctx(package, is_transactional), - sizeof(ValueHistoryEntry)); + sizeof(VarHistoryEntry)); - dlist_push_head(&variable->data, &historyEntry->node); + dlist_push_head(&variable->history, &historyEntry->node); if (typid != RECORDOID) { ScalarVar *scalar = &(historyEntry->value.scalar); @@ -1523,16 +1523,16 @@ createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) { ScalarVar *scalar; ValueHistory *history; - ValueHistoryEntry *history_entry_new, + VarHistoryEntry *history_entry_new, *history_entry_prev; MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(package->hctxTransact); - history = &variable->data; + history = &variable->history; /* Release memory for variable */ - history_entry_new = palloc0(sizeof(ValueHistoryEntry)); - history_entry_prev = get_actual_var_state(variable); + history_entry_new = palloc0(sizeof(VarHistoryEntry)); + history_entry_prev = getActualStateOfVar(variable); scalar = &history_entry_new->value.scalar; *scalar = history_entry_prev->value.scalar; @@ -1556,16 +1556,16 @@ static void releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) { ValueHistory *history; - ValueHistoryEntry *historyEntry; + VarHistoryEntry *historyEntry; - history = &variable->data; + history = &variable->history; if (dlist_has_next(history, dlist_head_node(history))) { - ValueHistoryEntry *historyEntryToDelete; + VarHistoryEntry *historyEntryToDelete; dlist_node *nodeToDelete; nodeToDelete = dlist_next_node(history, dlist_head_node(history)); - historyEntryToDelete = get_var_history_entry(nodeToDelete); + historyEntryToDelete = getHistoryEntryOfVar(nodeToDelete); if (variable->typid == RECORDOID) { @@ -1582,7 +1582,7 @@ releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) pfree(historyEntryToDelete); } /* Change subxact level due to release */ - historyEntry = get_actual_var_state(variable); + historyEntry = getActualStateOfVar(variable); historyEntry->level--; if (!historyEntry->is_valid && !dlist_has_next(history, dlist_head_node(history))) @@ -1602,7 +1602,7 @@ rollbackSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) cleanVariableCurrentState(variable); /* Remove variable if it was created in rolled back transaction */ - if (dlist_is_empty(&variable->data)) + if (dlist_is_empty(&variable->history)) { bool found; @@ -1621,9 +1621,9 @@ createSavepointPack(HashPackageEntry *package) PackHistoryEntry *history_entry_new, *history_entry_prev; - history = &package->packHistory; + history = &package->history; history_entry_new = MemoryContextAllocZero(ModuleContext, sizeof(PackHistoryEntry)); - history_entry_prev = get_actual_pack_state(package); + history_entry_prev = getActualStateOfPack(package); history_entry_new->is_valid = history_entry_prev->is_valid; dlist_push_head(history, &history_entry_new->node); } @@ -1637,9 +1637,9 @@ releaseSavepointPack(HashPackageEntry *package) PackHistory *history; PackHistoryEntry *historyEntry; - Assert(get_actual_pack_state(package)->level == + Assert(getActualStateOfPack(package)->level == GetCurrentTransactionNestLevel()); - history = &package->packHistory; + history = &package->history; /* Package existed in parent transaction */ if (dlist_has_next(history, dlist_head_node(history))) @@ -1654,7 +1654,7 @@ releaseSavepointPack(HashPackageEntry *package) pfree(historyEntryToDelete); } /* Package has no previous states and can be completely removed if necessary*/ - if (!get_actual_pack_state(package)->is_valid && + if (!getActualStateOfPack(package)->is_valid && !dlist_has_next(history, dlist_head_node(history))) { bool found; @@ -1673,7 +1673,7 @@ releaseSavepointPack(HashPackageEntry *package) /* Change subxact level due to release */ else { - historyEntry = get_actual_pack_state(package); + historyEntry = getActualStateOfPack(package); historyEntry->level--; } } @@ -1686,12 +1686,12 @@ rollbackSavepointPack(HashPackageEntry *package) { PackHistoryEntry *historyEntryToDelete; - historyEntryToDelete = get_actual_pack_state(package); + historyEntryToDelete = getActualStateOfPack(package); if (historyEntryToDelete->is_valid) releaseSavepointPack(package); else { - dlist_pop_head_node(&package->packHistory); + dlist_pop_head_node(&package->history); /* Create regular vars HTAB if it was removed */ if (!historyEntryToDelete->is_valid) makePackHTAB(package, false); @@ -1699,104 +1699,6 @@ rollbackSavepointPack(HashPackageEntry *package) } } -/* - * Initialize an instance of ChangedPacksNode datatype - */ -static inline ChangedPacksNode * -makeChangedPacksNode(MemoryContext ctx, HashPackageEntry *package) -{ - ChangedPacksNode *cpn; - - cpn = MemoryContextAllocZero(ctx, sizeof(ChangedPacksNode)); - cpn->package = package; - return cpn; -} - -/* - * Initialize an instance of ChangedVarsNode datatype - */ -static inline ChangedVarsNode * -makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, - HashVariableEntry *variable) -{ - ChangedVarsNode *cvn; - - cvn = MemoryContextAllocZero(ctx, sizeof(ChangedVarsNode)); - cvn->package = package; - cvn->variable = variable; - return cvn; -} - -/* - * Check if package was changed in current transaction level - */ -static bool -isPackChangedInCurrentTrans(HashPackageEntry *package) -{ - PackHistoryEntry *pack_state; - - if (!changesStack) - return false; - - pack_state = get_actual_pack_state(package); - return pack_state->level == GetCurrentTransactionNestLevel(); -} - -/* - * Check if variable was changed in parent transaction level - */ -static bool -isPackChangedInUpperTrans(HashPackageEntry *package) -{ - PackHistoryEntry *state, - *prev_state; - - state = get_actual_pack_state(package); - - if (dlist_has_next(&package->packHistory, &state->node)) - { - prev_state = get_pack_history_entry(state->node.next); - return prev_state->level == GetCurrentTransactionNestLevel() - 1; - } - - return false; -} - -/* - * Check if variable was changed in current transaction level - */ -static bool -isVarChangedInCurrentTrans(HashVariableEntry *variable) -{ - ValueHistoryEntry *var_state; - - if (!changesStack) - return false; - - var_state = get_actual_var_state(variable); - return var_state->level == GetCurrentTransactionNestLevel(); -} - -/* - * Check if variable was changed in parent transaction level - */ -static bool -isVarChangedInUpperTrans(HashVariableEntry *variable) -{ - ValueHistoryEntry *var_state, - *var_prev_state; - - var_state = get_actual_var_state(variable); - - if (dlist_has_next(&variable->data, &var_state->node)) - { - var_prev_state = get_var_history_entry(var_state->node.next); - return var_prev_state->level == GetCurrentTransactionNestLevel() - 1; - } - - return false; -} - /* * Create a new list of variables, changed in current transaction level */ @@ -1856,79 +1758,125 @@ prepareChangesStack(void) } /* - * Add a package to list of created or removed packs in current transaction level + * Check if object was changed in current transaction level */ -static void -addToChangedPacks(HashPackageEntry *package) -{ - prepareChangesStack(); - if (!isPackChangedInCurrentTrans(package)) - { - ChangesStackNode *csn; - ChangedPacksNode *cpn; - - csn = get_actual_changes_list(); - cpn = makeChangedPacksNode(csn->ctx, package); - dlist_push_head(csn->changedPacksList, &cpn->node); - - /* Give this package current subxact level */ - get_actual_pack_state(cpn->package)->level = GetCurrentTransactionNestLevel(); - } +#define IS_CHANGED_IN_CURRENT_TEMPLATE(type, argtype) \ +static bool \ +is##type##ChangedInCurrentTrans(argtype *object) \ +{ \ + type##HistoryEntry *state; \ + \ + if (!changesStack) \ + return false; \ + \ + state = getActualStateOf##type(object); \ + return state->level == GetCurrentTransactionNestLevel(); \ } /* - * Add a variable to list of changed vars in current transaction level + * Check if object was changed in parent transaction level */ -static void -addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable) -{ - prepareChangesStack(); - if (!isVarChangedInCurrentTrans(variable)) - { - ChangesStackNode *csn; - ChangedVarsNode *cvn; +#define IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(type, argtype) \ +static bool \ +is##type##ChangedInUpperTrans(argtype *object) \ +{ \ + type##HistoryEntry *state, \ + *prev_state; \ + \ + state = getActualStateOf##type(object); \ + if (dlist_has_next(&object->history, &state->node))\ + { \ + prev_state = getHistoryEntryOf##type(state->node.next); \ + return prev_state->level == GetCurrentTransactionNestLevel() - 1; \ + } \ + return false; \ +} - csn = get_actual_changes_list(); - cvn = makeChangedVarsNode(csn->ctx, package, variable); - dlist_push_head(csn->changedVarsList, &cvn->node); +/* + * Initialize an instance of Changed(Pack|Var)Node datatype + */ +#define MAKE_CHANGES_NODE_TEMPLATE(type) \ +static inline Changed##type##sNode * \ +makeChanged##type##sNode(MemoryContext ctx, HashPackageEntry *package FUNC_ARG) \ +{ \ + Changed##type##sNode *list_node; \ + \ + list_node = MemoryContextAllocZero(ctx, sizeof(Changed##type##sNode)); \ + list_node->package = package; \ + ASSIGN_VAR \ + return list_node; \ +} - /* Give this variable current subxact level */ - get_actual_var_state(cvn->variable)->level = GetCurrentTransactionNestLevel(); - } +/* + * Add an object to list of created or removed packs in current transaction level + */ +#define ADD_TO_CHANGES_TEMPLATE(type, object) \ +static void \ +addToChanged##type##s(HashPackageEntry *package FUNC_ARG) \ +{ \ + prepareChangesStack(); \ + if (!is##type##ChangedInCurrentTrans(object)) \ + { \ + ChangesStackNode *csn; \ + Changed##type##sNode *list_node; \ + \ + csn = get_actual_changes_list(); \ + list_node = makeChanged##type##sNode(csn->ctx, package MAKE_ARG); \ + dlist_push_head(csn->changed##type##sList, &list_node->node); \ + \ + getActualStateOf##type(list_node->object)->level = \ + GetCurrentTransactionNestLevel(); \ + } \ } +/* Functions to process changes history of packages */ +#define FUNC_ARG +#define ASSIGN_VAR +#define MAKE_ARG +MAKE_CHANGES_NODE_TEMPLATE(Pack) +ADD_TO_CHANGES_TEMPLATE(Pack, package) +IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(Pack, HashPackageEntry) +IS_CHANGED_IN_CURRENT_TEMPLATE(Pack, HashPackageEntry) +#undef FUNC_ARG +#undef ASSIGN_VAR +#undef MAKE_ARG + +/* Functions to process changes history of variables */ +#define FUNC_ARG , HashVariableEntry *variable +#define ASSIGN_VAR list_node->variable = variable; +#define MAKE_ARG , variable +MAKE_CHANGES_NODE_TEMPLATE(Var) +ADD_TO_CHANGES_TEMPLATE(Var, variable) +IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(Var, HashVariableEntry) +IS_CHANGED_IN_CURRENT_TEMPLATE(Var, HashVariableEntry) +#undef FUNC_ARG +#undef ASSIGN_VAR +#undef MAKE_ARG + /* * Remove from the changes list a deleted package */ static void removeFromChangedVars(HashPackageEntry *package) { - dlist_mutable_iter var_miter, - pack_miter; - dlist_head *changedVarsList, - *changedPacksList; - - /* First remove corresponding variables from changedVarsList */ - changedVarsList = get_actual_changes_list()->changedVarsList; - dlist_foreach_modify(var_miter, changedVarsList) - { - ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, - var_miter.cur); - if (cvn_cur->package == package) - dlist_delete(&cvn_cur->node); - } - /* Now remove package itself from changedPacksList */ - changedPacksList = get_actual_changes_list()->changedPacksList; - dlist_foreach_modify(pack_miter, changedPacksList) - { - ChangedPacksNode *cpn_cur = dlist_container(ChangedPacksNode, node, - pack_miter.cur); - if (cpn_cur->package == package) - { - dlist_delete(&cpn_cur->node); - break; - } - } +#define REMOVE(type) \ + { \ + dlist_mutable_iter miter##type; \ + dlist_head *changed##type##List; \ + changed##type##List = get_actual_changes_list()->changed##type##List; \ + dlist_foreach_modify(miter##type, changed##type##List) \ + { \ + Changed##type##Node *list_node = \ + dlist_container(Changed##type##Node, node, miter##type.cur); \ + if (list_node->package == package) \ + dlist_delete(&list_node->node); \ + } \ + } + + REMOVE(Vars); + REMOVE(Packs); + +#undef REMOVE } /* @@ -1975,8 +1923,8 @@ processChanges(Action action) * We do not check pack_state->level, because var cannot get in * list of changes until pack is removed. */ - if (!get_actual_pack_state(cvn_cur->package)->is_valid) - get_actual_var_state(cvn_cur->variable)->is_valid = false; + if (!getActualStateOfPack(cvn_cur->package)->is_valid) + getActualStateOfVar(cvn_cur->variable)->is_valid = false; /* Did this variable change at parent level? */ if (dlist_is_empty(changesStack) || @@ -2000,7 +1948,7 @@ processChanges(Action action) dlist_push_head(csn->changedVarsList, &cvn_new->node); /* Change subxact level due to release */ - get_actual_var_state(cvn_new->variable)->level--; + getActualStateOfVar(cvn_new->variable)->level--; } break; } @@ -2015,7 +1963,7 @@ processChanges(Action action) switch (action) { case ROLLBACK_TO_SAVEPOINT: - if (!get_actual_pack_state(cpn_cur->package)->is_valid) + if (!getActualStateOfPack(cpn_cur->package)->is_valid) { rollbackSavepointPack(cpn_cur->package); break; @@ -2042,7 +1990,7 @@ processChanges(Action action) dlist_push_head(csn->changedPacksList, &cpn_new->node); /* Change subxact level due to release */ - get_actual_pack_state(cpn_new->package)->level--; + getActualStateOfPack(cpn_new->package)->level--; } break; } diff --git a/pg_variables.h b/pg_variables.h index 2a3ab2b..7e90908 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -49,7 +49,7 @@ typedef struct HashPackageEntry /* Memory context for package variables for easy memory release */ MemoryContext hctxRegular, hctxTransact; - PackHistory packHistory; + PackHistory history; } HashPackageEntry; @@ -74,7 +74,7 @@ typedef struct ScalarVar } ScalarVar; /* List node that stores one of the variables states */ -typedef struct ValueHistoryEntry{ +typedef struct VarHistoryEntry{ dlist_node node; union { @@ -84,7 +84,7 @@ typedef struct ValueHistoryEntry{ /* Transaction nest level of current entry */ int level; bool is_valid; -} ValueHistoryEntry; +} VarHistoryEntry; typedef dlist_head ValueHistory; @@ -93,7 +93,7 @@ typedef struct HashVariableEntry { char name[NAMEDATALEN]; /* Entry point to list with states of value */ - ValueHistory data; + ValueHistory history; Oid typid; /* * The flag determines the further behavior of the variable. @@ -159,17 +159,17 @@ 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_actual_var_state(variable) \ - (dlist_head_element(ValueHistoryEntry, node, &variable->data)) -#define get_var_history_entry(node_ptr) \ - dlist_container(ValueHistoryEntry, node, node_ptr) -#define get_actual_pack_state(package) \ - (dlist_head_element(PackHistoryEntry, node, &package->packHistory)) -#define get_pack_history_entry(node_ptr) \ +#define getActualValueScalar(variable) \ + (&((dlist_head_element(VarHistoryEntry, node, &variable->history))->value.scalar)) +#define getActualValueRecord(variable) \ + (&((dlist_head_element(VarHistoryEntry, node, &variable->history))->value.record)) +#define getActualStateOfVar(variable) \ + (dlist_head_element(VarHistoryEntry, node, &variable->history)) +#define getActualStateOfPack(package) \ + (dlist_head_element(PackHistoryEntry, node, &package->history)) +#define getHistoryEntryOfVar(node_ptr) \ + dlist_container(VarHistoryEntry, node, node_ptr) +#define getHistoryEntryOfPack(node_ptr) \ dlist_container(PackHistoryEntry, node, node_ptr) #endif /* __PG_VARIABLES_H__ */ diff --git a/pg_variables_record.c b/pg_variables_record.c index af5bf41..2c5ae6a 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 = getActualValueRecord(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 = getActualValueRecord(variable); /* First, check columns count. */ if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, @@ -176,7 +176,7 @@ check_record_key(HashVariableEntry *variable, Oid typid) RecordVar *record; Assert(variable->typid == RECORDOID); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); if (GetTupleDescAttr(record->tupdesc, 0)->atttypid != typid) ereport(ERROR, @@ -204,7 +204,7 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -264,7 +264,7 @@ update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -314,7 +314,7 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) Assert(variable->typid == RECORDOID); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); /* Delete a record */ k.value = value; @@ -340,7 +340,7 @@ clean_records(HashVariableEntry *variable) Assert(variable->typid == RECORDOID); - record = get_actual_value_record(variable); + record = getActualValueRecord(variable); /* All records will be freed */ MemoryContextDelete(record->hctx); } @@ -356,7 +356,7 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) *record_new; HashRecordEntry *item_prev, *item_new; - ValueHistoryEntry *history_entry_new; + VarHistoryEntry *history_entry_new; HASH_SEQ_STATUS *rstat; bool found; MemoryContext oldcxt; @@ -364,12 +364,12 @@ insert_savepoint(HashVariableEntry *variable, MemoryContext packageContext) Assert(variable->typid == RECORDOID); /* Create new history entry */ - record_prev = get_actual_value_record(variable); + record_prev = getActualValueRecord(variable); oldcxt = MemoryContextSwitchTo(packageContext); - history_entry_new = palloc0(sizeof(ValueHistoryEntry)); + history_entry_new = palloc0(sizeof(VarHistoryEntry)); history_entry_new->is_valid = true; record_new = &(history_entry_new->value.record); - dlist_push_head(&variable->data, &history_entry_new->node); + dlist_push_head(&variable->history, &history_entry_new->node); init_attributes(variable, record_prev->tupdesc, packageContext); /* Copy previous history entry into the new one*/ From 2a17f69beef05a87ab3c14bf3600dfb26b3f5025 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 19 Jun 2018 21:54:51 +0300 Subject: [PATCH 036/147] OOP refactoring --- expected/pg_variables_trans.out | 2 +- pg_variables.c | 967 +++++++++++++++----------------- pg_variables.h | 158 +++--- pg_variables_record.c | 94 +--- sql/pg_variables_trans.sql | 2 +- 5 files changed, 579 insertions(+), 644 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 5d95c23..dbf21d1 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1744,7 +1744,7 @@ BEGIN; SAVEPOINT sp1; SAVEPOINT sp2; SAVEPOINT sp3; -SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); pgv_set --------- diff --git a/pg_variables.c b/pg_variables.c index db272d3..62d445d 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -49,38 +49,43 @@ extern void _PG_fini(void); static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); -static HashPackageEntry *getPackageByName(text *name, bool create, bool strict); -static HashVariableEntry *getVariableInternal(HashPackageEntry *package, +static Package *getPackageByName(text *name, bool create, bool strict); +static Variable *getVariableInternal(Package *package, text *name, Oid typid, bool strict); -static HashVariableEntry *createVariableInternal(HashPackageEntry *package, +static Variable *createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional); -static void removePackageInternal(HashPackageEntry *package); +static void removePackageInternal(Package *package); -static void releaseSavepointPack(HashPackageEntry *package); -static void rollbackSavepointPack(HashPackageEntry *package); -static void createSavepointPack(HashPackageEntry *package); -static void releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); -static void rollbackSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); -static void createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable); +/* Functions to work with transactional objects */ +static void createSavepoint(TransObject *object, TransObjectType type); +static void releaseSavepoint(TransObject *object, TransObjectType type); +static void rollbackSavepoint(TransObject *object, TransObjectType type); +static void copyValue(VarState *src, VarState *dest, Variable *destVar); +static void freeValue(VarState *varstate, Oid typid); +static void removeState(TransObject *object, TransObjectType type, State *stateToDelete); +static void removeObject(TransObject *object, TransObjectType type); +static bool isObjectChangedInCurrentTrans(TransObject *object); +static bool isObjectChangedInUpperTrans(TransObject *object); + +static void addToChangesStack(TransObject *object, TransObjectType type); static void pushChangesStack(void); -static void addToChangedPacks(HashPackageEntry *package); -static void addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable); -static void removeFromChangedVars(HashPackageEntry *package); - -static inline ChangedPacksNode * -makeChangedPacksNode(MemoryContext ctx, HashPackageEntry *package); -static inline ChangedVarsNode * -makeChangedVarsNode(MemoryContext ctx, HashPackageEntry *package, HashVariableEntry *variable); +static void removeFromChangedVars(Package *package); + +/* Wrappers */ +static inline void createSavepointPack(Package *package); +static inline void createSavepointVar(Variable *variable); +static inline void addToChangedPacks(Package *package); +static inline void addToChangedVars(Variable *variable); + +/* Constructors */ static void -makePackHTAB(HashPackageEntry *package, bool is_trans); +makePackHTAB(Package *package, bool is_trans); +static inline ChangedObject * +makeChangedObject(TransObject *object, MemoryContext ctx); -static bool isPackChangedInCurrentTrans(HashPackageEntry *package); -static bool isPackChangedInUpperTrans(HashPackageEntry *package); -static bool isVarChangedInCurrentTrans(HashVariableEntry *variable); -static bool isVarChangedInUpperTrans(HashVariableEntry *variable); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -98,9 +103,9 @@ static HTAB *packagesHash = NULL; static MemoryContext ModuleContext = NULL; /* Recent package */ -static HashPackageEntry *LastPackage = NULL; +static Package *LastPackage = NULL; /* Recent variable */ -static HashVariableEntry *LastVariable = NULL; +static Variable *LastVariable = NULL; /* This stack contains lists of changed variables and packages per each subxact level */ @@ -142,8 +147,8 @@ static void variable_set(text *package_name, text *var_name, Oid typid, Datum value, bool is_null, bool is_transactional) { - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; ScalarVar *scalar; MemoryContext oldcxt; @@ -172,8 +177,8 @@ static Datum variable_get(text *package_name, text *var_name, Oid typid, bool *is_null, bool strict) { - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; ScalarVar *scalar; package = getPackageByName(package_name, false, strict); @@ -282,13 +287,14 @@ variable_insert(PG_FUNCTION_ARGS) text *package_name; text *var_name; HeapTupleHeader rec; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; bool is_transactional; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; + RecordVar *record; /* Checks */ CHECK_ARGS_FOR_NULL(); @@ -306,8 +312,8 @@ variable_insert(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(LastPackage->name) || - strncmp(VARDATA_ANY(package_name), LastPackage->name, + VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), getName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, true, false); @@ -319,8 +325,8 @@ variable_insert(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(LastVariable->name) || - strncmp(VARDATA_ANY(var_name), LastVariable->name, + VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastPackage)) || + strncmp(VARDATA_ANY(var_name), getName(LastPackage), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = createVariableInternal(package, var_name, RECORDOID, @@ -341,10 +347,10 @@ variable_insert(PG_FUNCTION_ARGS) errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, LastVariable->is_transactional ? "" : "NOT "))); } - if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) + if (variable->is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); - addToChangedVars(package, variable); + createSavepointVar(variable); + addToChangedVars(variable); } } @@ -352,13 +358,13 @@ variable_insert(PG_FUNCTION_ARGS) tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - - if (!(getActualValueRecord(variable))->tupdesc) + record = getActualValueRecord(variable); + if (!record->tupdesc) { /* * This is the first record for the var_name. Initialize attributes. */ - init_attributes(variable, tupdesc, pack_hctx(package, is_transactional)); + init_record(record, tupdesc, variable); } else check_attributes(variable, tupdesc); @@ -380,10 +386,9 @@ variable_update(PG_FUNCTION_ARGS) text *package_name; text *var_name; HeapTupleHeader rec; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; bool res; - Oid tupType; int32 tupTypmod; TupleDesc tupdesc; @@ -403,8 +408,8 @@ variable_update(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(LastPackage->name) || - strncmp(VARDATA_ANY(package_name), LastPackage->name, + VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), getName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, false, true); @@ -416,8 +421,8 @@ variable_update(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(LastVariable->name) || - strncmp(VARDATA_ANY(var_name), LastVariable->name, + VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), getName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package, var_name, RECORDOID, true); @@ -426,10 +431,10 @@ variable_update(PG_FUNCTION_ARGS) else variable = LastVariable; - if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) + if (variable->is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); - addToChangedVars(package, variable); + createSavepointVar(variable); + addToChangedVars(variable); } /* Update a record */ @@ -457,8 +462,8 @@ variable_delete(PG_FUNCTION_ARGS) Oid value_type; Datum value; bool value_is_null = PG_ARGISNULL(2); - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; bool res; CHECK_ARGS_FOR_NULL(); @@ -480,8 +485,8 @@ variable_delete(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(LastPackage->name) || - strncmp(VARDATA_ANY(package_name), LastPackage->name, + VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), getName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, false, true); @@ -493,8 +498,8 @@ variable_delete(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(LastVariable->name) || - strncmp(VARDATA_ANY(var_name), LastVariable->name, + VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), getName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package, var_name, RECORDOID, true); @@ -503,10 +508,11 @@ variable_delete(PG_FUNCTION_ARGS) else variable = LastVariable; - if (variable->is_transactional && !isVarChangedInCurrentTrans(variable)) + if (variable->is_transactional && + !isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); - addToChangedVars(package, variable); + createSavepointVar(variable); + addToChangedVars(variable); } /* Delete a record */ @@ -532,8 +538,8 @@ variable_select(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; MemoryContext oldcontext; RecordVar *record; @@ -590,8 +596,8 @@ variable_select_by_value(PG_FUNCTION_ARGS) Oid value_type; Datum value; bool value_is_null = PG_ARGISNULL(2); - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; HashRecordEntry *item; RecordVar *record; @@ -644,7 +650,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) /* Structure for variable_select_by_values() */ typedef struct { - HashVariableEntry *variable; + Variable *variable; ArrayIterator iterator; } VariableIteratorRec; @@ -662,8 +668,8 @@ variable_select_by_values(PG_FUNCTION_ARGS) text *package_name; text *var_name; ArrayType *values; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; MemoryContext oldcontext; /* Checks */ @@ -739,41 +745,6 @@ variable_select_by_values(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } -/* - * Remove one entry from history of states of arg 'variable' - */ -static void -cleanVariableCurrentState(HashVariableEntry *variable) -{ - VarHistoryEntry *historyEntryToDelete; - - if (variable->typid == RECORDOID) - clean_records(variable); - else - { - ScalarVar *scalar = getActualValueScalar(variable); - - if (scalar->typbyval == false && scalar->is_null == false) - pfree(DatumGetPointer(scalar->value)); - } - - historyEntryToDelete = getHistoryEntryOfVar(dlist_pop_head_node(&variable->history)); - pfree(historyEntryToDelete); -} - -/* - * Remove all entries from history of states of arg 'variable'. - * DOES NOT remove 'variable' itself. - */ -static void -cleanVariableAllStates(HashVariableEntry *variable) -{ - while(!dlist_is_empty(&variable->history)) - { - cleanVariableCurrentState(variable); - } -} - /* * Check if variable exists. */ @@ -782,8 +753,8 @@ variable_exists(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; char key[NAMEDATALEN]; bool found; @@ -803,16 +774,16 @@ variable_exists(PG_FUNCTION_ARGS) getKeyFromName(var_name, key); - variable = (HashVariableEntry *) hash_search(package->varHashRegular, - key, HASH_FIND, &found); + variable = (Variable *) hash_search(package->varHashRegular, + key, HASH_FIND, &found); if (!found) - variable = (HashVariableEntry *) hash_search(package->varHashTransact, - key, HASH_FIND, &found); + variable = (Variable *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found ? getActualStateOfVar(variable)->is_valid : found); + PG_RETURN_BOOL(found ? (getActualStateOfContainer(variable))->is_valid : found); } /* @@ -845,8 +816,8 @@ remove_variable(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - HashPackageEntry *package; - HashVariableEntry *variable; + Package *package; + Variable *variable; bool found; char key[NAMEDATALEN]; @@ -858,27 +829,30 @@ remove_variable(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); getKeyFromName(var_name, key); - variable = (HashVariableEntry *) hash_search(package->varHashRegular, - key, HASH_REMOVE, &found); + variable = (Variable *) hash_search(package->varHashRegular, + key, HASH_REMOVE, &found); if (found) + { /* Regular variable */ - cleanVariableAllStates(variable); + TransObject *object = &variable->transObject; + removeState(object, VARIABLE, getActualState(object)); + } else { - variable = (HashVariableEntry *) hash_search(package->varHashTransact, - key, HASH_FIND, &found); + variable = (Variable *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); /* Variable doesn't exist in both HTAB */ if (!found) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); /* Transactional variable */ - if (!isVarChangedInCurrentTrans(variable)) + if (!isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); - addToChangedVars(package, variable); + createSavepointVar(variable); + addToChangedVars(variable); } - getActualStateOfVar(variable)->is_valid = false; + getActualStateOfContainer(variable)->is_valid = false; } /* Remove variable from cache */ @@ -896,7 +870,7 @@ remove_variable(PG_FUNCTION_ARGS) Datum remove_package(PG_FUNCTION_ARGS) { - HashPackageEntry *package; + Package *package; text *package_name; char key[NAMEDATALEN]; @@ -927,18 +901,18 @@ remove_package(PG_FUNCTION_ARGS) } static void -removePackageInternal(HashPackageEntry *package) +removePackageInternal(Package *package) { /* All regular variables will be freed */ MemoryContextDelete(package->hctxRegular); /* Add to changes list */ - if (!isPackChangedInCurrentTrans(package)) + if (!isObjectChangedInCurrentTrans(&package->transObject)) { createSavepointPack(package); addToChangedPacks(package); } - getActualStateOfPack(package)->is_valid = false; + getActualStateOfContainer(package)->is_valid = false; } /* @@ -948,8 +922,8 @@ removePackageInternal(HashPackageEntry *package) Datum remove_packages(PG_FUNCTION_ARGS) { - HashPackageEntry *package; - HASH_SEQ_STATUS pstat; + Package *package; + HASH_SEQ_STATUS pstat; /* There is no any packages and variables */ if (packagesHash == NULL) PG_RETURN_VOID(); @@ -958,7 +932,7 @@ remove_packages(PG_FUNCTION_ARGS) /* Get packages list */ hash_seq_init(&pstat, packagesHash); while ((package = - (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) + (Package *) hash_seq_search(&pstat)) != NULL) { removePackageInternal(package); } @@ -1011,7 +985,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) */ if (packagesHash) { - HashPackageEntry *package; + Package *package; HASH_SEQ_STATUS pstat; int mRecs = NUMVARIABLES, nRecs = 0; @@ -1021,14 +995,14 @@ get_packages_and_variables(PG_FUNCTION_ARGS) /* Get packages list */ hash_seq_init(&pstat, packagesHash); while ((package = - (HashPackageEntry *) hash_seq_search(&pstat)) != NULL) + (Package *) hash_seq_search(&pstat)) != NULL) { - HashVariableEntry *variable; + Variable *variable; HASH_SEQ_STATUS vstat; int i; /* Skip packages marked as deleted */ - if (!getActualStateOfPack(package)->is_valid) + if (!getActualStateOfContainer(package)->is_valid) continue; /* Get variables list for package */ for (i=0; i < 2; i++) @@ -1036,9 +1010,9 @@ get_packages_and_variables(PG_FUNCTION_ARGS) hash_seq_init(&vstat, i ? package->varHashTransact : package->varHashRegular); while ((variable = - (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) + (Variable *) hash_seq_search(&vstat)) != NULL) { - if (!getActualStateOfVar(variable)->is_valid) + if (!getActualStateOfContainer(variable)->is_valid) continue; /* Resize recs if necessary */ if (nRecs >= mRecs) @@ -1048,8 +1022,8 @@ get_packages_and_variables(PG_FUNCTION_ARGS) sizeof(VariableRec) * mRecs); } - recs[nRecs].package = package->name; - recs[nRecs].variable = variable->name; + recs[nRecs].package = getName(package); + recs[nRecs].variable = getName(variable); recs[nRecs].is_transactional = variable->is_transactional; nRecs++; } @@ -1134,7 +1108,7 @@ get_packages_stats(PG_FUNCTION_ARGS) FuncCallContext *funcctx; MemoryContext oldcontext; HASH_SEQ_STATUS *pstat; - HashPackageEntry *package; + Package *package; if (SRF_IS_FIRSTCALL()) { @@ -1177,7 +1151,7 @@ get_packages_stats(PG_FUNCTION_ARGS) /* Get packages list */ pstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; - package = (HashPackageEntry *) hash_seq_search(pstat); + package = (Package *) hash_seq_search(pstat); if (package != NULL) { Datum values[2]; @@ -1191,8 +1165,8 @@ get_packages_stats(PG_FUNCTION_ARGS) memset(nulls, 0, sizeof(nulls)); /* Fill data */ - values[0] = PointerGetDatum(cstring_to_text(package->name)); - if (getActualStateOfPack(package)->is_valid) + values[0] = PointerGetDatum(cstring_to_text(getName(package))); + if (getActualStateOfContainer(package)->is_valid) getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); totalSpace = regularSpace + transactSpace; @@ -1243,7 +1217,7 @@ ensurePackagesHashExists(void) ALLOCSET_DEFAULT_SIZES); ctl.keysize = NAMEDATALEN; - ctl.entrysize = sizeof(HashPackageEntry); + ctl.entrysize = sizeof(Package); ctl.hcxt = ModuleContext; packagesHash = hash_create("Packages hash", @@ -1255,7 +1229,7 @@ ensurePackagesHashExists(void) * Initialize a hash table with proper vars type */ static void -makePackHTAB(HashPackageEntry *package, bool is_trans) +makePackHTAB(Package *package, bool is_trans) { HASHCTL ctl; char key[NAMEDATALEN], @@ -1272,7 +1246,7 @@ makePackHTAB(HashPackageEntry *package, bool is_trans) snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", is_trans ? "Transactional" : "Regular", key); ctl.keysize = NAMEDATALEN; - ctl.entrysize = sizeof(HashVariableEntry); + ctl.entrysize = sizeof(Variable); ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); if (is_trans) package->varHashTransact = hash_create(hash_name, @@ -1284,11 +1258,11 @@ makePackHTAB(HashPackageEntry *package, bool is_trans) HASH_ELEM | HASH_CONTEXT); } -static HashPackageEntry * +static Package * getPackageByName(text* name, bool create, bool strict) { - HashPackageEntry *package; - PackHistoryEntry *historyEntry; + Package *package; + PackState *packState; char key[NAMEDATALEN]; bool found; @@ -1310,38 +1284,38 @@ getPackageByName(text* name, bool create, bool strict) } /* Find or create a package entry */ - package = (HashPackageEntry *) hash_search(packagesHash, key, - create ? HASH_ENTER : HASH_FIND, - &found); + package = (Package *) hash_search(packagesHash, key, + create ? HASH_ENTER : HASH_FIND, + &found); if (found) { - if (getActualStateOfPack(package)->is_valid) + if (getActualStateOfContainer(package)->is_valid) return package; else if (create) { HASH_SEQ_STATUS vstat; - HashVariableEntry *variable; + Variable *variable; /* Make new history entry of package */ - if (!isPackChangedInCurrentTrans(package)) + if (!isObjectChangedInCurrentTrans(&package->transObject)) { createSavepointPack(package); addToChangedPacks(package); } - getActualStateOfPack(package)->is_valid = true; + getActualStateOfContainer(package)->is_valid = true; /* Restore previously removed HTAB for regular variables */ makePackHTAB(package, false); /* Mark all transactional variables in package as removed */ hash_seq_init(&vstat, package->varHashTransact); while ((variable = - (HashVariableEntry *) hash_seq_search(&vstat)) != NULL) + (Variable *) hash_seq_search(&vstat)) != NULL) { - if (!isVarChangedInCurrentTrans(variable)) + if (!isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); - addToChangedVars(package, variable); + createSavepointVar(variable); + addToChangedVars(variable); } - getActualStateOfVar(variable)->is_valid = false; + getActualStateOfContainer(variable)->is_valid = false; } return package; } @@ -1366,11 +1340,10 @@ getPackageByName(text* name, bool create, bool strict) makePackHTAB(package, false); makePackHTAB(package, true); /* Initialize history */ - dlist_init(&package->history); - historyEntry = MemoryContextAllocZero(ModuleContext, - sizeof(PackHistoryEntry)); - dlist_push_head(&package->history, &historyEntry->node); - historyEntry->is_valid = true; + dlist_init(getStateStorage(package)); + packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); + dlist_push_head(getStateStorage(package), &(packState->state.node)); + packState->state.is_valid = true; /* Add to changes list */ addToChangedPacks(package); return package; @@ -1381,20 +1354,20 @@ getPackageByName(text* name, bool create, bool strict) * Function is useful to request a value of existing variable and * flag 'is_transactional' of this variable is unknown. */ -static HashVariableEntry * -getVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool strict) +static Variable * +getVariableInternal(Package *package, text *name, Oid typid, bool strict) { - HashVariableEntry *variable; + Variable *variable; char key[NAMEDATALEN]; bool found; getKeyFromName(name, key); - variable = (HashVariableEntry *) hash_search(package->varHashRegular, - key, HASH_FIND, &found); + variable = (Variable *) hash_search(package->varHashRegular, + key, HASH_FIND, &found); if (!found) - variable = (HashVariableEntry *) hash_search(package->varHashTransact, - key, HASH_FIND, &found); + variable = (Variable *) hash_search(package->varHashTransact, + key, HASH_FIND, &found); /* Check variable type */ if (found) @@ -1409,7 +1382,7 @@ getVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool stric errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } - if (!getActualStateOfVar(variable)->is_valid && strict) + if (!getActualStateOfContainer(variable)->is_valid && strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); @@ -1430,11 +1403,11 @@ getVariableInternal(HashPackageEntry *package, text *name, Oid typid, bool stric * Function is useful to set new value to variable and * flag 'is_transactional' is known. */ -static HashVariableEntry * -createVariableInternal(HashPackageEntry *package, text *name, Oid typid, +static Variable * +createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional) { - HashVariableEntry *variable; + Variable *variable; char key[NAMEDATALEN]; bool found; @@ -1448,8 +1421,7 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, is_transactional ? "NOT " : ""))); - variable = (HashVariableEntry *) hash_search( - pack_htab(package, is_transactional), + variable = (Variable *) hash_search(pack_htab(package, is_transactional), key, HASH_ENTER, &found); /* Check variable type */ @@ -1472,231 +1444,288 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid, * For each transaction level there should be a corresponding savepoint. * New value should be stored in a last state. */ - if (is_transactional && !isVarChangedInCurrentTrans(variable)) + if (is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) { - createSavepointVar(package, variable); + createSavepointVar(variable); } } else { - VarHistoryEntry *historyEntry; + VarState *varState; /* Variable entry was created, so initialize new variable. */ Assert(variable); variable->typid = typid; + variable->package = package; variable->is_transactional = is_transactional; - dlist_init(&variable->history); - historyEntry = MemoryContextAllocZero(pack_hctx(package, is_transactional), - sizeof(VarHistoryEntry)); + dlist_init(getStateStorage(variable)); + varState = MemoryContextAllocZero(pack_hctx(package, is_transactional), + sizeof(VarState)); - dlist_push_head(&variable->history, &historyEntry->node); + dlist_push_head(getStateStorage(variable), &varState->state.node); if (typid != RECORDOID) { - ScalarVar *scalar = &(historyEntry->value.scalar); + ScalarVar *scalar = &(varState->value.scalar); get_typlenbyval(variable->typid, &scalar->typlen, &scalar->typbyval); - historyEntry->value.scalar.is_null = true; + varState->value.scalar.is_null = true; } - historyEntry->is_valid = true; + varState->state.is_valid = true; } /* If it is necessary, put variable to changedVars */ if (is_transactional) - addToChangedVars(package, variable); + addToChangedVars(variable); return variable; } -/* - * Create a new history point of variable and copy value from - * previous state - */ static void -createSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) +copyValue(VarState *src, VarState *dest, Variable *destVar) { + MemoryContext oldcxt, + destctx; - if (variable->typid == RECORDOID) - { - insert_savepoint(variable, package->hctxTransact); - } - else - { - ScalarVar *scalar; - ValueHistory *history; - VarHistoryEntry *history_entry_new, - *history_entry_prev; - MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(package->hctxTransact); - history = &variable->history; + destctx = destVar->package->hctxTransact; + oldcxt = MemoryContextSwitchTo(destctx); + if (destVar->typid == RECORDOID) + /* copy record value */ + { + bool found; + HASH_SEQ_STATUS *rstat; + HashRecordEntry *item_prev, + *item_new; + RecordVar *record_src = &src->value.record; + RecordVar *record_dest = &dest->value.record; - /* Release memory for variable */ - history_entry_new = palloc0(sizeof(VarHistoryEntry)); - history_entry_prev = getActualStateOfVar(variable); - scalar = &history_entry_new->value.scalar; - *scalar = history_entry_prev->value.scalar; + init_record(record_dest, record_src->tupdesc, destVar); - if (!scalar->is_null) + /* Copy previous history entry into the new one*/ + rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); + hash_seq_init(rstat, record_src->rhash); + while((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) !=NULL) { - scalar->value = datumCopy(history_entry_prev->value.scalar.value, - scalar->typbyval, scalar->typlen); + HashRecordKey k; + + k = item_prev->key; + item_new = (HashRecordEntry *) hash_search(record_dest->rhash, &k, + HASH_ENTER, &found); + item_new->tuple = heap_copytuple(item_prev->tuple); } + } + else + /* copy scalar value */ + { + ScalarVar *scalar = &dest->value.scalar; + *scalar = src->value.scalar; + if (!scalar->is_null) + scalar->value = datumCopy(src->value.scalar.value, + scalar->typbyval, scalar->typlen); else scalar->value = 0; - history_entry_new->is_valid = history_entry_prev->is_valid; - dlist_push_head(history, &history_entry_new->node); - MemoryContextSwitchTo(oldcxt); } + MemoryContextSwitchTo(oldcxt); } -/* - * Remove previous state of variable - */ static void -releaseSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) +freeValue(VarState *varstate, Oid typid) { - ValueHistory *history; - VarHistoryEntry *historyEntry; - - history = &variable->history; - if (dlist_has_next(history, dlist_head_node(history))) + if (typid == RECORDOID) { - VarHistoryEntry *historyEntryToDelete; - dlist_node *nodeToDelete; + /* All records will be freed */ + MemoryContextDelete(varstate->value.record.hctx); + } + else if (varstate->value.scalar.typbyval == false && + varstate->value.scalar.is_null == false) + { + pfree(DatumGetPointer(varstate->value.scalar.value)); + } +} - nodeToDelete = dlist_next_node(history, dlist_head_node(history)); - historyEntryToDelete = getHistoryEntryOfVar(nodeToDelete); +static void +removeState(TransObject *object, TransObjectType type, State *stateToDelete) +{ + if (type == VARIABLE) + { + Variable *var = getObjectContainer(object, Variable); + freeValue(getStateContainer(stateToDelete), var->typid); + } + dlist_delete(&stateToDelete->node); + pfree(stateToDelete); +} - if (variable->typid == RECORDOID) - { - /* 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)); - } +static void +removeObject(TransObject *object, TransObjectType type) +{ + bool found; + HTAB *hash; - dlist_delete(nodeToDelete); - pfree(historyEntryToDelete); - } - /* Change subxact level due to release */ - historyEntry = getActualStateOfVar(variable); - historyEntry->level--; - if (!historyEntry->is_valid && - !dlist_has_next(history, dlist_head_node(history))) + if (type == PACKAGE) { - bool found; - - hash_search(package->varHashTransact, variable->name, HASH_REMOVE, &found); + Package *package = getObjectContainer(object, Package); + /* + * Delete a variable from the change history of the overlying + * transaction level (head of 'changesStack' at this point) + */ + if (!dlist_is_empty(changesStack)) + removeFromChangedVars(package); + /* Regular variables had already removed */ + MemoryContextDelete(package->hctxTransact); + hash = packagesHash; } + else + hash = getObjectContainer(object, Variable)->package->varHashTransact; + + /* Remove all object's states */ + while(!dlist_is_empty(&object->stateStorage)) + removeState(object, type, getActualState(object)); + /* Remove object from hash table */ + hash_search(hash, object->name, HASH_REMOVE, &found); } /* - * Rollback variable to previous state and remove current value + * Create a new state of object */ static void -rollbackSavepointVar(HashPackageEntry *package, HashVariableEntry *variable) +createSavepoint(TransObject *object, TransObjectType type) { - cleanVariableCurrentState(variable); - - /* Remove variable if it was created in rolled back transaction */ - if (dlist_is_empty(&variable->history)) + StateStorage *stateStorage; + State *newState, + *prevState; + + stateStorage = &object->stateStorage; + prevState = getActualState(object); + if (type==PACKAGE) + newState = &((PackState *)MemoryContextAllocZero(ModuleContext, + sizeof(PackState)))->state; + else { - bool found; - - hash_search(package->varHashTransact, variable->name, HASH_REMOVE, &found); + Variable *var = getObjectContainer(object, Variable); + newState = &((VarState *)MemoryContextAllocZero(var->package->hctxTransact, + sizeof(VarState)))->state; + copyValue(getStateContainer(prevState), getStateContainer(newState), var); } + dlist_push_head(stateStorage, &newState->node); + newState->is_valid = prevState->is_valid; +} + +/* + * Wrapper functions for convinient use + */ +static inline void +createSavepointVar(Variable *variable) +{ + createSavepoint(&variable->transObject, VARIABLE); +} + +static inline void +createSavepointPack(Package *package) +{ + createSavepoint(&package->transObject, PACKAGE); } /* - * Create a new history point of variable and copy value from - * previous state + * Rollback object to its previous state */ static void -createSavepointPack(HashPackageEntry *package) +rollbackSavepoint(TransObject *object, TransObjectType type) { - PackHistory *history; - PackHistoryEntry *history_entry_new, - *history_entry_prev; - - history = &package->history; - history_entry_new = MemoryContextAllocZero(ModuleContext, sizeof(PackHistoryEntry)); - history_entry_prev = getActualStateOfPack(package); - history_entry_new->is_valid = history_entry_prev->is_valid; - dlist_push_head(history, &history_entry_new->node); + Package *package; + State *state; + state = getActualState(object); + if (type == PACKAGE) + { + if (!state->is_valid) + { + package = getObjectContainer(object, Package); + dlist_pop_head_node(&object->stateStorage); + pfree(state); + /* Restore regular vars HTAB */ + makePackHTAB(package, false); + } + } + else + { + package = getObjectContainer(object, Variable)->package; + /* Remove current state */ + removeState(object, VARIABLE, state); + + /* Remove variable if it was created in rolled back transaction */ + if (dlist_is_empty(&object->stateStorage)) + removeObject(object, VARIABLE); + } } /* - * Remove previous state of variable + * Remove previous state of object */ static void -releaseSavepointPack(HashPackageEntry *package) +releaseSavepoint(TransObject *object, TransObjectType type) { - PackHistory *history; - PackHistoryEntry *historyEntry; + StateStorage *stateStorage; - Assert(getActualStateOfPack(package)->level == - GetCurrentTransactionNestLevel()); - history = &package->history; + Assert(getActualState(object)->level == GetCurrentTransactionNestLevel()); + stateStorage = &object->stateStorage; - /* Package existed in parent transaction */ - if (dlist_has_next(history, dlist_head_node(history))) + /* Object existed in parent transaction */ + if (dlist_has_next(stateStorage, dlist_head_node(stateStorage))) { - PackHistoryEntry *historyEntryToDelete; + State *stateToDelete; dlist_node *nodeToDelete; - nodeToDelete = dlist_next_node(history, dlist_head_node(history)); - historyEntryToDelete = dlist_container(PackHistoryEntry, node, nodeToDelete); - - dlist_delete(nodeToDelete); - pfree(historyEntryToDelete); + /* Remove previous state */ + nodeToDelete = dlist_next_node(stateStorage, dlist_head_node(stateStorage)); + stateToDelete = dlist_container(State, node, nodeToDelete); + removeState(object, type, stateToDelete); } - /* Package has no previous states and can be completely removed if necessary*/ - if (!getActualStateOfPack(package)->is_valid && - !dlist_has_next(history, dlist_head_node(history))) + /* Object has no more previous states and can be completely removed if necessary*/ + if (!getActualState(object)->is_valid && + !dlist_has_next(stateStorage, dlist_head_node(stateStorage))) { - bool found; - /* Regular variables had already removed */ - MemoryContextDelete(package->hctxTransact); - /* Remove package from packagesHash */ - hash_search(packagesHash, package->name, HASH_REMOVE, &found); - /* - * Delete a variable from the change history of the overlying - * transaction level (head of 'changesStack' at this point) - */ - if (!dlist_is_empty(changesStack)) - removeFromChangedVars(package); + removeObject(object, type); } - /* Change subxact level due to release */ else { - historyEntry = getActualStateOfPack(package); - historyEntry->level--; + State *state; + state = getActualState(object); + state->level--; } } /* - * Rollback variable to previous state and remove current value + * Check if object was changed in current transaction level */ -static void -rollbackSavepointPack(HashPackageEntry *package) +static bool +isObjectChangedInCurrentTrans(TransObject *object) { - PackHistoryEntry *historyEntryToDelete; + State *state; - historyEntryToDelete = getActualStateOfPack(package); - if (historyEntryToDelete->is_valid) - releaseSavepointPack(package); - else + if (!changesStack) + return false; + + state = getActualState(object); + return state->level == GetCurrentTransactionNestLevel(); +} + +/* + * Check if object was changed in parent transaction level + */ +static bool +isObjectChangedInUpperTrans(TransObject *object) +{ + State *cur_state, + *prev_state; + + cur_state = getActualState(object); + if (dlist_has_next(&object->stateStorage, &cur_state->node)) { - dlist_pop_head_node(&package->history); - /* Create regular vars HTAB if it was removed */ - if (!historyEntryToDelete->is_valid) - makePackHTAB(package, false); - pfree(historyEntryToDelete); + prev_state = dlist_container(State, node, cur_state->node.next); + return prev_state->level == GetCurrentTransactionNestLevel() - 1; } + + return false; } /* @@ -1758,125 +1787,91 @@ prepareChangesStack(void) } /* - * Check if object was changed in current transaction level + * Initialize an instance of ChangedObject datatype */ -#define IS_CHANGED_IN_CURRENT_TEMPLATE(type, argtype) \ -static bool \ -is##type##ChangedInCurrentTrans(argtype *object) \ -{ \ - type##HistoryEntry *state; \ - \ - if (!changesStack) \ - return false; \ - \ - state = getActualStateOf##type(object); \ - return state->level == GetCurrentTransactionNestLevel(); \ -} +static inline ChangedObject * +makeChangedObject(TransObject *object, MemoryContext ctx) +{ + ChangedObject *co; -/* - * Check if object was changed in parent transaction level - */ -#define IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(type, argtype) \ -static bool \ -is##type##ChangedInUpperTrans(argtype *object) \ -{ \ - type##HistoryEntry *state, \ - *prev_state; \ - \ - state = getActualStateOf##type(object); \ - if (dlist_has_next(&object->history, &state->node))\ - { \ - prev_state = getHistoryEntryOf##type(state->node.next); \ - return prev_state->level == GetCurrentTransactionNestLevel() - 1; \ - } \ - return false; \ + co = MemoryContextAllocZero(ctx, sizeof(ChangedObject)); + co->object = object; + return co; } /* - * Initialize an instance of Changed(Pack|Var)Node datatype + * Add an object to the list of created, removed, or changed objects + * in current transaction level */ -#define MAKE_CHANGES_NODE_TEMPLATE(type) \ -static inline Changed##type##sNode * \ -makeChanged##type##sNode(MemoryContext ctx, HashPackageEntry *package FUNC_ARG) \ -{ \ - Changed##type##sNode *list_node; \ - \ - list_node = MemoryContextAllocZero(ctx, sizeof(Changed##type##sNode)); \ - list_node->package = package; \ - ASSIGN_VAR \ - return list_node; \ +static void +addToChangesStack(TransObject *object, TransObjectType type) +{ + prepareChangesStack(); + if (!isObjectChangedInCurrentTrans(object)) + { + ChangesStackNode *csn; + ChangedObject *co; + + csn = get_actual_changes_list(); + co = makeChangedObject(object, csn->ctx); + dlist_push_head(type == PACKAGE ? csn->changedPacksList : + csn->changedVarsList, &co->node); + + /* Give this object current subxact level */ + getActualState(object)->level = GetCurrentTransactionNestLevel(); + } } + /* - * Add an object to list of created or removed packs in current transaction level + * Wrapper functions for convinient use */ -#define ADD_TO_CHANGES_TEMPLATE(type, object) \ -static void \ -addToChanged##type##s(HashPackageEntry *package FUNC_ARG) \ -{ \ - prepareChangesStack(); \ - if (!is##type##ChangedInCurrentTrans(object)) \ - { \ - ChangesStackNode *csn; \ - Changed##type##sNode *list_node; \ - \ - csn = get_actual_changes_list(); \ - list_node = makeChanged##type##sNode(csn->ctx, package MAKE_ARG); \ - dlist_push_head(csn->changed##type##sList, &list_node->node); \ - \ - getActualStateOf##type(list_node->object)->level = \ - GetCurrentTransactionNestLevel(); \ - } \ +static inline void +addToChangedPacks(Package *package) +{ + addToChangesStack(&package->transObject, PACKAGE); } -/* Functions to process changes history of packages */ -#define FUNC_ARG -#define ASSIGN_VAR -#define MAKE_ARG -MAKE_CHANGES_NODE_TEMPLATE(Pack) -ADD_TO_CHANGES_TEMPLATE(Pack, package) -IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(Pack, HashPackageEntry) -IS_CHANGED_IN_CURRENT_TEMPLATE(Pack, HashPackageEntry) -#undef FUNC_ARG -#undef ASSIGN_VAR -#undef MAKE_ARG - -/* Functions to process changes history of variables */ -#define FUNC_ARG , HashVariableEntry *variable -#define ASSIGN_VAR list_node->variable = variable; -#define MAKE_ARG , variable -MAKE_CHANGES_NODE_TEMPLATE(Var) -ADD_TO_CHANGES_TEMPLATE(Var, variable) -IS_CHANGED_IN_UPPER_TRANS_TEMPLATE(Var, HashVariableEntry) -IS_CHANGED_IN_CURRENT_TEMPLATE(Var, HashVariableEntry) -#undef FUNC_ARG -#undef ASSIGN_VAR -#undef MAKE_ARG +static inline void +addToChangedVars(Variable *variable) +{ + addToChangesStack(&variable->transObject, VARIABLE); +} /* * Remove from the changes list a deleted package */ static void -removeFromChangedVars(HashPackageEntry *package) +removeFromChangedVars(Package *package) { -#define REMOVE(type) \ - { \ - dlist_mutable_iter miter##type; \ - dlist_head *changed##type##List; \ - changed##type##List = get_actual_changes_list()->changed##type##List; \ - dlist_foreach_modify(miter##type, changed##type##List) \ - { \ - Changed##type##Node *list_node = \ - dlist_container(Changed##type##Node, node, miter##type.cur); \ - if (list_node->package == package) \ - dlist_delete(&list_node->node); \ - } \ - } - - REMOVE(Vars); - REMOVE(Packs); - -#undef REMOVE + dlist_mutable_iter var_miter, + pack_miter; + dlist_head *changedVarsList, + *changedPacksList; + + /* First remove corresponding variables from changedVarsList */ + changedVarsList = get_actual_changes_list()->changedVarsList; + dlist_foreach_modify(var_miter, changedVarsList) + { + ChangedObject *co_cur = dlist_container(ChangedObject, node, + var_miter.cur); + Variable *var = getObjectContainer(co_cur->object, Variable); + if (var->package == package) + dlist_delete(&co_cur->node); + } + /* Now remove package itself from changedPacksList */ + changedPacksList = get_actual_changes_list()->changedPacksList; + dlist_foreach_modify(pack_miter, changedPacksList) + { + ChangedObject *co_cur = dlist_container(ChangedObject, node, + pack_miter.cur); + Package *pack = getObjectContainer(co_cur->object, Package); + if (pack == package) + { + dlist_delete(&co_cur->node); + break; + } + } } /* @@ -1897,102 +1892,74 @@ static void processChanges(Action action) { - ChangesStackNode *bottom_list; - dlist_iter var_iter, - pack_iter; + ChangesStackNode *bottom_list; + int i; Assert(changesStack && changesStackContext); /* List removed from stack but we still can use it */ bottom_list = dlist_container(ChangesStackNode, node, dlist_pop_head_node(changesStack)); - /* Proceed variables first */ - dlist_foreach(var_iter, bottom_list->changedVarsList) + /* i: + * 1 - manage variables + * 0 - manage packages + */ + for (i = 1; i > -1; i--) { - ChangedVarsNode *cvn_cur = dlist_container(ChangedVarsNode, node, var_iter.cur); - - switch (action) + dlist_iter iter; + dlist_foreach(iter, i ? bottom_list->changedVarsList : + bottom_list->changedPacksList) { - case ROLLBACK_TO_SAVEPOINT: - rollbackSavepointVar(cvn_cur->package, cvn_cur->variable); - break; - case RELEASE_SAVEPOINT: - /* - * If package was removed in current transaction level - * mark var as removed. - * We do not check pack_state->level, because var cannot get in - * list of changes until pack is removed. - */ - if (!getActualStateOfPack(cvn_cur->package)->is_valid) - getActualStateOfVar(cvn_cur->variable)->is_valid = false; - - /* Did this variable change at parent level? */ - if (dlist_is_empty(changesStack) || - isVarChangedInUpperTrans(cvn_cur->variable)) - { - /* We just have to drop previous state */ - releaseSavepointVar(cvn_cur->package, cvn_cur->variable); - } - else - { - ChangedVarsNode *cvn_new; - ChangesStackNode *csn; + ChangedObject *co = dlist_container(ChangedObject, node, iter.cur); + TransObject *object = co->object; + switch (action) + { + case ROLLBACK_TO_SAVEPOINT: + rollbackSavepoint(object, i ? VARIABLE : PACKAGE); + break; + case RELEASE_SAVEPOINT: /* - * Impossible to push in upper list existing node because - * it was created in another context - */ - csn = dlist_head_element(ChangesStackNode, node, changesStack); - cvn_new = makeChangedVarsNode(csn->ctx, cvn_cur->package, - cvn_cur->variable); - dlist_push_head(csn->changedVarsList, &cvn_new->node); - - /* Change subxact level due to release */ - getActualStateOfVar(cvn_new->variable)->level--; - } - break; - } - } - - /* Proceed packages */ - dlist_foreach(pack_iter, bottom_list->changedPacksList) - { - ChangedPacksNode *cpn_cur = dlist_container(ChangedPacksNode, node, - pack_iter.cur); + * If package was removed in current transaction level + * mark var as removed. + * We do not check pack_state->level, because var cannot get in + * list of changes until pack is removed. + */ + if (i) + { + Variable *variable = getObjectContainer(object, Variable); + Package *package = variable->package; + if (!getActualStateOfContainer(package)->is_valid) + getActualState(object)->is_valid = false; + } - switch (action) - { - case ROLLBACK_TO_SAVEPOINT: - if (!getActualStateOfPack(cpn_cur->package)->is_valid) - { - rollbackSavepointPack(cpn_cur->package); + /* Did this object change at parent level? */ + if (dlist_is_empty(changesStack) || + isObjectChangedInUpperTrans(object)) + { + /* We just have to drop previous state */ + releaseSavepoint(object, i ? VARIABLE : PACKAGE); + } + else + { + /* Mark object as changed at parent level */ + ChangedObject *co_new; + ChangesStackNode *csn; + + /* + * Impossible to push in upper list existing node because + * it was created in another context + */ + csn = dlist_head_element(ChangesStackNode, node, changesStack); + co_new = makeChangedObject(object, csn->ctx); + dlist_push_head(i ? csn->changedVarsList : + csn->changedPacksList, &co_new->node); + + /* Change subxact level due to release */ + getActualState(object)->level--; + } break; - } - case RELEASE_SAVEPOINT: - /* Did this package change at parent level? */ - if (dlist_is_empty(changesStack) || - isPackChangedInUpperTrans(cpn_cur->package)) - { - /* We just have to drop previous state */ - releaseSavepointPack(cpn_cur->package); - } - else - { - ChangedPacksNode *cpn_new; - ChangesStackNode *csn; - - /* - * Impossible to push in upper list existing node because - * it was created in another context - */ - csn = dlist_head_element(ChangesStackNode, node, changesStack); - cpn_new = makeChangedPacksNode(csn->ctx, cpn_cur->package); - dlist_push_head(csn->changedPacksList, &cpn_new->node); - - /* Change subxact level due to release */ - getActualStateOfPack(cpn_new->package)->level--; - } - break; + } } } diff --git a/pg_variables.h b/pg_variables.h index 7e90908..b691710 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -32,27 +32,6 @@ #define NUMPACKAGES 8 #define NUMVARIABLES 16 -/* List node that stores one of the package states */ -typedef struct PackHistoryEntry{ - dlist_node node; - bool is_valid; - int level; -} PackHistoryEntry; - -typedef dlist_head PackHistory; - -typedef struct HashPackageEntry -{ - char name[NAMEDATALEN]; - HTAB *varHashRegular, - *varHashTransact; - /* Memory context for package variables for easy memory release */ - MemoryContext hctxRegular, - hctxTransact; - PackHistory history; - -} HashPackageEntry; - typedef struct RecordVar { HTAB *rhash; @@ -73,34 +52,62 @@ typedef struct ScalarVar int16 typlen; } ScalarVar; -/* List node that stores one of the variables states */ -typedef struct VarHistoryEntry{ - dlist_node node; +/* State of TransObject instance */ +typedef struct State +{ + dlist_node node; + bool is_valid; + int level; +} State; + +/* List node that stores one of the package's states */ +typedef struct PackState +{ + State state; +} PackState; + +/* List node that stores one of the variable's states */ +typedef struct VarState{ + State state; union { ScalarVar scalar; RecordVar record; } value; - /* Transaction nest level of current entry */ - int level; - bool is_valid; -} VarHistoryEntry; +} VarState; -typedef dlist_head ValueHistory; +typedef dlist_head StateStorage; + +/* Transactional object */ +typedef struct TransObject +{ + char name[NAMEDATALEN]; + StateStorage stateStorage; +} TransObject; + +/* Transactional package */ +typedef struct Package +{ + TransObject transObject; + HTAB *varHashRegular, + *varHashTransact; + /* Memory context for package variables for easy memory release */ + MemoryContext hctxRegular, + hctxTransact; +} Package; -/* Variable by itself */ -typedef struct HashVariableEntry +/* Transactional variable */ +typedef struct Variable { - char name[NAMEDATALEN]; - /* Entry point to list with states of value */ - ValueHistory history; + TransObject transObject; + Package *package; Oid typid; /* * The flag determines the further behavior of the variable. * Can be specified only when creating a variable. */ bool is_transactional; -} HashVariableEntry; +} Variable; typedef struct HashRecordKey { @@ -118,20 +125,19 @@ typedef struct HashRecordEntry HeapTuple tuple; } HashRecordEntry; -/* Element of list with variables, changed within transaction */ -typedef struct ChangedVarsNode +/* Element of list with objects created, changed or removed within transaction */ +typedef struct ChangedObject { - dlist_node node; - HashPackageEntry *package; - HashVariableEntry *variable; -} ChangedVarsNode; + dlist_node node; + TransObject *object; +} ChangedObject; -/* Element of list with packages, removed within transaction */ -typedef struct ChangedPacksNode +/* Type of transactional object instance */ +typedef enum TransObjectType { - dlist_node node; - HashPackageEntry *package; -} ChangedPacksNode; + PACKAGE, + VARIABLE +} TransObjectType; /* Element of stack with 'changedVars' and 'changedPacks' list heads*/ typedef struct ChangesStackNode @@ -142,34 +148,52 @@ typedef struct ChangesStackNode MemoryContext ctx; } ChangesStackNode; -extern void init_attributes(HashVariableEntry* variable, TupleDesc tupdesc, - MemoryContext topctx); -extern void check_attributes(HashVariableEntry *variable, TupleDesc tupdesc); -extern void check_record_key(HashVariableEntry *variable, Oid typid); +extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable); +extern void check_attributes(Variable *variable, TupleDesc tupdesc); +extern void check_record_key(Variable *variable, Oid typid); -extern void insert_record(HashVariableEntry* variable, +extern void insert_record(Variable* variable, HeapTupleHeader tupleHeader); -extern bool update_record(HashVariableEntry *variable, +extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); -extern bool delete_record(HashVariableEntry* variable, Datum value, +extern bool delete_record(Variable* variable, Datum value, bool is_null); -extern void clean_records(HashVariableEntry *variable); -extern void insert_savepoint(HashVariableEntry *variable, - MemoryContext packageContext); +/* Internal getters */ +/* pack-var */ +#define getActualStateOfContainer(object) \ + (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ + (dlist_head_element(State, node, &(object->transObject.stateStorage)))) -/* Internal macros to manage with dlist structure */ #define getActualValueScalar(variable) \ - (&((dlist_head_element(VarHistoryEntry, node, &variable->history))->value.scalar)) + (AssertVariableIsOfTypeMacro(*variable, Variable), \ + &(((VarState *) getActualStateOfContainer(variable) - \ + offsetof(VarState, state))->value.scalar)) + #define getActualValueRecord(variable) \ - (&((dlist_head_element(VarHistoryEntry, node, &variable->history))->value.record)) -#define getActualStateOfVar(variable) \ - (dlist_head_element(VarHistoryEntry, node, &variable->history)) -#define getActualStateOfPack(package) \ - (dlist_head_element(PackHistoryEntry, node, &package->history)) -#define getHistoryEntryOfVar(node_ptr) \ - dlist_container(VarHistoryEntry, node, node_ptr) -#define getHistoryEntryOfPack(node_ptr) \ - dlist_container(PackHistoryEntry, node, node_ptr) + (AssertVariableIsOfTypeMacro(*variable, Variable), \ + &(((VarState *) getActualStateOfContainer(variable) - \ + offsetof(VarState, state))->value.record)) + +#define getName(object) \ + (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ + object->transObject.name) + +#define getStateStorage(object) \ + (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ + &(object->transObject.stateStorage)) + +/* State */ +#define getStateContainer(state_ptr) \ + ((VarState *) state_ptr - offsetof(VarState, state)) + +/* TransObject */ +#define getObjectContainer(object_ptr, type) \ + (AssertVariableIsOfTypeMacro(object_ptr, TransObject *), \ + (type *) object_ptr - offsetof(type, transObject)) + +#define getActualState(transObject) \ + (AssertVariableIsOfTypeMacro(transObject, TransObject *), \ + (dlist_head_element(State, node, &(transObject->stateStorage)))) #endif /* __PG_VARIABLES_H__ */ diff --git a/pg_variables_record.c b/pg_variables_record.c index 2c5ae6a..42a18aa 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -64,21 +64,22 @@ record_match(const void *key1, const void *key2, Size keysize) } void -init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, - MemoryContext topctx) +init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - MemoryContext oldcxt; - RecordVar *record; + HASHCTL ctl; + char hash_name[BUFSIZ]; + MemoryContext oldcxt, + topctx; TypeCacheEntry *typentry; - Oid keyid; + Oid keyid; Assert(variable->typid == RECORDOID); - sprintf(hash_name, "Records hash for variable \"%s\"", variable->name); + sprintf(hash_name, "Records hash for variable \"%s\"", getName(variable)); - record = getActualValueRecord(variable); + topctx = variable->is_transactional ? + variable->package->hctxTransact : + variable->package->hctxRegular; #if PG_VERSION_NUM >= 110000 record->hctx = AllocSetContextCreateExtended(topctx, @@ -136,7 +137,7 @@ init_attributes(HashVariableEntry *variable, TupleDesc tupdesc, * New record structure should be the same as the first record. */ void -check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) +check_attributes(Variable *variable, TupleDesc tupdesc) { int i; RecordVar *record; @@ -149,7 +150,7 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record structure differs from variable \"%s\" " - "structure", variable->name))); + "structure", getName(variable)))); /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) @@ -163,7 +164,7 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record structure differs from variable \"%s\" " - "structure", variable->name))); + "structure", getName(variable)))); } } @@ -171,7 +172,7 @@ check_attributes(HashVariableEntry *variable, TupleDesc tupdesc) * Check record key type. If not same then throw a error. */ void -check_record_key(HashVariableEntry *variable, Oid typid) +check_record_key(Variable *variable, Oid typid) { RecordVar *record; @@ -182,14 +183,14 @@ check_record_key(HashVariableEntry *variable, Oid typid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested value type differs from variable \"%s\" " - "key type", variable->name))); + "key type", getName(variable)))); } /* * Insert a new record. New record key should be unique in the variable. */ void -insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) +insert_record(Variable *variable, HeapTupleHeader tupleHeader) { TupleDesc tupdesc; HeapTuple tuple; @@ -237,7 +238,7 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("there is a record in the variable \"%s\" with same " - "key", variable->name))); + "key", getName(variable)))); } /* Second, insert a new record */ item->tuple = tuple; @@ -249,7 +250,7 @@ insert_record(HashVariableEntry *variable, HeapTupleHeader tupleHeader) * Insert a record. New record key should be unique in the variable. */ bool -update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) +update_record(Variable* variable, HeapTupleHeader tupleHeader) { TupleDesc tupdesc; HeapTuple tuple; @@ -305,7 +306,7 @@ update_record(HashVariableEntry* variable, HeapTupleHeader tupleHeader) } bool -delete_record(HashVariableEntry *variable, Datum value, bool is_null) +delete_record(Variable *variable, Datum value, bool is_null) { HashRecordKey k; HashRecordEntry *item; @@ -329,60 +330,3 @@ delete_record(HashVariableEntry *variable, Datum value, bool is_null) return found; } - -/* - * Free allocated space for records hash table and datums array. - */ -void -clean_records(HashVariableEntry *variable) -{ - RecordVar *record; - - Assert(variable->typid == RECORDOID); - - record = getActualValueRecord(variable); - /* All records will be freed */ - 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; - VarHistoryEntry *history_entry_new; - HASH_SEQ_STATUS *rstat; - bool found; - MemoryContext oldcxt; - - Assert(variable->typid == RECORDOID); - - /* Create new history entry */ - record_prev = getActualValueRecord(variable); - oldcxt = MemoryContextSwitchTo(packageContext); - history_entry_new = palloc0(sizeof(VarHistoryEntry)); - history_entry_new->is_valid = true; - record_new = &(history_entry_new->value.record); - dlist_push_head(&variable->history, &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_trans.sql b/sql/pg_variables_trans.sql index cf7a28f..bbfc952 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -437,7 +437,7 @@ BEGIN; SAVEPOINT sp1; SAVEPOINT sp2; SAVEPOINT sp3; -SELECT pgv_set('vars2', 'trans', 'variable exists'::text, true); +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); SAVEPOINT sp4; SAVEPOINT sp5; SELECT pgv_free(); From e306b3fa607c636db20d0e132fb285bf313270a2 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 20 Jun 2018 00:36:38 +0300 Subject: [PATCH 037/147] Removed forgotten string --- pg_variables.c | 1 - 1 file changed, 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index 62d445d..0549870 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1647,7 +1647,6 @@ rollbackSavepoint(TransObject *object, TransObjectType type) } else { - package = getObjectContainer(object, Variable)->package; /* Remove current state */ removeState(object, VARIABLE, state); From 370ddfe34dfb739a02ae2bcba28864128f972809 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 20 Jun 2018 15:01:06 +0300 Subject: [PATCH 038/147] Fix a valid state of a newly created variable after removing its package --- expected/pg_variables_trans.out | 37 +++++++++++++++++++++++++++++++++ pg_variables.c | 2 +- sql/pg_variables_trans.sql | 9 ++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index dbf21d1..910f505 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1801,3 +1801,40 @@ SELECT package FROM pgv_stats(); --------- (0 rows) +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +COMMIT; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 0549870..01db1c1 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1471,8 +1471,8 @@ createVariableInternal(Package *package, text *name, Oid typid, &scalar->typbyval); varState->value.scalar.is_null = true; } - varState->state.is_valid = true; } + getActualStateOfContainer(variable)->is_valid = true; /* If it is necessary, put variable to changedVars */ if (is_transactional) addToChangedVars(variable); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index bbfc952..d59843a 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -451,3 +451,12 @@ SELECT package FROM pgv_stats(); SELECT * FROM pgv_list() ORDER BY package, name; COMMIT; SELECT package FROM pgv_stats(); + +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); +SELECT pgv_remove('vars'); +SELECT * FROM pgv_list() ORDER BY package, name; +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); +SELECT * FROM pgv_list() ORDER BY package, name; +COMMIT; +SELECT pgv_remove('vars'); From 41d32ff3ed0175590e2d26644e210471d95dcc07 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 20 Jun 2018 18:13:43 +0300 Subject: [PATCH 039/147] Fixed insert in cached variables --- pg_variables.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 01db1c1..ed54e1f 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -325,8 +325,8 @@ variable_insert(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastPackage)) || - strncmp(VARDATA_ANY(var_name), getName(LastPackage), + VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), getName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = createVariableInternal(package, var_name, RECORDOID, From 8bc1289aaa39979a213c7189bfe35105d57a2c7f Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 21 Jun 2018 14:19:07 +0300 Subject: [PATCH 040/147] Refactor transaction state structures and macroses --- pg_variables.c | 414 ++++++++++++++++++++++-------------------- pg_variables.h | 64 ++----- pg_variables_record.c | 20 +- 3 files changed, 244 insertions(+), 254 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index ed54e1f..d03f5ac 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -65,7 +65,8 @@ static void rollbackSavepoint(TransObject *object, TransObjectType type); static void copyValue(VarState *src, VarState *dest, Variable *destVar); static void freeValue(VarState *varstate, Oid typid); -static void removeState(TransObject *object, TransObjectType type, State *stateToDelete); +static void removeState(TransObject *object, TransObjectType type, + TransState *stateToDelete); static void removeObject(TransObject *object, TransObjectType type); static bool isObjectChangedInCurrentTrans(TransObject *object); static bool isObjectChangedInUpperTrans(TransObject *object); @@ -74,17 +75,9 @@ static void addToChangesStack(TransObject *object, TransObjectType type); static void pushChangesStack(void); static void removeFromChangedVars(Package *package); -/* Wrappers */ -static inline void createSavepointPack(Package *package); -static inline void createSavepointVar(Variable *variable); -static inline void addToChangedPacks(Package *package); -static inline void addToChangedVars(Variable *variable); - /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); -static inline ChangedObject * -makeChangedObject(TransObject *object, MemoryContext ctx); #define CHECK_ARGS_FOR_NULL() \ @@ -150,13 +143,12 @@ variable_set(text *package_name, text *var_name, Package *package; Variable *variable; ScalarVar *scalar; - MemoryContext oldcxt; package = getPackageByName(package_name, true, false); variable = createVariableInternal(package, var_name, typid, is_transactional); - scalar = getActualValueScalar(variable); + scalar = &(GetActualValue(variable).scalar); /* Release memory for variable */ if (scalar->typbyval == false && scalar->is_null == false) @@ -165,6 +157,8 @@ variable_set(text *package_name, text *var_name, scalar->is_null = is_null; if (!scalar->is_null) { + MemoryContext oldcxt; + oldcxt = MemoryContextSwitchTo(pack_hctx(package, is_transactional)); scalar->value = datumCopy(value, scalar->typbyval, scalar->typlen); MemoryContextSwitchTo(oldcxt); @@ -195,8 +189,10 @@ variable_get(text *package_name, text *var_name, *is_null = true; return 0; } - scalar = getActualValueScalar(variable); + + scalar = &(GetActualValue(variable).scalar); *is_null = scalar->is_null; + return scalar->value; } @@ -312,8 +308,8 @@ variable_insert(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || - strncmp(VARDATA_ANY(package_name), getName(LastPackage), + VARSIZE_ANY_EXHDR(package_name) != strlen(GetName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), GetName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, true, false); @@ -325,8 +321,8 @@ variable_insert(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || - strncmp(VARDATA_ANY(var_name), getName(LastVariable), + VARSIZE_ANY_EXHDR(var_name) != strlen(GetName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = createVariableInternal(package, var_name, RECORDOID, @@ -335,9 +331,9 @@ variable_insert(PG_FUNCTION_ARGS) } else { - if (LastVariable->is_transactional == is_transactional) - variable = LastVariable; - else + TransObject *transObj; + + if (LastVariable->is_transactional != is_transactional) { char key[NAMEDATALEN]; @@ -347,10 +343,15 @@ variable_insert(PG_FUNCTION_ARGS) errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, LastVariable->is_transactional ? "" : "NOT "))); } - if (variable->is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) + + variable = LastVariable; + transObj = &variable->transObject; + + if (variable->is_transactional && + !isObjectChangedInCurrentTrans(transObj)) { - createSavepointVar(variable); - addToChangedVars(variable); + createSavepoint(transObj, TOP_VARIABLE); + addToChangesStack(transObj, TOP_VARIABLE); } } @@ -358,11 +359,12 @@ variable_insert(PG_FUNCTION_ARGS) tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - record = getActualValueRecord(variable); + + record = &(GetActualValue(variable).record); if (!record->tupdesc) { /* - * This is the first record for the var_name. Initialize attributes. + * This is the first record for the var_name. Initialize record. */ init_record(record, tupdesc, variable); } @@ -388,6 +390,7 @@ variable_update(PG_FUNCTION_ARGS) HeapTupleHeader rec; Package *package; Variable *variable; + TransObject *transObject; bool res; Oid tupType; int32 tupTypmod; @@ -408,8 +411,8 @@ variable_update(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || - strncmp(VARDATA_ANY(package_name), getName(LastPackage), + VARSIZE_ANY_EXHDR(package_name) != strlen(GetName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), GetName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, false, true); @@ -421,8 +424,8 @@ variable_update(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || - strncmp(VARDATA_ANY(var_name), getName(LastVariable), + VARSIZE_ANY_EXHDR(var_name) != strlen(GetName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package, var_name, RECORDOID, true); @@ -431,10 +434,12 @@ variable_update(PG_FUNCTION_ARGS) else variable = LastVariable; - if (variable->is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) + transObject = &variable->transObject; + if (variable->is_transactional && + !isObjectChangedInCurrentTrans(transObject)) { - createSavepointVar(variable); - addToChangedVars(variable); + createSavepoint(transObject, TOP_VARIABLE); + addToChangesStack(transObject, TOP_VARIABLE); } /* Update a record */ @@ -464,6 +469,7 @@ variable_delete(PG_FUNCTION_ARGS) bool value_is_null = PG_ARGISNULL(2); Package *package; Variable *variable; + TransObject *transObject; bool res; CHECK_ARGS_FOR_NULL(); @@ -485,8 +491,8 @@ variable_delete(PG_FUNCTION_ARGS) /* Get cached package */ if (LastPackage == NULL || - VARSIZE_ANY_EXHDR(package_name) != strlen(getName(LastPackage)) || - strncmp(VARDATA_ANY(package_name), getName(LastPackage), + VARSIZE_ANY_EXHDR(package_name) != strlen(GetName(LastPackage)) || + strncmp(VARDATA_ANY(package_name), GetName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { package = getPackageByName(package_name, false, true); @@ -498,8 +504,8 @@ variable_delete(PG_FUNCTION_ARGS) /* Get cached variable */ if (LastVariable == NULL || - VARSIZE_ANY_EXHDR(var_name) != strlen(getName(LastVariable)) || - strncmp(VARDATA_ANY(var_name), getName(LastVariable), + VARSIZE_ANY_EXHDR(var_name) != strlen(GetName(LastVariable)) || + strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = getVariableInternal(package, var_name, RECORDOID, true); @@ -508,11 +514,12 @@ variable_delete(PG_FUNCTION_ARGS) else variable = LastVariable; + transObject = &variable->transObject; if (variable->is_transactional && - !isObjectChangedInCurrentTrans(&variable->transObject)) + !isObjectChangedInCurrentTrans(transObject)) { - createSavepointVar(variable); - addToChangedVars(variable); + createSavepoint(transObject, TOP_VARIABLE); + addToChangesStack(transObject, TOP_VARIABLE); } /* Delete a record */ @@ -552,7 +559,7 @@ variable_select(PG_FUNCTION_ARGS) package = getPackageByName(package_name, false, true); variable = getVariableInternal(package, var_name, RECORDOID, true); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -627,7 +634,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) if (!value_is_null) check_record_key(variable, value_type); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); /* Search a record */ k.value = value; @@ -699,7 +706,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = CreateTupleDescCopy( - getActualValueRecord(variable)->tupdesc); + GetActualValue(variable).record.tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -721,7 +728,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) bool found; RecordVar *record; - record = getActualValueRecord(var->variable); + record = &(GetActualValue(var->variable).record); /* Search a record */ k.value = value; k.is_null = isnull; @@ -783,7 +790,7 @@ variable_exists(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found ? (getActualStateOfContainer(variable))->is_valid : found); + PG_RETURN_BOOL(found ? GetActualState(variable)->is_valid : found); } /* @@ -834,11 +841,13 @@ remove_variable(PG_FUNCTION_ARGS) if (found) { /* Regular variable */ - TransObject *object = &variable->transObject; - removeState(object, VARIABLE, getActualState(object)); + removeState(&variable->transObject, TOP_VARIABLE, + GetActualState(variable)); } else { + TransObject *transObject; + variable = (Variable *) hash_search(package->varHashTransact, key, HASH_FIND, &found); /* Variable doesn't exist in both HTAB */ @@ -847,12 +856,13 @@ remove_variable(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); /* Transactional variable */ - if (!isObjectChangedInCurrentTrans(&variable->transObject)) + transObject = &variable->transObject; + if (!isObjectChangedInCurrentTrans(transObject)) { - createSavepointVar(variable); - addToChangedVars(variable); + createSavepoint(transObject, TOP_VARIABLE); + addToChangesStack(transObject, TOP_VARIABLE); } - getActualStateOfContainer(variable)->is_valid = false; + GetActualState(variable)->is_valid = false; } /* Remove variable from cache */ @@ -903,16 +913,19 @@ remove_package(PG_FUNCTION_ARGS) static void removePackageInternal(Package *package) { + TransObject *transObject; + /* All regular variables will be freed */ MemoryContextDelete(package->hctxRegular); /* Add to changes list */ - if (!isObjectChangedInCurrentTrans(&package->transObject)) + transObject = &package->transObject; + if (!isObjectChangedInCurrentTrans(transObject)) { - createSavepointPack(package); - addToChangedPacks(package); + createSavepoint(transObject, TOP_PACKAGE); + addToChangesStack(transObject, TOP_PACKAGE); } - getActualStateOfContainer(package)->is_valid = false; + GetActualState(package)->is_valid = false; } /* @@ -924,15 +937,14 @@ remove_packages(PG_FUNCTION_ARGS) { Package *package; HASH_SEQ_STATUS pstat; + /* There is no any packages and variables */ if (packagesHash == NULL) PG_RETURN_VOID(); - /* Get packages list */ hash_seq_init(&pstat, packagesHash); - while ((package = - (Package *) hash_seq_search(&pstat)) != NULL) + while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { removePackageInternal(package); } @@ -994,15 +1006,14 @@ get_packages_and_variables(PG_FUNCTION_ARGS) /* Get packages list */ hash_seq_init(&pstat, packagesHash); - while ((package = - (Package *) hash_seq_search(&pstat)) != NULL) + while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { Variable *variable; HASH_SEQ_STATUS vstat; int i; /* Skip packages marked as deleted */ - if (!getActualStateOfContainer(package)->is_valid) + if (!GetActualState(package)->is_valid) continue; /* Get variables list for package */ for (i=0; i < 2; i++) @@ -1012,8 +1023,9 @@ get_packages_and_variables(PG_FUNCTION_ARGS) while ((variable = (Variable *) hash_seq_search(&vstat)) != NULL) { - if (!getActualStateOfContainer(variable)->is_valid) + if (!GetActualState(variable)->is_valid) continue; + /* Resize recs if necessary */ if (nRecs >= mRecs) { @@ -1022,8 +1034,8 @@ get_packages_and_variables(PG_FUNCTION_ARGS) sizeof(VariableRec) * mRecs); } - recs[nRecs].package = getName(package); - recs[nRecs].variable = getName(variable); + recs[nRecs].package = GetName(package); + recs[nRecs].variable = GetName(variable); recs[nRecs].is_transactional = variable->is_transactional; nRecs++; } @@ -1165,10 +1177,12 @@ get_packages_stats(PG_FUNCTION_ARGS) memset(nulls, 0, sizeof(nulls)); /* Fill data */ - values[0] = PointerGetDatum(cstring_to_text(getName(package))); - if (getActualStateOfContainer(package)->is_valid) + values[0] = PointerGetDatum(cstring_to_text(GetName(package))); + + if (GetActualState(package)->is_valid) getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); + totalSpace = regularSpace + transactSpace; values[1] = Int64GetDatum(totalSpace); @@ -1290,63 +1304,81 @@ getPackageByName(text* name, bool create, bool strict) if (found) { - if (getActualStateOfContainer(package)->is_valid) + if (GetActualState(package)->is_valid) return package; else if (create) { HASH_SEQ_STATUS vstat; - Variable *variable; + Variable *variable; + TransObject *transObj = &package->transObject; + /* Make new history entry of package */ - if (!isObjectChangedInCurrentTrans(&package->transObject)) + if (!isObjectChangedInCurrentTrans(transObj)) { - createSavepointPack(package); - addToChangedPacks(package); + createSavepoint(transObj, TOP_PACKAGE); + addToChangesStack(transObj, TOP_PACKAGE); } - getActualStateOfContainer(package)->is_valid = true; + + GetActualState(package)->is_valid = true; + + /* XXX Check is this necessary */ + /* Restore previously removed HTAB for regular variables */ makePackHTAB(package, false); + /* Mark all transactional variables in package as removed */ hash_seq_init(&vstat, package->varHashTransact); while ((variable = (Variable *) hash_seq_search(&vstat)) != NULL) { - if (!isObjectChangedInCurrentTrans(&variable->transObject)) + transObj = &variable->transObject; + + if (!isObjectChangedInCurrentTrans(transObj)) { - createSavepointVar(variable); - addToChangedVars(variable); + createSavepoint(transObj, TOP_VARIABLE); + addToChangesStack(transObj, TOP_VARIABLE); } - getActualStateOfContainer(variable)->is_valid = false; + GetActualState(variable)->is_valid = false; } + return package; } - if (strict) + else if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized package \"%s\"", key))); else return NULL; } - if (!create) + else if (!create) { if (strict) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized package \"%s\"", key))); else return package; } + else + { + /* + * Package entry was created, so we need create hash table for + * variables. + */ + makePackHTAB(package, false); + makePackHTAB(package, true); - /* Package entry was created, so we need create hash table for variables. */ - makePackHTAB(package, false); - makePackHTAB(package, true); - /* Initialize history */ - dlist_init(getStateStorage(package)); - packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); - dlist_push_head(getStateStorage(package), &(packState->state.node)); - packState->state.is_valid = true; - /* Add to changes list */ - addToChangedPacks(package); - return package; + /* Initialize history */ + dlist_init(GetStateStorage(package)); + packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); + dlist_push_head(GetStateStorage(package), &(packState->state.node)); + packState->state.is_valid = true; + + /* Add to changes list */ + addToChangesStack(&package->transObject, TOP_PACKAGE); + + return package; + } } /* @@ -1382,7 +1414,7 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } - if (!getActualStateOfContainer(variable)->is_valid && strict) + if (!GetActualState(variable)->is_valid && strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized variable \"%s\"", key))); @@ -1408,13 +1440,19 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional) { Variable *variable; + TransObject *transObject; char key[NAMEDATALEN]; bool found; getKeyFromName(name, key); - hash_search(is_transactional ? package->varHashRegular : package->varHashTransact, - key, HASH_FIND, &found); + /* + * Reverse check: for non-transactional variable search in regular table and + * vice versa. + */ + hash_search(is_transactional ? + package->varHashRegular : package->varHashTransact, + key, HASH_FIND, &found); if (found) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1422,7 +1460,9 @@ createVariableInternal(Package *package, text *name, Oid typid, key, is_transactional ? "NOT " : ""))); variable = (Variable *) hash_search(pack_htab(package, is_transactional), - key, HASH_ENTER, &found); + key, HASH_ENTER, &found); + Assert(variable); + transObject = &variable->transObject; /* Check variable type */ if (found) @@ -1444,9 +1484,10 @@ createVariableInternal(Package *package, text *name, Oid typid, * For each transaction level there should be a corresponding savepoint. * New value should be stored in a last state. */ - if (is_transactional && !isObjectChangedInCurrentTrans(&variable->transObject)) + if (is_transactional && + !isObjectChangedInCurrentTrans(transObject)) { - createSavepointVar(variable); + createSavepoint(transObject, TOP_VARIABLE); } } else @@ -1454,15 +1495,15 @@ createVariableInternal(Package *package, text *name, Oid typid, VarState *varState; /* Variable entry was created, so initialize new variable. */ - Assert(variable); variable->typid = typid; variable->package = package; variable->is_transactional = is_transactional; - dlist_init(getStateStorage(variable)); + + dlist_init(GetStateStorage(variable)); varState = MemoryContextAllocZero(pack_hctx(package, is_transactional), sizeof(VarState)); - dlist_push_head(getStateStorage(variable), &varState->state.node); + dlist_push_head(GetStateStorage(variable), &varState->state.node); if (typid != RECORDOID) { ScalarVar *scalar = &(varState->value.scalar); @@ -1472,10 +1513,11 @@ createVariableInternal(Package *package, text *name, Oid typid, varState->value.scalar.is_null = true; } } - getActualStateOfContainer(variable)->is_valid = true; + + GetActualState(variable)->is_valid = true; /* If it is necessary, put variable to changedVars */ if (is_transactional) - addToChangedVars(variable); + addToChangesStack(transObject, TOP_VARIABLE); return variable; } @@ -1486,9 +1528,9 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) MemoryContext oldcxt, destctx; - destctx = destVar->package->hctxTransact; oldcxt = MemoryContextSwitchTo(destctx); + if (destVar->typid == RECORDOID) /* copy record value */ { @@ -1525,6 +1567,7 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) else scalar->value = 0; } + MemoryContextSwitchTo(oldcxt); } @@ -1544,12 +1587,12 @@ freeValue(VarState *varstate, Oid typid) } static void -removeState(TransObject *object, TransObjectType type, State *stateToDelete) +removeState(TransObject *object, TransObjectType type, TransState *stateToDelete) { - if (type == VARIABLE) + if (type == TOP_VARIABLE) { - Variable *var = getObjectContainer(object, Variable); - freeValue(getStateContainer(stateToDelete), var->typid); + Variable *var = (Variable *) object; + freeValue((VarState *) stateToDelete, var->typid); } dlist_delete(&stateToDelete->node); pfree(stateToDelete); @@ -1561,9 +1604,10 @@ removeObject(TransObject *object, TransObjectType type) bool found; HTAB *hash; - if (type == PACKAGE) + if (type == TOP_PACKAGE) { - Package *package = getObjectContainer(object, Package); + Package *package = (Package *) object; + /* * Delete a variable from the change history of the overlying * transaction level (head of 'changesStack' at this point) @@ -1575,11 +1619,12 @@ removeObject(TransObject *object, TransObjectType type) hash = packagesHash; } else - hash = getObjectContainer(object, Variable)->package->varHashTransact; + hash = ((Variable *) object)->package->varHashTransact; /* Remove all object's states */ - while(!dlist_is_empty(&object->stateStorage)) - removeState(object, type, getActualState(object)); + while(!dlist_is_empty(&object->states)) + removeState(object, type, GetActualState(object)); + /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); } @@ -1588,71 +1633,54 @@ removeObject(TransObject *object, TransObjectType type) * Create a new state of object */ static void -createSavepoint(TransObject *object, TransObjectType type) +createSavepoint(TransObject *transObj, TransObjectType type) { - StateStorage *stateStorage; - State *newState, + TransState *newState, *prevState; - stateStorage = &object->stateStorage; - prevState = getActualState(object); - if (type==PACKAGE) - newState = &((PackState *)MemoryContextAllocZero(ModuleContext, - sizeof(PackState)))->state; + prevState = GetActualState(transObj); + if (type==TOP_PACKAGE) + newState = (TransState *) MemoryContextAllocZero(ModuleContext, + sizeof(PackState)); else { - Variable *var = getObjectContainer(object, Variable); - newState = &((VarState *)MemoryContextAllocZero(var->package->hctxTransact, - sizeof(VarState)))->state; - copyValue(getStateContainer(prevState), getStateContainer(newState), var); + Variable *var = (Variable *) transObj; + + newState = (TransState *) MemoryContextAllocZero(var->package->hctxTransact, + sizeof(VarState)); + copyValue((VarState *) prevState, (VarState *) newState, var); } - dlist_push_head(stateStorage, &newState->node); + dlist_push_head(&transObj->states, &newState->node); newState->is_valid = prevState->is_valid; } -/* - * Wrapper functions for convinient use - */ -static inline void -createSavepointVar(Variable *variable) -{ - createSavepoint(&variable->transObject, VARIABLE); -} - -static inline void -createSavepointPack(Package *package) -{ - createSavepoint(&package->transObject, PACKAGE); -} - /* * Rollback object to its previous state */ static void rollbackSavepoint(TransObject *object, TransObjectType type) { - Package *package; - State *state; - state = getActualState(object); - if (type == PACKAGE) + TransState *state; + + state = GetActualState(object); + if (type == TOP_PACKAGE) { if (!state->is_valid) { - package = getObjectContainer(object, Package); - dlist_pop_head_node(&object->stateStorage); + dlist_pop_head_node(&object->states); pfree(state); /* Restore regular vars HTAB */ - makePackHTAB(package, false); + makePackHTAB((Package *) object, false); } } else { /* Remove current state */ - removeState(object, VARIABLE, state); + removeState(object, TOP_VARIABLE, state); /* Remove variable if it was created in rolled back transaction */ - if (dlist_is_empty(&object->stateStorage)) - removeObject(object, VARIABLE); + if (dlist_is_empty(&object->states)) + removeObject(object, TOP_VARIABLE); } } @@ -1662,33 +1690,33 @@ rollbackSavepoint(TransObject *object, TransObjectType type) static void releaseSavepoint(TransObject *object, TransObjectType type) { - StateStorage *stateStorage; + dlist_head *states; - Assert(getActualState(object)->level == GetCurrentTransactionNestLevel()); - stateStorage = &object->stateStorage; + Assert(GetActualState(object)->level == GetCurrentTransactionNestLevel()); + states = &object->states; /* Object existed in parent transaction */ - if (dlist_has_next(stateStorage, dlist_head_node(stateStorage))) + if (dlist_has_next(states, dlist_head_node(states))) { - State *stateToDelete; + TransState *stateToDelete; dlist_node *nodeToDelete; /* Remove previous state */ - nodeToDelete = dlist_next_node(stateStorage, dlist_head_node(stateStorage)); - stateToDelete = dlist_container(State, node, nodeToDelete); + nodeToDelete = dlist_next_node(states, dlist_head_node(states)); + stateToDelete = dlist_container(TransState, node, nodeToDelete); removeState(object, type, stateToDelete); } /* Object has no more previous states and can be completely removed if necessary*/ - if (!getActualState(object)->is_valid && - !dlist_has_next(stateStorage, dlist_head_node(stateStorage))) + if (!GetActualState(object)->is_valid && + !dlist_has_next(states, dlist_head_node(states))) { removeObject(object, type); } /* Change subxact level due to release */ else { - State *state; - state = getActualState(object); + TransState *state; + state = GetActualState(object); state->level--; } } @@ -1697,14 +1725,14 @@ releaseSavepoint(TransObject *object, TransObjectType type) * Check if object was changed in current transaction level */ static bool -isObjectChangedInCurrentTrans(TransObject *object) +isObjectChangedInCurrentTrans(TransObject *transObj) { - State *state; + TransState *state; if (!changesStack) return false; - state = getActualState(object); + state = GetActualState(transObj); return state->level == GetCurrentTransactionNestLevel(); } @@ -1714,13 +1742,13 @@ isObjectChangedInCurrentTrans(TransObject *object) static bool isObjectChangedInUpperTrans(TransObject *object) { - State *cur_state, + TransState *cur_state, *prev_state; - cur_state = getActualState(object); - if (dlist_has_next(&object->stateStorage, &cur_state->node)) + cur_state = GetActualState(object); + if (dlist_has_next(&object->states, &cur_state->node)) { - prev_state = dlist_container(State, node, cur_state->node.next); + prev_state = dlist_container(TransState, node, cur_state->node.next); return prev_state->level == GetCurrentTransactionNestLevel() - 1; } @@ -1758,8 +1786,8 @@ pushChangesStack(void) csn->changedPacksList = palloc0(sizeof(dlist_head)); csn->ctx = AllocSetContextCreate(changesStackContext, - PGV_MCXT_STACK_NODE, - ALLOCSET_START_SMALL_SIZES); + PGV_MCXT_STACK_NODE, + ALLOCSET_START_SMALL_SIZES); dlist_init(csn->changedVarsList); dlist_init(csn->changedPacksList); @@ -1795,6 +1823,7 @@ makeChangedObject(TransObject *object, MemoryContext ctx) co = MemoryContextAllocZero(ctx, sizeof(ChangedObject)); co->object = object; + return co; } @@ -1803,40 +1832,25 @@ makeChangedObject(TransObject *object, MemoryContext ctx) * in current transaction level */ static void -addToChangesStack(TransObject *object, TransObjectType type) +addToChangesStack(TransObject *transObj, TransObjectType type) { prepareChangesStack(); - if (!isObjectChangedInCurrentTrans(object)) + + if (!isObjectChangedInCurrentTrans(transObj)) { ChangesStackNode *csn; ChangedObject *co; csn = get_actual_changes_list(); - co = makeChangedObject(object, csn->ctx); - dlist_push_head(type == PACKAGE ? csn->changedPacksList : - csn->changedVarsList, &co->node); + co = makeChangedObject(transObj, csn->ctx); + dlist_push_head(type == TOP_PACKAGE ? csn->changedPacksList : + csn->changedVarsList, &co->node); /* Give this object current subxact level */ - getActualState(object)->level = GetCurrentTransactionNestLevel(); + GetActualState(transObj)->level = GetCurrentTransactionNestLevel(); } } - -/* - * Wrapper functions for convinient use - */ -static inline void -addToChangedPacks(Package *package) -{ - addToChangesStack(&package->transObject, PACKAGE); -} - -static inline void -addToChangedVars(Variable *variable) -{ - addToChangesStack(&variable->transObject, VARIABLE); -} - /* * Remove from the changes list a deleted package */ @@ -1854,7 +1868,7 @@ removeFromChangedVars(Package *package) { ChangedObject *co_cur = dlist_container(ChangedObject, node, var_miter.cur); - Variable *var = getObjectContainer(co_cur->object, Variable); + Variable *var = (Variable *) co_cur->object; if (var->package == package) dlist_delete(&co_cur->node); } @@ -1864,7 +1878,7 @@ removeFromChangedVars(Package *package) { ChangedObject *co_cur = dlist_container(ChangedObject, node, pack_miter.cur); - Package *pack = getObjectContainer(co_cur->object, Package); + Package *pack = (Package *) co_cur->object; if (pack == package) { dlist_delete(&co_cur->node); @@ -1890,7 +1904,6 @@ typedef enum Action static void processChanges(Action action) { - ChangesStackNode *bottom_list; int i; @@ -1915,7 +1928,7 @@ processChanges(Action action) switch (action) { case ROLLBACK_TO_SAVEPOINT: - rollbackSavepoint(object, i ? VARIABLE : PACKAGE); + rollbackSavepoint(object, i ? TOP_VARIABLE : TOP_PACKAGE); break; case RELEASE_SAVEPOINT: /* @@ -1926,10 +1939,11 @@ processChanges(Action action) */ if (i) { - Variable *variable = getObjectContainer(object, Variable); + Variable *variable = (Variable *) object; Package *package = variable->package; - if (!getActualStateOfContainer(package)->is_valid) - getActualState(object)->is_valid = false; + + if (!GetActualState(package)->is_valid) + GetActualState(variable)->is_valid = false; } /* Did this object change at parent level? */ @@ -1937,7 +1951,7 @@ processChanges(Action action) isObjectChangedInUpperTrans(object)) { /* We just have to drop previous state */ - releaseSavepoint(object, i ? VARIABLE : PACKAGE); + releaseSavepoint(object, i ? TOP_VARIABLE : TOP_PACKAGE); } else { @@ -1955,7 +1969,7 @@ processChanges(Action action) csn->changedPacksList, &co_new->node); /* Change subxact level due to release */ - getActualState(object)->level--; + GetActualState(object)->level--; } break; } diff --git a/pg_variables.h b/pg_variables.h index b691710..23c266f 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -53,22 +53,23 @@ typedef struct ScalarVar } ScalarVar; /* State of TransObject instance */ -typedef struct State +typedef struct TransState { - dlist_node node; - bool is_valid; - int level; -} State; + dlist_node node; + bool is_valid; + int level; +} TransState; /* List node that stores one of the package's states */ typedef struct PackState { - State state; + TransState state; } PackState; /* List node that stores one of the variable's states */ -typedef struct VarState{ - State state; +typedef struct VarState +{ + TransState state; union { ScalarVar scalar; @@ -76,13 +77,11 @@ typedef struct VarState{ } value; } VarState; -typedef dlist_head StateStorage; - /* Transactional object */ typedef struct TransObject { - char name[NAMEDATALEN]; - StateStorage stateStorage; + char name[NAMEDATALEN]; + dlist_head states; } TransObject; /* Transactional package */ @@ -135,8 +134,8 @@ typedef struct ChangedObject /* Type of transactional object instance */ typedef enum TransObjectType { - PACKAGE, - VARIABLE + TOP_PACKAGE, + TOP_VARIABLE } TransObjectType; /* Element of stack with 'changedVars' and 'changedPacks' list heads*/ @@ -159,41 +158,18 @@ extern bool update_record(Variable *variable, extern bool delete_record(Variable* variable, Datum value, bool is_null); -/* Internal getters */ -/* pack-var */ -#define getActualStateOfContainer(object) \ - (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ - (dlist_head_element(State, node, &(object->transObject.stateStorage)))) - -#define getActualValueScalar(variable) \ - (AssertVariableIsOfTypeMacro(*variable, Variable), \ - &(((VarState *) getActualStateOfContainer(variable) - \ - offsetof(VarState, state))->value.scalar)) +#define GetActualState(object) \ + (dlist_head_element(TransState, node, &((TransObject *) object)->states)) -#define getActualValueRecord(variable) \ - (AssertVariableIsOfTypeMacro(*variable, Variable), \ - &(((VarState *) getActualStateOfContainer(variable) - \ - offsetof(VarState, state))->value.record)) +#define GetActualValue(variable) \ + (((VarState *) GetActualState(variable))->value) -#define getName(object) \ +#define GetName(object) \ (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ object->transObject.name) -#define getStateStorage(object) \ +#define GetStateStorage(object) \ (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ - &(object->transObject.stateStorage)) - -/* State */ -#define getStateContainer(state_ptr) \ - ((VarState *) state_ptr - offsetof(VarState, state)) - -/* TransObject */ -#define getObjectContainer(object_ptr, type) \ - (AssertVariableIsOfTypeMacro(object_ptr, TransObject *), \ - (type *) object_ptr - offsetof(type, transObject)) - -#define getActualState(transObject) \ - (AssertVariableIsOfTypeMacro(transObject, TransObject *), \ - (dlist_head_element(State, node, &(transObject->stateStorage)))) + &(object->transObject.states)) #endif /* __PG_VARIABLES_H__ */ diff --git a/pg_variables_record.c b/pg_variables_record.c index 42a18aa..6a4d399 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -75,7 +75,7 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) Assert(variable->typid == RECORDOID); - sprintf(hash_name, "Records hash for variable \"%s\"", getName(variable)); + sprintf(hash_name, "Records hash for variable \"%s\"", GetName(variable)); topctx = variable->is_transactional ? variable->package->hctxTransact : @@ -144,13 +144,13 @@ check_attributes(Variable *variable, TupleDesc tupdesc) Assert(variable->typid == RECORDOID); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); /* First, check columns count. */ if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record structure differs from variable \"%s\" " - "structure", getName(variable)))); + "structure", GetName(variable)))); /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) @@ -164,7 +164,7 @@ check_attributes(Variable *variable, TupleDesc tupdesc) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record structure differs from variable \"%s\" " - "structure", getName(variable)))); + "structure", GetName(variable)))); } } @@ -177,13 +177,13 @@ check_record_key(Variable *variable, Oid typid) RecordVar *record; Assert(variable->typid == RECORDOID); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); if (GetTupleDescAttr(record->tupdesc, 0)->atttypid != typid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested value type differs from variable \"%s\" " - "key type", getName(variable)))); + "key type", GetName(variable)))); } /* @@ -205,7 +205,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -238,7 +238,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("there is a record in the variable \"%s\" with same " - "key", getName(variable)))); + "key", GetName(variable)))); } /* Second, insert a new record */ item->tuple = tuple; @@ -265,7 +265,7 @@ update_record(Variable* variable, HeapTupleHeader tupleHeader) Assert(variable->typid == RECORDOID); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); oldcxt = MemoryContextSwitchTo(record->hctx); @@ -315,7 +315,7 @@ delete_record(Variable *variable, Datum value, bool is_null) Assert(variable->typid == RECORDOID); - record = getActualValueRecord(variable); + record = &(GetActualValue(variable).record); /* Delete a record */ k.value = value; From 58ecc51f12b796bf9b7d2f48e803af41a1837417 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Thu, 21 Jun 2018 14:56:35 +0300 Subject: [PATCH 041/147] Transaction object types clarifying --- README.md | 2 +- pg_variables.c | 54 +++++++++++++++++++++++++------------------------- pg_variables.h | 4 ++-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7b71cee..f87e233 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ SELECT pgv_get('pack', 'var_text', NULL::text); before transaction block ``` -If you create variable after `BEGIN` or `SAVEPOINT` statements and than rollback +If you create a transactional variable after `BEGIN` or `SAVEPOINT` statements and than rollback to previous state - variable will not be exist: ```sql diff --git a/pg_variables.c b/pg_variables.c index d03f5ac..148cbb5 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -350,8 +350,8 @@ variable_insert(PG_FUNCTION_ARGS) if (variable->is_transactional && !isObjectChangedInCurrentTrans(transObj)) { - createSavepoint(transObj, TOP_VARIABLE); - addToChangesStack(transObj, TOP_VARIABLE); + createSavepoint(transObj, TRANS_VARIABLE); + addToChangesStack(transObj, TRANS_VARIABLE); } } @@ -438,8 +438,8 @@ variable_update(PG_FUNCTION_ARGS) if (variable->is_transactional && !isObjectChangedInCurrentTrans(transObject)) { - createSavepoint(transObject, TOP_VARIABLE); - addToChangesStack(transObject, TOP_VARIABLE); + createSavepoint(transObject, TRANS_VARIABLE); + addToChangesStack(transObject, TRANS_VARIABLE); } /* Update a record */ @@ -518,8 +518,8 @@ variable_delete(PG_FUNCTION_ARGS) if (variable->is_transactional && !isObjectChangedInCurrentTrans(transObject)) { - createSavepoint(transObject, TOP_VARIABLE); - addToChangesStack(transObject, TOP_VARIABLE); + createSavepoint(transObject, TRANS_VARIABLE); + addToChangesStack(transObject, TRANS_VARIABLE); } /* Delete a record */ @@ -841,7 +841,7 @@ remove_variable(PG_FUNCTION_ARGS) if (found) { /* Regular variable */ - removeState(&variable->transObject, TOP_VARIABLE, + removeState(&variable->transObject, TRANS_VARIABLE, GetActualState(variable)); } else @@ -859,8 +859,8 @@ remove_variable(PG_FUNCTION_ARGS) transObject = &variable->transObject; if (!isObjectChangedInCurrentTrans(transObject)) { - createSavepoint(transObject, TOP_VARIABLE); - addToChangesStack(transObject, TOP_VARIABLE); + createSavepoint(transObject, TRANS_VARIABLE); + addToChangesStack(transObject, TRANS_VARIABLE); } GetActualState(variable)->is_valid = false; } @@ -922,8 +922,8 @@ removePackageInternal(Package *package) transObject = &package->transObject; if (!isObjectChangedInCurrentTrans(transObject)) { - createSavepoint(transObject, TOP_PACKAGE); - addToChangesStack(transObject, TOP_PACKAGE); + createSavepoint(transObject, TRANS_PACKAGE); + addToChangesStack(transObject, TRANS_PACKAGE); } GetActualState(package)->is_valid = false; } @@ -1315,8 +1315,8 @@ getPackageByName(text* name, bool create, bool strict) /* Make new history entry of package */ if (!isObjectChangedInCurrentTrans(transObj)) { - createSavepoint(transObj, TOP_PACKAGE); - addToChangesStack(transObj, TOP_PACKAGE); + createSavepoint(transObj, TRANS_PACKAGE); + addToChangesStack(transObj, TRANS_PACKAGE); } GetActualState(package)->is_valid = true; @@ -1335,8 +1335,8 @@ getPackageByName(text* name, bool create, bool strict) if (!isObjectChangedInCurrentTrans(transObj)) { - createSavepoint(transObj, TOP_VARIABLE); - addToChangesStack(transObj, TOP_VARIABLE); + createSavepoint(transObj, TRANS_VARIABLE); + addToChangesStack(transObj, TRANS_VARIABLE); } GetActualState(variable)->is_valid = false; } @@ -1375,7 +1375,7 @@ getPackageByName(text* name, bool create, bool strict) packState->state.is_valid = true; /* Add to changes list */ - addToChangesStack(&package->transObject, TOP_PACKAGE); + addToChangesStack(&package->transObject, TRANS_PACKAGE); return package; } @@ -1487,7 +1487,7 @@ createVariableInternal(Package *package, text *name, Oid typid, if (is_transactional && !isObjectChangedInCurrentTrans(transObject)) { - createSavepoint(transObject, TOP_VARIABLE); + createSavepoint(transObject, TRANS_VARIABLE); } } else @@ -1517,7 +1517,7 @@ createVariableInternal(Package *package, text *name, Oid typid, GetActualState(variable)->is_valid = true; /* If it is necessary, put variable to changedVars */ if (is_transactional) - addToChangesStack(transObject, TOP_VARIABLE); + addToChangesStack(transObject, TRANS_VARIABLE); return variable; } @@ -1589,7 +1589,7 @@ freeValue(VarState *varstate, Oid typid) static void removeState(TransObject *object, TransObjectType type, TransState *stateToDelete) { - if (type == TOP_VARIABLE) + if (type == TRANS_VARIABLE) { Variable *var = (Variable *) object; freeValue((VarState *) stateToDelete, var->typid); @@ -1604,7 +1604,7 @@ removeObject(TransObject *object, TransObjectType type) bool found; HTAB *hash; - if (type == TOP_PACKAGE) + if (type == TRANS_PACKAGE) { Package *package = (Package *) object; @@ -1639,7 +1639,7 @@ createSavepoint(TransObject *transObj, TransObjectType type) *prevState; prevState = GetActualState(transObj); - if (type==TOP_PACKAGE) + if (type==TRANS_PACKAGE) newState = (TransState *) MemoryContextAllocZero(ModuleContext, sizeof(PackState)); else @@ -1663,7 +1663,7 @@ rollbackSavepoint(TransObject *object, TransObjectType type) TransState *state; state = GetActualState(object); - if (type == TOP_PACKAGE) + if (type == TRANS_PACKAGE) { if (!state->is_valid) { @@ -1676,11 +1676,11 @@ rollbackSavepoint(TransObject *object, TransObjectType type) else { /* Remove current state */ - removeState(object, TOP_VARIABLE, state); + removeState(object, TRANS_VARIABLE, state); /* Remove variable if it was created in rolled back transaction */ if (dlist_is_empty(&object->states)) - removeObject(object, TOP_VARIABLE); + removeObject(object, TRANS_VARIABLE); } } @@ -1843,7 +1843,7 @@ addToChangesStack(TransObject *transObj, TransObjectType type) csn = get_actual_changes_list(); co = makeChangedObject(transObj, csn->ctx); - dlist_push_head(type == TOP_PACKAGE ? csn->changedPacksList : + dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : csn->changedVarsList, &co->node); /* Give this object current subxact level */ @@ -1928,7 +1928,7 @@ processChanges(Action action) switch (action) { case ROLLBACK_TO_SAVEPOINT: - rollbackSavepoint(object, i ? TOP_VARIABLE : TOP_PACKAGE); + rollbackSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); break; case RELEASE_SAVEPOINT: /* @@ -1951,7 +1951,7 @@ processChanges(Action action) isObjectChangedInUpperTrans(object)) { /* We just have to drop previous state */ - releaseSavepoint(object, i ? TOP_VARIABLE : TOP_PACKAGE); + releaseSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); } else { diff --git a/pg_variables.h b/pg_variables.h index 23c266f..d9a7117 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -134,8 +134,8 @@ typedef struct ChangedObject /* Type of transactional object instance */ typedef enum TransObjectType { - TOP_PACKAGE, - TOP_VARIABLE + TRANS_PACKAGE, + TRANS_VARIABLE } TransObjectType; /* Element of stack with 'changedVars' and 'changedPacks' list heads*/ From 4194f40ba0d108017db0a87f0124baaae4e27eca Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Thu, 21 Jun 2018 15:05:02 +0300 Subject: [PATCH 042/147] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f87e233..d495fa2 100644 --- a/README.md +++ b/README.md @@ -304,8 +304,8 @@ SELECT pgv_get('pack', 'var_text', NULL::text); before transaction block ``` -If you create a transactional variable after `BEGIN` or `SAVEPOINT` statements and than rollback -to previous state - variable will not be exist: +If you create a transactional variable after `BEGIN` or `SAVEPOINT` statements +and then rollback to previous state - variable will not be exist: ```sql BEGIN; From 57898603151cd8baa799abbaf71d11f0090ff1ab Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 21 Jun 2018 17:21:55 +0300 Subject: [PATCH 043/147] Run pgindent on pg_variables --- pg_variables.c | 263 ++++++++++++++++++++++-------------------- pg_variables.h | 56 +++++---- pg_variables_record.c | 24 ++-- 3 files changed, 178 insertions(+), 165 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 148cbb5..65e4dfd 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -51,11 +51,11 @@ static void getKeyFromName(text *name, char *key); static Package *getPackageByName(text *name, bool create, bool strict); static Variable *getVariableInternal(Package *package, - text *name, Oid typid, - bool strict); + text *name, Oid typid, + bool strict); static Variable *createVariableInternal(Package *package, - text *name, Oid typid, - bool is_transactional); + text *name, Oid typid, + bool is_transactional); static void removePackageInternal(Package *package); /* Functions to work with transactional objects */ @@ -76,8 +76,7 @@ static void pushChangesStack(void); static void removeFromChangedVars(Package *package); /* Constructors */ -static void -makePackHTAB(Package *package, bool is_trans); +static void makePackHTAB(Package *package, bool is_trans); #define CHECK_ARGS_FOR_NULL() \ @@ -140,8 +139,8 @@ static void variable_set(text *package_name, text *var_name, Oid typid, Datum value, bool is_null, bool is_transactional) { - Package *package; - Variable *variable; + Package *package; + Variable *variable; ScalarVar *scalar; package = getPackageByName(package_name, true, false); @@ -171,8 +170,8 @@ static Datum variable_get(text *package_name, text *var_name, Oid typid, bool *is_null, bool strict) { - Package *package; - Variable *variable; + Package *package; + Variable *variable; ScalarVar *scalar; package = getPackageByName(package_name, false, strict); @@ -657,9 +656,9 @@ variable_select_by_value(PG_FUNCTION_ARGS) /* Structure for variable_select_by_values() */ typedef struct { - Variable *variable; + Variable *variable; ArrayIterator iterator; -} VariableIteratorRec; +} VariableIteratorRec; Datum variable_select_by_values(PG_FUNCTION_ARGS) @@ -690,8 +689,8 @@ variable_select_by_values(PG_FUNCTION_ARGS) values = PG_GETARG_ARRAYTYPE_P(2); if (ARR_NDIM(values) > 1) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("searching for elements in multidimensional arrays is not supported"))); + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("searching for elements in multidimensional arrays is not supported"))); /* Get arguments */ package_name = PG_GETARG_TEXT_PP(0); @@ -705,8 +704,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - funcctx->tuple_desc = CreateTupleDescCopy( - GetActualValue(variable).record.tupdesc); + funcctx->tuple_desc = CreateTupleDescCopy(GetActualValue(variable).record.tupdesc); var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -854,7 +852,8 @@ remove_variable(PG_FUNCTION_ARGS) if (!found) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized variable \"%s\"", key))); + errmsg("unrecognized variable \"%s\"", key))); + /* Transactional variable */ transObject = &variable->transObject; if (!isObjectChangedInCurrentTrans(transObject)) @@ -935,8 +934,8 @@ removePackageInternal(Package *package) Datum remove_packages(PG_FUNCTION_ARGS) { - Package *package; - HASH_SEQ_STATUS pstat; + Package *package; + HASH_SEQ_STATUS pstat; /* There is no any packages and variables */ if (packagesHash == NULL) @@ -948,6 +947,7 @@ remove_packages(PG_FUNCTION_ARGS) { removePackageInternal(package); } + /* Remove package and variable from cache */ LastPackage = NULL; LastVariable = NULL; @@ -963,7 +963,7 @@ typedef struct char *package; char *variable; bool is_transactional; -} VariableRec; +} VariableRec; /* * Get list of assigned packages and variables. @@ -997,7 +997,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) */ if (packagesHash) { - Package *package; + Package *package; HASH_SEQ_STATUS pstat; int mRecs = NUMVARIABLES, nRecs = 0; @@ -1008,20 +1008,21 @@ get_packages_and_variables(PG_FUNCTION_ARGS) hash_seq_init(&pstat, packagesHash); while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { - Variable *variable; + Variable *variable; HASH_SEQ_STATUS vstat; - int i; + int i; /* Skip packages marked as deleted */ if (!GetActualState(package)->is_valid) continue; + /* Get variables list for package */ - for (i=0; i < 2; i++) + for (i = 0; i < 2; i++) { hash_seq_init(&vstat, i ? package->varHashTransact : - package->varHashRegular); + package->varHashRegular); while ((variable = - (Variable *) hash_seq_search(&vstat)) != NULL) + (Variable *) hash_seq_search(&vstat)) != NULL) { if (!GetActualState(variable)->is_valid) continue; @@ -1031,7 +1032,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) { mRecs *= 2; recs = (VariableRec *) repalloc(recs, - sizeof(VariableRec) * mRecs); + sizeof(VariableRec) * mRecs); } recs[nRecs].package = GetName(package); @@ -1120,7 +1121,7 @@ get_packages_stats(PG_FUNCTION_ARGS) FuncCallContext *funcctx; MemoryContext oldcontext; HASH_SEQ_STATUS *pstat; - Package *package; + Package *package; if (SRF_IS_FIRSTCALL()) { @@ -1211,8 +1212,8 @@ getKeyFromName(text *name, char *key) if (key_len >= NAMEDATALEN - 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("name \"%.*s\" is too long", - key_len, VARDATA_ANY(name)))); + errmsg("name \"%.*s\" is too long", + key_len, VARDATA_ANY(name)))); strncpy(key, VARDATA_ANY(name), key_len); key[key_len] = '\0'; @@ -1221,7 +1222,7 @@ getKeyFromName(text *name, char *key) static void ensurePackagesHashExists(void) { - HASHCTL ctl; + HASHCTL ctl; if (packagesHash) return; @@ -1251,29 +1252,31 @@ makePackHTAB(Package *package, bool is_trans) if (is_trans) package->hctxTransact = AllocSetContextCreate(ModuleContext, - PGV_MCXT_VARS, - ALLOCSET_DEFAULT_SIZES); + PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); else package->hctxRegular = AllocSetContextCreate(ModuleContext, - PGV_MCXT_VARS, - ALLOCSET_DEFAULT_SIZES); + PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); + snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", - is_trans ? "Transactional" : "Regular", key); + is_trans ? "Transactional" : "Regular", key); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(Variable); ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); + if (is_trans) package->varHashTransact = hash_create(hash_name, - NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); + NUMVARIABLES, &ctl, + HASH_ELEM | HASH_CONTEXT); else package->varHashRegular = hash_create(hash_name, - NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); + NUMVARIABLES, &ctl, + HASH_ELEM | HASH_CONTEXT); } static Package * -getPackageByName(text* name, bool create, bool strict) +getPackageByName(text *name, bool create, bool strict) { Package *package; PackState *packState; @@ -1308,7 +1311,7 @@ getPackageByName(text* name, bool create, bool strict) return package; else if (create) { - HASH_SEQ_STATUS vstat; + HASH_SEQ_STATUS vstat; Variable *variable; TransObject *transObj = &package->transObject; @@ -1329,7 +1332,7 @@ getPackageByName(text* name, bool create, bool strict) /* Mark all transactional variables in package as removed */ hash_seq_init(&vstat, package->varHashTransact); while ((variable = - (Variable *) hash_seq_search(&vstat)) != NULL) + (Variable *) hash_seq_search(&vstat)) != NULL) { transObj = &variable->transObject; @@ -1346,7 +1349,7 @@ getPackageByName(text* name, bool create, bool strict) else if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + errmsg("unrecognized package \"%s\"", key))); else return NULL; } @@ -1355,7 +1358,7 @@ getPackageByName(text* name, bool create, bool strict) if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + errmsg("unrecognized package \"%s\"", key))); else return package; } @@ -1389,7 +1392,7 @@ getPackageByName(text* name, bool create, bool strict) static Variable * getVariableInternal(Package *package, text *name, Oid typid, bool strict) { - Variable *variable; + Variable *variable; char key[NAMEDATALEN]; bool found; @@ -1406,8 +1409,8 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) { if (variable->typid != typid) { - char *var_type = DatumGetCString(DirectFunctionCall1( - regtypeout, ObjectIdGetDatum(variable->typid))); + char *var_type = DatumGetCString(DirectFunctionCall1(regtypeout, + ObjectIdGetDatum(variable->typid))); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1437,7 +1440,7 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) */ static Variable * createVariableInternal(Package *package, text *name, Oid typid, - bool is_transactional) + bool is_transactional) { Variable *variable; TransObject *transObject; @@ -1447,17 +1450,17 @@ createVariableInternal(Package *package, text *name, Oid typid, getKeyFromName(name, key); /* - * Reverse check: for non-transactional variable search in regular table and - * vice versa. + * Reverse check: for non-transactional variable search in regular table + * and vice versa. */ hash_search(is_transactional ? package->varHashRegular : package->varHashTransact, key, HASH_FIND, &found); if (found) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("variable \"%s\" already created as %sTRANSACTIONAL", - key, is_transactional ? "NOT " : ""))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + key, is_transactional ? "NOT " : ""))); variable = (Variable *) hash_search(pack_htab(package, is_transactional), key, HASH_ENTER, &found); @@ -1469,8 +1472,8 @@ createVariableInternal(Package *package, text *name, Oid typid, { if (variable->typid != typid) { - char *var_type = DatumGetCString(DirectFunctionCall1( - regtypeout, ObjectIdGetDatum(variable->typid))); + char *var_type = DatumGetCString(DirectFunctionCall1(regtypeout, + ObjectIdGetDatum(variable->typid))); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1480,9 +1483,9 @@ createVariableInternal(Package *package, text *name, Oid typid, /* * Savepoint must be created when variable changed in current - * transaction. - * For each transaction level there should be a corresponding savepoint. - * New value should be stored in a last state. + * transaction. For each transaction level there should be a + * corresponding savepoint. New value should be stored in a last + * state. */ if (is_transactional && !isObjectChangedInCurrentTrans(transObject)) @@ -1492,7 +1495,7 @@ createVariableInternal(Package *package, text *name, Oid typid, } else { - VarState *varState; + VarState *varState; /* Variable entry was created, so initialize new variable. */ variable->typid = typid; @@ -1525,45 +1528,46 @@ createVariableInternal(Package *package, text *name, Oid typid, static void copyValue(VarState *src, VarState *dest, Variable *destVar) { - MemoryContext oldcxt, - destctx; + MemoryContext oldcxt, + destctx; destctx = destVar->package->hctxTransact; oldcxt = MemoryContextSwitchTo(destctx); if (destVar->typid == RECORDOID) - /* copy record value */ + /* copy record value */ { - bool found; - HASH_SEQ_STATUS *rstat; - HashRecordEntry *item_prev, - *item_new; - RecordVar *record_src = &src->value.record; - RecordVar *record_dest = &dest->value.record; + bool found; + HASH_SEQ_STATUS *rstat; + HashRecordEntry *item_prev, + *item_new; + RecordVar *record_src = &src->value.record; + RecordVar *record_dest = &dest->value.record; init_record(record_dest, record_src->tupdesc, destVar); - /* Copy previous history entry into the new one*/ + /* Copy previous history entry into the new one */ rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); hash_seq_init(rstat, record_src->rhash); - while((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) !=NULL) + while ((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) != NULL) { HashRecordKey k; k = item_prev->key; item_new = (HashRecordEntry *) hash_search(record_dest->rhash, &k, - HASH_ENTER, &found); + HASH_ENTER, &found); item_new->tuple = heap_copytuple(item_prev->tuple); } } else - /* copy scalar value */ + /* copy scalar value */ { - ScalarVar *scalar = &dest->value.scalar; + ScalarVar *scalar = &dest->value.scalar; + *scalar = src->value.scalar; if (!scalar->is_null) scalar->value = datumCopy(src->value.scalar.value, - scalar->typbyval, scalar->typlen); + scalar->typbyval, scalar->typlen); else scalar->value = 0; } @@ -1591,7 +1595,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete { if (type == TRANS_VARIABLE) { - Variable *var = (Variable *) object; + Variable *var = (Variable *) object; + freeValue((VarState *) stateToDelete, var->typid); } dlist_delete(&stateToDelete->node); @@ -1601,12 +1606,12 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete static void removeObject(TransObject *object, TransObjectType type) { - bool found; - HTAB *hash; + bool found; + HTAB *hash; if (type == TRANS_PACKAGE) { - Package *package = (Package *) object; + Package *package = (Package *) object; /* * Delete a variable from the change history of the overlying @@ -1622,7 +1627,7 @@ removeObject(TransObject *object, TransObjectType type) hash = ((Variable *) object)->package->varHashTransact; /* Remove all object's states */ - while(!dlist_is_empty(&object->states)) + while (!dlist_is_empty(&object->states)) removeState(object, type, GetActualState(object)); /* Remove object from hash table */ @@ -1635,19 +1640,19 @@ removeObject(TransObject *object, TransObjectType type) static void createSavepoint(TransObject *transObj, TransObjectType type) { - TransState *newState, - *prevState; + TransState *newState, + *prevState; prevState = GetActualState(transObj); - if (type==TRANS_PACKAGE) + if (type == TRANS_PACKAGE) newState = (TransState *) MemoryContextAllocZero(ModuleContext, - sizeof(PackState)); + sizeof(PackState)); else { - Variable *var = (Variable *) transObj; + Variable *var = (Variable *) transObj; newState = (TransState *) MemoryContextAllocZero(var->package->hctxTransact, - sizeof(VarState)); + sizeof(VarState)); copyValue((VarState *) prevState, (VarState *) newState, var); } dlist_push_head(&transObj->states, &newState->node); @@ -1706,7 +1711,11 @@ releaseSavepoint(TransObject *object, TransObjectType type) stateToDelete = dlist_container(TransState, node, nodeToDelete); removeState(object, type, stateToDelete); } - /* Object has no more previous states and can be completely removed if necessary*/ + + /* + * Object has no more previous states and can be completely removed if + * necessary + */ if (!GetActualState(object)->is_valid && !dlist_has_next(states, dlist_head_node(states))) { @@ -1715,7 +1724,8 @@ releaseSavepoint(TransObject *object, TransObjectType type) /* Change subxact level due to release */ else { - TransState *state; + TransState *state; + state = GetActualState(object); state->level--; } @@ -1727,7 +1737,7 @@ releaseSavepoint(TransObject *object, TransObjectType type) static bool isObjectChangedInCurrentTrans(TransObject *transObj) { - TransState *state; + TransState *state; if (!changesStack) return false; @@ -1742,8 +1752,8 @@ isObjectChangedInCurrentTrans(TransObject *transObj) static bool isObjectChangedInUpperTrans(TransObject *object) { - TransState *cur_state, - *prev_state; + TransState *cur_state, + *prev_state; cur_state = GetActualState(object); if (dlist_has_next(&object->states, &cur_state->node)) @@ -1761,17 +1771,17 @@ isObjectChangedInUpperTrans(TransObject *object) static void pushChangesStack(void) { - MemoryContext oldcxt; - ChangesStackNode *csn; + MemoryContext oldcxt; + ChangesStackNode *csn; /* - * Initialize changesStack and create MemoryContext for it - * if not done before. + * Initialize changesStack and create MemoryContext for it if not done + * before. */ if (!changesStackContext) changesStackContext = AllocSetContextCreate(ModuleContext, - PGV_MCXT_STACK, - ALLOCSET_START_SMALL_SIZES); + PGV_MCXT_STACK, + ALLOCSET_START_SMALL_SIZES); Assert(changesStackContext); oldcxt = MemoryContextSwitchTo(changesStackContext); @@ -1804,7 +1814,7 @@ prepareChangesStack(void) { if (!changesStack) { - int level = GetCurrentTransactionNestLevel(); + int level = GetCurrentTransactionNestLevel(); while (level-- > 0) { @@ -1844,7 +1854,7 @@ addToChangesStack(TransObject *transObj, TransObjectType type) csn = get_actual_changes_list(); co = makeChangedObject(transObj, csn->ctx); dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : - csn->changedVarsList, &co->node); + csn->changedVarsList, &co->node); /* Give this object current subxact level */ GetActualState(transObj)->level = GetCurrentTransactionNestLevel(); @@ -1857,18 +1867,19 @@ addToChangesStack(TransObject *transObj, TransObjectType type) static void removeFromChangedVars(Package *package) { - dlist_mutable_iter var_miter, - pack_miter; - dlist_head *changedVarsList, - *changedPacksList; + dlist_mutable_iter var_miter, + pack_miter; + dlist_head *changedVarsList, + *changedPacksList; /* First remove corresponding variables from changedVarsList */ changedVarsList = get_actual_changes_list()->changedVarsList; dlist_foreach_modify(var_miter, changedVarsList) { ChangedObject *co_cur = dlist_container(ChangedObject, node, - var_miter.cur); - Variable *var = (Variable *) co_cur->object; + var_miter.cur); + Variable *var = (Variable *) co_cur->object; + if (var->package == package) dlist_delete(&co_cur->node); } @@ -1877,8 +1888,9 @@ removeFromChangedVars(Package *package) dlist_foreach_modify(pack_miter, changedPacksList) { ChangedObject *co_cur = dlist_container(ChangedObject, node, - pack_miter.cur); - Package *pack = (Package *) co_cur->object; + pack_miter.cur); + Package *pack = (Package *) co_cur->object; + if (pack == package) { dlist_delete(&co_cur->node); @@ -1895,7 +1907,7 @@ typedef enum Action { RELEASE_SAVEPOINT, ROLLBACK_TO_SAVEPOINT -} Action; +} Action; /* * Iterate variables and packages from list of changes and @@ -1905,22 +1917,24 @@ static void processChanges(Action action) { ChangesStackNode *bottom_list; - int i; + int i; Assert(changesStack && changesStackContext); /* List removed from stack but we still can use it */ bottom_list = dlist_container(ChangesStackNode, node, - dlist_pop_head_node(changesStack)); + dlist_pop_head_node(changesStack)); - /* i: + /* + * i: * 1 - manage variables * 0 - manage packages */ for (i = 1; i > -1; i--) { - dlist_iter iter; + dlist_iter iter; + dlist_foreach(iter, i ? bottom_list->changedVarsList : - bottom_list->changedPacksList) + bottom_list->changedPacksList) { ChangedObject *co = dlist_container(ChangedObject, node, iter.cur); TransObject *object = co->object; @@ -1931,16 +1945,17 @@ processChanges(Action action) rollbackSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); break; case RELEASE_SAVEPOINT: + /* - * If package was removed in current transaction level - * mark var as removed. - * We do not check pack_state->level, because var cannot get in - * list of changes until pack is removed. - */ + * If package was removed in current transaction level + * mark var as removed. We do not check pack_state->level, + * because var cannot get in list of changes until pack is + * removed. + */ if (i) { - Variable *variable = (Variable *) object; - Package *package = variable->package; + Variable *variable = (Variable *) object; + Package *package = variable->package; if (!GetActualState(package)->is_valid) GetActualState(variable)->is_valid = false; @@ -1960,13 +1975,13 @@ processChanges(Action action) ChangesStackNode *csn; /* - * Impossible to push in upper list existing node because - * it was created in another context - */ + * Impossible to push in upper list existing node + * because it was created in another context + */ csn = dlist_head_element(ChangesStackNode, node, changesStack); co_new = makeChangedObject(object, csn->ctx); dlist_push_head(i ? csn->changedVarsList : - csn->changedPacksList, &co_new->node); + csn->changedPacksList, &co_new->node); /* Change subxact level due to release */ GetActualState(object)->level--; diff --git a/pg_variables.h b/pg_variables.h index d9a7117..cc01445 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -42,7 +42,7 @@ typedef struct RecordVar FmgrInfo hash_proc; /* Match function info */ FmgrInfo cmp_proc; -} RecordVar; +} RecordVar; typedef struct ScalarVar { @@ -50,7 +50,7 @@ typedef struct ScalarVar bool is_null; bool typbyval; int16 typlen; -} ScalarVar; +} ScalarVar; /* State of TransObject instance */ typedef struct TransState @@ -64,7 +64,7 @@ typedef struct TransState typedef struct PackState { TransState state; -} PackState; +} PackState; /* List node that stores one of the variable's states */ typedef struct VarState @@ -72,17 +72,17 @@ typedef struct VarState TransState state; union { - ScalarVar scalar; - RecordVar record; - } value; -} VarState; + ScalarVar scalar; + RecordVar record; + } value; +} VarState; /* Transactional object */ typedef struct TransObject { char name[NAMEDATALEN]; dlist_head states; -} TransObject; +} TransObject; /* Transactional package */ typedef struct Package @@ -92,21 +92,22 @@ typedef struct Package *varHashTransact; /* Memory context for package variables for easy memory release */ MemoryContext hctxRegular, - hctxTransact; -} Package; + hctxTransact; +} Package; /* Transactional variable */ typedef struct Variable { - TransObject transObject; - Package *package; + TransObject transObject; + Package *package; Oid typid; + /* - * The flag determines the further behavior of the variable. - * Can be specified only when creating a variable. + * The flag determines the further behavior of the variable. Can be + * specified only when creating a variable. */ bool is_transactional; -} Variable; +} Variable; typedef struct HashRecordKey { @@ -116,27 +117,27 @@ typedef struct HashRecordKey FmgrInfo *hash_proc; /* Match function info */ FmgrInfo *cmp_proc; -} HashRecordKey; +} HashRecordKey; typedef struct HashRecordEntry { HashRecordKey key; HeapTuple tuple; -} HashRecordEntry; +} HashRecordEntry; /* Element of list with objects created, changed or removed within transaction */ typedef struct ChangedObject { - dlist_node node; - TransObject *object; -} ChangedObject; + dlist_node node; + TransObject *object; +} ChangedObject; /* Type of transactional object instance */ typedef enum TransObjectType { TRANS_PACKAGE, TRANS_VARIABLE -} TransObjectType; +} TransObjectType; /* Element of stack with 'changedVars' and 'changedPacks' list heads*/ typedef struct ChangesStackNode @@ -145,18 +146,15 @@ typedef struct ChangesStackNode dlist_head *changedVarsList; dlist_head *changedPacksList; MemoryContext ctx; -} ChangesStackNode; +} ChangesStackNode; extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable); extern void check_attributes(Variable *variable, TupleDesc tupdesc); extern void check_record_key(Variable *variable, Oid typid); -extern void insert_record(Variable* variable, - HeapTupleHeader tupleHeader); -extern bool update_record(Variable *variable, - HeapTupleHeader tupleHeader); -extern bool delete_record(Variable* variable, Datum value, - bool is_null); +extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader); +extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); +extern bool delete_record(Variable *variable, Datum value, bool is_null); #define GetActualState(object) \ (dlist_head_element(TransState, node, &((TransObject *) object)->states)) @@ -172,4 +170,4 @@ extern bool delete_record(Variable* variable, Datum value, (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ &(object->transObject.states)) -#endif /* __PG_VARIABLES_H__ */ +#endif /* __PG_VARIABLES_H__ */ diff --git a/pg_variables_record.c b/pg_variables_record.c index 6a4d399..bdad386 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -66,20 +66,20 @@ record_match(const void *key1, const void *key2, Size keysize) void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - MemoryContext oldcxt, - topctx; + HASHCTL ctl; + char hash_name[BUFSIZ]; + MemoryContext oldcxt, + topctx; TypeCacheEntry *typentry; - Oid keyid; + Oid keyid; Assert(variable->typid == RECORDOID); sprintf(hash_name, "Records hash for variable \"%s\"", GetName(variable)); - topctx = variable->is_transactional ? - variable->package->hctxTransact : - variable->package->hctxRegular; + topctx = variable->is_transactional ? + variable->package->hctxTransact : + variable->package->hctxRegular; #if PG_VERSION_NUM >= 110000 record->hctx = AllocSetContextCreateExtended(topctx, @@ -162,9 +162,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc) || (attr1->attndims != attr2->attndims) || (attr1->atttypmod != attr2->atttypmod)) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("new record structure differs from variable \"%s\" " - "structure", GetName(variable)))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("new record structure differs from variable \"%s\" " + "structure", GetName(variable)))); } } @@ -250,7 +250,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) * Insert a record. New record key should be unique in the variable. */ bool -update_record(Variable* variable, HeapTupleHeader tupleHeader) +update_record(Variable *variable, HeapTupleHeader tupleHeader) { TupleDesc tupdesc; HeapTuple tuple; From 238c1ef814776b59cbf87fdc2c9dc8595c8f68ce Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 29 Jun 2018 18:19:04 +0300 Subject: [PATCH 044/147] Remove unnecessary row notes from examples --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index d495fa2..946af69 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ SELECT pgv_get('pack', 'var_text', NULL::text); before transaction block ``` -If you create a transactional variable after `BEGIN` or `SAVEPOINT` statements +If you create a transactional variable after `BEGIN` or `SAVEPOINT` statements and then rollback to previous state - variable will not be exist: ```sql @@ -342,15 +342,12 @@ SELECT * FROM pgv_stats(); package | allocated_memory ---------+------------------ pack | 24576 -(1 row) ROLLBACK; SELECT * FROM pgv_list(); package | name | is_transactional ---------+-----------+------------------ pack | var_trans | t -(1 row) - ``` If you created transactional variable once, you should use flag `is_transactional` From c037953c4ba7e296485c368fb4155d3b2da7ca27 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 3 Aug 2018 14:19:29 +0300 Subject: [PATCH 045/147] Fix HTAB name setting in makePackHTAB() --- pg_variables.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 65e4dfd..32b004f 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1247,8 +1247,7 @@ static void makePackHTAB(Package *package, bool is_trans) { HASHCTL ctl; - char key[NAMEDATALEN], - hash_name[BUFSIZ]; + char hash_name[BUFSIZ]; if (is_trans) package->hctxTransact = AllocSetContextCreate(ModuleContext, @@ -1260,7 +1259,7 @@ makePackHTAB(Package *package, bool is_trans) ALLOCSET_DEFAULT_SIZES); snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", - is_trans ? "Transactional" : "Regular", key); + is_trans ? "Transactional" : "Regular", package->transObject.name); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(Variable); ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); From f731e68eff6760d5247817f168a4131250f7dafb Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sun, 5 Aug 2018 23:00:11 +0300 Subject: [PATCH 046/147] Fix package state rollback --- pg_variables.c | 96 ++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 32b004f..9c5bcaa 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -77,7 +77,8 @@ static void removeFromChangedVars(Package *package); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); - +static inline ChangedObject * +makeChangedObject(TransObject *object, MemoryContext ctx); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -1676,6 +1677,9 @@ rollbackSavepoint(TransObject *object, TransObjectType type) /* Restore regular vars HTAB */ makePackHTAB((Package *) object, false); } + else + /* Pass current state to parent level */ + releaseSavepoint(object, TRANS_PACKAGE); } else { @@ -1695,39 +1699,57 @@ static void releaseSavepoint(TransObject *object, TransObjectType type) { dlist_head *states; - Assert(GetActualState(object)->level == GetCurrentTransactionNestLevel()); - states = &object->states; - /* Object existed in parent transaction */ - if (dlist_has_next(states, dlist_head_node(states))) + /* Mark object as changed in parent transaction... */ + if (!dlist_is_empty(changesStack) /* ...if there is an upper level... */ + /* ...and object is not yet in list of that level changes. */ + && !isObjectChangedInUpperTrans(object)) { - TransState *stateToDelete; - dlist_node *nodeToDelete; + ChangedObject *co_new; + ChangesStackNode *csn; - /* Remove previous state */ - nodeToDelete = dlist_next_node(states, dlist_head_node(states)); - stateToDelete = dlist_container(TransState, node, nodeToDelete); - removeState(object, type, stateToDelete); - } + /* + * Impossible to push in upper list existing node + * because it was created in another context + */ + csn = dlist_head_element(ChangesStackNode, node, changesStack); + co_new = makeChangedObject(object, csn->ctx); + dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : + csn->changedVarsList, + &co_new->node); - /* - * Object has no more previous states and can be completely removed if - * necessary - */ - if (!GetActualState(object)->is_valid && - !dlist_has_next(states, dlist_head_node(states))) - { - removeObject(object, type); } - /* Change subxact level due to release */ else { - TransState *state; + states = &object->states; - state = GetActualState(object); - state->level--; + /* If object existed in parent transaction... */ + if (dlist_has_next(states, dlist_head_node(states))) + { + TransState *stateToDelete; + dlist_node *nodeToDelete; + + /* ...remove its previous state */ + nodeToDelete = dlist_next_node(states, dlist_head_node(states)); + stateToDelete = dlist_container(TransState, node, nodeToDelete); + removeState(object, type, stateToDelete); + } + + /* + * Object has no more previous states and can be completely removed if + * necessary + */ + if (!GetActualState(object)->is_valid && + !dlist_has_next(states, dlist_head_node(states))) + { + removeObject(object, type); + return; + } } + + /* Change subxact level due to release */ + GetActualState(object)->level--; } /* @@ -1960,31 +1982,7 @@ processChanges(Action action) GetActualState(variable)->is_valid = false; } - /* Did this object change at parent level? */ - if (dlist_is_empty(changesStack) || - isObjectChangedInUpperTrans(object)) - { - /* We just have to drop previous state */ - releaseSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); - } - else - { - /* Mark object as changed at parent level */ - ChangedObject *co_new; - ChangesStackNode *csn; - - /* - * Impossible to push in upper list existing node - * because it was created in another context - */ - csn = dlist_head_element(ChangesStackNode, node, changesStack); - co_new = makeChangedObject(object, csn->ctx); - dlist_push_head(i ? csn->changedVarsList : - csn->changedPacksList, &co_new->node); - - /* Change subxact level due to release */ - GetActualState(object)->level--; - } + releaseSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); break; } } From 054e746a43b01b720e84b82a214809669bf671ea Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sun, 5 Aug 2018 23:00:31 +0300 Subject: [PATCH 047/147] Fix tests --- expected/pg_variables_trans.out | 12 ++++++------ sql/pg_variables_trans.sql | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 910f505..6403020 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1758,11 +1758,11 @@ SELECT pgv_free(); (1 row) -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; package --------- - vars2 vars + vars2 (2 rows) SELECT * FROM pgv_list() ORDER BY package, name; @@ -1771,11 +1771,11 @@ SELECT * FROM pgv_list() ORDER BY package, name; (0 rows) RELEASE sp5; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; package --------- - vars2 vars + vars2 (2 rows) SELECT * FROM pgv_list() ORDER BY package, name; @@ -1784,7 +1784,7 @@ SELECT * FROM pgv_list() ORDER BY package, name; (0 rows) RELEASE sp4; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; package --------- vars @@ -1796,7 +1796,7 @@ SELECT * FROM pgv_list() ORDER BY package, name; (0 rows) COMMIT; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; package --------- (0 rows) diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index d59843a..83e03aa 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -441,16 +441,16 @@ SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); SAVEPOINT sp4; SAVEPOINT sp5; SELECT pgv_free(); -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; SELECT * FROM pgv_list() ORDER BY package, name; RELEASE sp5; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; SELECT * FROM pgv_list() ORDER BY package, name; RELEASE sp4; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; SELECT * FROM pgv_list() ORDER BY package, name; COMMIT; -SELECT package FROM pgv_stats(); +SELECT package FROM pgv_stats() ORDER BY package; BEGIN; SELECT pgv_set('vars', 'trans1', 'package created'::text, true); From 8ac0090a6746372d12e15555dbaab4060f473c72 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Mon, 6 Aug 2018 13:38:56 +0300 Subject: [PATCH 048/147] Use GetName() --- pg_variables.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 9c5bcaa..bffe30a 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -77,8 +77,8 @@ static void removeFromChangedVars(Package *package); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); -static inline ChangedObject * -makeChangedObject(TransObject *object, MemoryContext ctx); +static inline ChangedObject *makeChangedObject(TransObject *object, + MemoryContext ctx); #define CHECK_ARGS_FOR_NULL() \ do { \ @@ -1260,7 +1260,7 @@ makePackHTAB(Package *package, bool is_trans) ALLOCSET_DEFAULT_SIZES); snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", - is_trans ? "Transactional" : "Regular", package->transObject.name); + is_trans ? "Transactional" : "Regular", GetName(package)); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(Variable); ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); From a5f6a7e3922ab2db0df7d3e46168a4102656cfcb Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 2 Nov 2018 15:26:38 +0300 Subject: [PATCH 049/147] Add LICENSE --- LICENSE | 11 +++++++++++ README.md | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f340ae8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +pg_variables is released under the PostgreSQL License, a liberal Open Source license, similar to the BSD or MIT licenses. + +Copyright (c) 2016-2018, Postgres Professional +Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL POSTGRES PROFESSIONAL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF POSTGRES PROFESSIONAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +POSTGRES PROFESSIONAL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND POSTGRES PROFESSIONAL HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/README.md b/README.md index 946af69..cbf8404 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ SELECT pgv_get('vars', 'trans_int', NULL::int); ## License -This module available under the same license as +This module available under the [license](LICENSE) similar to [PostgreSQL](http://www.postgresql.org/about/licence/). ## Installation From 43dbd76a47b1857ad5ef711106757e30fd7aea93 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 2 Nov 2018 17:34:58 +0300 Subject: [PATCH 050/147] PGPRO-2118: Check existance of hash and cmp functions before initializing a variable --- pg_variables_record.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pg_variables_record.c b/pg_variables_record.c index bdad386..3d5c7b2 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -75,6 +75,26 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) Assert(variable->typid == RECORDOID); + /* First get hash and match functions for key type. */ + keyid = GetTupleDescAttr(tupdesc, 0)->atttypid; + typentry = lookup_type_cache(keyid, + TYPECACHE_HASH_PROC_FINFO | + TYPECACHE_CMP_PROC_FINFO); + + if (!OidIsValid(typentry->hash_proc_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a hash function for type %s", + format_type_be(keyid)))); + + if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a matching function for type %s", + format_type_be(keyid)))); + + /* Initialize the record */ + sprintf(hash_name, "Records hash for variable \"%s\"", GetName(variable)); topctx = variable->is_transactional ? @@ -109,24 +129,6 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE); - /* Get hash and match functions for key type. */ - keyid = GetTupleDescAttr(record->tupdesc, 0)->atttypid; - typentry = lookup_type_cache(keyid, - TYPECACHE_HASH_PROC_FINFO | - TYPECACHE_CMP_PROC_FINFO); - - if (!OidIsValid(typentry->hash_proc_finfo.fn_oid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("could not identify a hash function for type %s", - format_type_be(keyid)))); - - if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("could not identify a matching function for type %s", - format_type_be(keyid)))); - fmgr_info(typentry->hash_proc_finfo.fn_oid, &record->hash_proc); fmgr_info(typentry->cmp_proc_finfo.fn_oid, &record->cmp_proc); From 8a6cbcf021c492fcfb3835ec2f0836a5748c8e9c Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Mon, 24 Dec 2018 12:24:25 +0300 Subject: [PATCH 051/147] Improve support of master --- pg_variables_record.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pg_variables_record.c b/pg_variables_record.c index 3d5c7b2..2749b23 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -101,7 +101,13 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) variable->package->hctxTransact : variable->package->hctxRegular; -#if PG_VERSION_NUM >= 110000 +#if PG_VERSION_NUM >= 120000 + record->hctx = AllocSetContextCreateInternal(topctx, + hash_name, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); +#elif PG_VERSION_NUM >= 110000 record->hctx = AllocSetContextCreateExtended(topctx, hash_name, ALLOCSET_DEFAULT_MINSIZE, From 3074d0797831ce6a9190f09ed216d10178e6552e Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 22 Dec 2018 13:08:02 +0300 Subject: [PATCH 052/147] Fix using cached variables --- pg_variables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index bffe30a..0936421 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1632,6 +1632,9 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); + + LastPackage = NULL; + LastVariable = NULL; } /* From 8ba32c2db12f3de46eda14e1d986bda8d16c7211 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 24 Dec 2018 18:14:07 +0300 Subject: [PATCH 053/147] Add cache test --- expected/pg_variables_trans.out | 16 ++++++++++++++++ sql/pg_variables_trans.sql | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 6403020..95ba17e 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1838,3 +1838,19 @@ SELECT pgv_remove('vars'); (1 row) +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +ERROR: division by zero +SELECT pgv_insert('package', 'errs',row(1), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 83e03aa..d40cdaf 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -460,3 +460,10 @@ SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); SELECT * FROM pgv_list() ORDER BY package, name; COMMIT; SELECT pgv_remove('vars'); + +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +SELECT pgv_insert('package', 'errs',row(1), true); + +SELECT pgv_free(); From 49c8a931c2453f2a96e34e9e7613765b466a00eb Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 24 Dec 2018 17:41:13 +0300 Subject: [PATCH 054/147] Refactoring of makePackHTAB() --- pg_variables.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 0936421..243cc1f 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1247,32 +1247,25 @@ ensurePackagesHashExists(void) static void makePackHTAB(Package *package, bool is_trans) { - HASHCTL ctl; - char hash_name[BUFSIZ]; + HASHCTL ctl; + char hash_name[BUFSIZ]; + HTAB **htab; + MemoryContext *context; - if (is_trans) - package->hctxTransact = AllocSetContextCreate(ModuleContext, - PGV_MCXT_VARS, - ALLOCSET_DEFAULT_SIZES); - else - package->hctxRegular = AllocSetContextCreate(ModuleContext, - PGV_MCXT_VARS, - ALLOCSET_DEFAULT_SIZES); + htab = is_trans ? &package->varHashTransact : &package->varHashRegular; + context = is_trans ? &package->hctxTransact : &package->hctxRegular; + + *context = AllocSetContextCreate(ModuleContext, PGV_MCXT_VARS, + ALLOCSET_DEFAULT_SIZES); snprintf(hash_name, BUFSIZ, "%s variables hash for package \"%s\"", is_trans ? "Transactional" : "Regular", GetName(package)); ctl.keysize = NAMEDATALEN; ctl.entrysize = sizeof(Variable); - ctl.hcxt = (is_trans ? package->hctxTransact : package->hctxRegular); + ctl.hcxt = *context; - if (is_trans) - package->varHashTransact = hash_create(hash_name, - NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); - else - package->varHashRegular = hash_create(hash_name, - NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); + *htab = hash_create(hash_name, NUMVARIABLES, &ctl, + HASH_ELEM | HASH_CONTEXT); } static Package * From 9924cd26aa2557f55c67347c80ab5f4b109b56a4 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 21 Jan 2019 15:11:56 +0300 Subject: [PATCH 055/147] Reset the Package->hctxRegular if package in state "removed, but not committed yet". --- expected/pg_variables_trans.out | 6 ++++++ pg_variables.c | 6 +++++- sql/pg_variables_trans.sql | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 95ba17e..87741f9 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1705,6 +1705,12 @@ SELECT pgv_free(); (1 row) +SELECT pgv_free(); -- Check sequential package removal in one subtransaction + pgv_free +---------- + +(1 row) + SELECT * FROM pgv_list() ORDER BY package, name; package | name | is_transactional ---------+------+------------------ diff --git a/pg_variables.c b/pg_variables.c index 243cc1f..0e56bb1 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -916,7 +916,11 @@ removePackageInternal(Package *package) TransObject *transObject; /* All regular variables will be freed */ - MemoryContextDelete(package->hctxRegular); + if (package->hctxRegular) + { + MemoryContextDelete(package->hctxRegular); + package->hctxRegular = NULL; + } /* Add to changes list */ transObject = &package->transObject; diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index d40cdaf..069b7cd 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -425,6 +425,7 @@ SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); BEGIN; SELECT pgv_free(); +SELECT pgv_free(); -- Check sequential package removal in one subtransaction SELECT * FROM pgv_list() ORDER BY package, name; SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); SELECT * FROM pgv_list() ORDER BY package, name; From cadb2273c9e2c6fa301d1e9e94e6ea272f62529f Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Mon, 21 Jan 2019 15:47:17 +0300 Subject: [PATCH 056/147] Add PostgreSQL 11 into Travis tests --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6d40aa6..7edda6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,12 +18,13 @@ notifications: on_failure: always env: - - PG_VERSION=10 LEVEL=nightmare - - PG_VERSION=10 LEVEL=hardcore + - PG_VERSION=11 LEVEL=nightmare + - PG_VERSION=11 LEVEL=hardcore + - PG_VERSION=11 - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 matrix: allow_failures: - - env: PG_VERSION=10 LEVEL=nightmare + - env: PG_VERSION=11 LEVEL=nightmare From 497913b4a8b364b153df0f2101620565516dbef3 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 23 Jan 2019 19:32:56 +0300 Subject: [PATCH 057/147] Version 1.2 - Bug fixes - Add support for array types --- .gitignore | 1 + Makefile | 4 ++-- README.md | 19 +++++++++++++++++-- pg_variables--1.1--1.2.sql | 16 ++++++++++++++++ pg_variables.c | 2 ++ pg_variables.control | 2 +- sql/pg_variables_any.sql | 20 ++++++++++++++++++++ 7 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 pg_variables--1.1--1.2.sql diff --git a/.gitignore b/.gitignore index 09586c9..ee52e69 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ lib*.pc /tmp_install/ Dockerfile pg_variables--1.1.sql +pg_variables--1.2.sql diff --git a/Makefile b/Makefile index 2f47be0..7e262ff 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ MODULE_big = pg_variables OBJS = pg_variables.o pg_variables_record.o $(WIN32RES) EXTENSION = pg_variables -EXTVERSION = 1.1 -DATA = pg_variables--1.0.sql pg_variables--1.0--1.1.sql +EXTVERSION = 1.2 +DATA = pg_variables--1.0.sql pg_variables--1.0--1.1.sql pg_variables--1.1--1.2.sql DATA_built = $(EXTENSION)--$(EXTVERSION).sql PGFILEDESC = "pg_variables - sessional variables" diff --git a/README.md b/README.md index cbf8404..87867e8 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,13 @@ Function | Returns `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` +## Array variables functions + +Function | Returns +-------- | ------- +`pgv_set(package text, name text, value anyarray, is_transactional bool default false)` | `void` +`pgv_get(package text, name text, var_type anyarray, strict bool default true)` | `anyarray` + ## **Deprecated** scalar variables functions ### Integer variables @@ -181,7 +188,7 @@ Note that **pgv_stats()** works only with the PostgreSQL 9.6 and newer. ## Examples -It is easy to use functions to work with scalar variables: +It is easy to use functions to work with scalar and array variables: ```sql SELECT pgv_set('vars', 'int1', 101); @@ -196,6 +203,13 @@ SELECT SELECT pgv_get('vars', 'text1', NULL::text); pgv_get --------------- text variable + +SELECT pgv_set('vars', 'arr1', '{101,102}'::int[]); + +SELECT pgv_get('vars', 'arr1', NULL::int[]); + pgv_get +----------- + {101,102} ``` Let's assume we have a **tab** table: @@ -246,6 +260,7 @@ You can list packages and variables: SELECT * FROM pgv_list() order by package, name; package | name | is_transactional ---------+-------+------------------ + vars | arr1 | f vars | int1 | f vars | r1 | f vars | text1 | f @@ -257,7 +272,7 @@ And get used memory in bytes: SELECT * FROM pgv_stats() order by package; package | allocated_memory ---------+------------------ - vars | 32768 + vars | 49152 ``` You can delete variables or whole packages: diff --git a/pg_variables--1.1--1.2.sql b/pg_variables--1.1--1.2.sql new file mode 100644 index 0000000..b28a964 --- /dev/null +++ b/pg_variables--1.1--1.2.sql @@ -0,0 +1,16 @@ +/* contrib/pg_variables/pg_variables--1.1--1.2.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_variables UPDATE TO '1.2'" to load this file. \quit + +-- Functions to work with arrays + +CREATE FUNCTION pgv_set(package text, name text, value anyarray, is_transactional bool default false) +RETURNS void +AS 'MODULE_PATHNAME', 'variable_set_array' +LANGUAGE C VOLATILE; + +CREATE FUNCTION pgv_get(package text, name text, var_type anyarray, strict bool default true) +RETURNS anyarray +AS 'MODULE_PATHNAME', 'variable_get_array' +LANGUAGE C VOLATILE; diff --git a/pg_variables.c b/pg_variables.c index 0e56bb1..6f7dd17 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -237,6 +237,7 @@ VARIABLE_GET_TEMPLATE(0, 1, 2, jsonb, JSONBOID) /* current API */ VARIABLE_GET_TEMPLATE(0, 1, 3, any, get_fn_expr_argtype(fcinfo->flinfo, 2)) +VARIABLE_GET_TEMPLATE(0, 1, 3, array, get_fn_expr_argtype(fcinfo->flinfo, 2)) #define VARIABLE_SET_TEMPLATE(type, typid) \ @@ -275,6 +276,7 @@ VARIABLE_SET_TEMPLATE(jsonb, JSONBOID) /* current API */ VARIABLE_SET_TEMPLATE(any, get_fn_expr_argtype(fcinfo->flinfo, 2)) +VARIABLE_SET_TEMPLATE(array, get_fn_expr_argtype(fcinfo->flinfo, 2)) Datum diff --git a/pg_variables.control b/pg_variables.control index 2776600..2d049e7 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.1' +default_version = '1.2' module_pathname = '$libdir/pg_variables' relocatable = true diff --git a/sql/pg_variables_any.sql b/sql/pg_variables_any.sql index 3b9d488..39da5c1 100644 --- a/sql/pg_variables_any.sql +++ b/sql/pg_variables_any.sql @@ -139,6 +139,26 @@ SELECT pgv_get('vars', 'd1', NULL::jsonb); SELECT pgv_set('vars', 'jNULL', NULL::jsonb); SELECT pgv_get('vars', 'jNULL', NULL::jsonb); +-- Array variables +SELECT pgv_set('vars', 'arr1', '{1, 2, null}'::int[]); +SELECT pgv_set('vars', 'arr2', '{"bar", "balance", "active"}'::text[]); +SELECT pgv_set('vars2', 'j1', '{1, 2, null}'::int[]); + +SELECT pgv_get('vars', 'arr1', NULL::int[]); +SELECT pgv_get('vars', 'arr2', NULL::int[]); +SELECT pgv_set('vars', 'arr1', '{"bar", "balance", "active"}'::text[]); +SELECT pgv_set('vars', 'arr1', '{3, 4, 5}'::int[]); +SELECT pgv_get('vars', 'arr1', NULL::int[]); + +SELECT pgv_get('vars', 'arr3', NULL::int[]); +SELECT pgv_get('vars', 'arr3', NULL::int[], false); +SELECT pgv_exists('vars', 'arr3'); +SELECT pgv_exists('vars', 'arr1'); +SELECT pgv_get('vars2', 'j1', NULL::int[]); + +SELECT pgv_set('vars', 'arrNULL', NULL::int[]); +SELECT pgv_get('vars', 'arrNULL', NULL::int[]); + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; From 8da2d2dbaf7c5add5b67b10b17c8c018c594609f Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 23 Jan 2019 19:40:33 +0300 Subject: [PATCH 058/147] Added forgot expected tests result --- expected/pg_variables_any.out | 81 ++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/expected/pg_variables_any.out b/expected/pg_variables_any.out index 12987b0..814b40a 100644 --- a/expected/pg_variables_any.out +++ b/expected/pg_variables_any.out @@ -530,10 +530,84 @@ SELECT pgv_get('vars', 'jNULL', NULL::jsonb); (1 row) +-- Array variables +SELECT pgv_set('vars', 'arr1', '{1, 2, null}'::int[]); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'arr2', '{"bar", "balance", "active"}'::text[]); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 'j1', '{1, 2, null}'::int[]); +ERROR: variable "j1" requires "jsonb" value +SELECT pgv_get('vars', 'arr1', NULL::int[]); + pgv_get +------------ + {1,2,NULL} +(1 row) + +SELECT pgv_get('vars', 'arr2', NULL::int[]); +ERROR: variable "arr2" requires "text[]" value +SELECT pgv_set('vars', 'arr1', '{"bar", "balance", "active"}'::text[]); +ERROR: variable "arr1" requires "integer[]" value +SELECT pgv_set('vars', 'arr1', '{3, 4, 5}'::int[]); + pgv_set +--------- + +(1 row) + +SELECT pgv_get('vars', 'arr1', NULL::int[]); + pgv_get +--------- + {3,4,5} +(1 row) + +SELECT pgv_get('vars', 'arr3', NULL::int[]); +ERROR: unrecognized variable "arr3" +SELECT pgv_get('vars', 'arr3', NULL::int[], false); + pgv_get +--------- + +(1 row) + +SELECT pgv_exists('vars', 'arr3'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_exists('vars', 'arr1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars2', 'j1', NULL::int[]); +ERROR: variable "j1" requires "jsonb" value +SELECT pgv_set('vars', 'arrNULL', NULL::int[]); + pgv_set +--------- + +(1 row) + +SELECT pgv_get('vars', 'arrNULL', NULL::int[]); + pgv_get +--------- + +(1 row) + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; package | name | is_transactional ---------+----------+------------------ + vars | arr1 | f + vars | arr2 | f + vars | arrNULL | f vars | d1 | f vars | d2 | f vars | dNULL | f @@ -555,7 +629,7 @@ SELECT * FROM pgv_list() order by package, name; vars | tstzNULL | f vars2 | j1 | f vars2 | j2 | f -(21 rows) +(24 rows) SELECT pgv_remove('vars', 'int3'); ERROR: unrecognized variable "int3" @@ -590,6 +664,9 @@ SELECT pgv_exists('vars2'); SELECT * FROM pgv_list() order by package, name; package | name | is_transactional ---------+----------+------------------ + vars | arr1 | f + vars | arr2 | f + vars | arrNULL | f vars | d1 | f vars | d2 | f vars | dNULL | f @@ -608,7 +685,7 @@ SELECT * FROM pgv_list() order by package, name; vars | tstz1 | f vars | tstz2 | f vars | tstzNULL | f -(18 rows) +(21 rows) SELECT pgv_free(); pgv_free From c51714dcf4ac86668ca1c1609da9e550323db64d Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 25 Jan 2019 18:23:29 +0300 Subject: [PATCH 059/147] Cache last used type oid to do faster variable_insert(), variable_update() --- pg_variables.c | 70 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 6f7dd17..fa2547c 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -57,6 +57,7 @@ static Variable *createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional); static void removePackageInternal(Package *package); +static void resetVariablesCache(bool with_package); /* Functions to work with transactional objects */ static void createSavepoint(TransObject *object, TransObjectType type); @@ -99,6 +100,8 @@ static MemoryContext ModuleContext = NULL; static Package *LastPackage = NULL; /* Recent variable */ static Variable *LastVariable = NULL; +/* Recent row type id */ +static Oid LastTypeId = InvalidOid; /* This stack contains lists of changed variables and packages per each subxact level */ @@ -291,7 +294,7 @@ variable_insert(PG_FUNCTION_ARGS) Oid tupType; int32 tupTypmod; - TupleDesc tupdesc; + TupleDesc tupdesc = NULL; RecordVar *record; /* Checks */ @@ -360,7 +363,6 @@ variable_insert(PG_FUNCTION_ARGS) /* Insert a record */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); record = &(GetActualValue(variable).record); if (!record->tupdesc) @@ -368,15 +370,27 @@ variable_insert(PG_FUNCTION_ARGS) /* * This is the first record for the var_name. Initialize record. */ + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); init_record(record, tupdesc, variable); } - else + else if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || + LastTypeId != tupType) + { + /* + * We need to check attributes of the new row if this is a transient + * record type or if last record has different id. + */ + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); check_attributes(variable, tupdesc); + } + + LastTypeId = tupType; insert_record(variable, rec); /* Release resources */ - ReleaseTupleDesc(tupdesc); + if (tupdesc) + ReleaseTupleDesc(tupdesc); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -396,7 +410,6 @@ variable_update(PG_FUNCTION_ARGS) bool res; Oid tupType; int32 tupTypmod; - TupleDesc tupdesc; /* Checks */ CHECK_ARGS_FOR_NULL(); @@ -447,14 +460,22 @@ variable_update(PG_FUNCTION_ARGS) /* Update a record */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - check_attributes(variable, tupdesc); + if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || + LastTypeId != tupType) + { + TupleDesc tupdesc = NULL; + + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + check_attributes(variable, tupdesc); + ReleaseTupleDesc(tupdesc); + } + + LastTypeId = tupType; + res = update_record(variable, rec); /* Release resources */ - ReleaseTupleDesc(tupdesc); - PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -867,8 +888,7 @@ remove_variable(PG_FUNCTION_ARGS) GetActualState(variable)->is_valid = false; } - /* Remove variable from cache */ - LastVariable = NULL; + resetVariablesCache(false); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -904,9 +924,7 @@ remove_package(PG_FUNCTION_ARGS) errmsg("unrecognized package \"%s\"", key))); } - /* Remove package and variable from cache */ - LastPackage = NULL; - LastVariable = NULL; + resetVariablesCache(true); PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); @@ -934,6 +952,20 @@ removePackageInternal(Package *package) GetActualState(package)->is_valid = false; } +/* + * Reset cache variables to their default values. It is necessary to do in case + * of some changes: removing, rollbacking, etc. + */ +static void +resetVariablesCache(bool with_package) +{ + /* Remove package and variable from cache */ + if (with_package) + LastPackage = NULL; + LastVariable = NULL; + LastTypeId = InvalidOid; +} + /* * Remove all packages and variables. * Memory context will be released after committing. @@ -955,9 +987,7 @@ remove_packages(PG_FUNCTION_ARGS) removePackageInternal(package); } - /* Remove package and variable from cache */ - LastPackage = NULL; - LastVariable = NULL; + resetVariablesCache(true); PG_RETURN_VOID(); } @@ -1632,8 +1662,7 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); - LastPackage = NULL; - LastVariable = NULL; + resetVariablesCache(true); } /* @@ -2004,8 +2033,7 @@ processChanges(Action action) MemoryContextDelete(ModuleContext); packagesHash = NULL; ModuleContext = NULL; - LastPackage = NULL; - LastVariable = NULL; + resetVariablesCache(true); changesStack = NULL; changesStackContext = NULL; } From 8766f08eaef86da0bf73d0bd39db265211ddbc0b Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 25 Jan 2019 18:41:17 +0300 Subject: [PATCH 060/147] Clean up hash table sequential scan status --- pg_variables.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index fa2547c..971b488 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -81,6 +81,9 @@ static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, MemoryContext ctx); +/* Hook functions */ +static void variable_ExecutorEnd(QueryDesc *queryDesc); + #define CHECK_ARGS_FOR_NULL() \ do { \ if (fcinfo->argnull[0]) \ @@ -103,6 +106,16 @@ static Variable *LastVariable = NULL; /* Recent row type id */ static Oid LastTypeId = InvalidOid; +/* + * Cache sequentially search through hash table status. It is necessary for + * clean up if hash_seq_term() wasn't called or if we didn't scan the whole + * table. In this case we need to manually call hash_seq_term() within + * variable_ExecutorEnd(). + */ +static HASH_SEQ_STATUS *LastHSeqStatus = NULL; + +/* Saved hook values for recall */ +static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; /* This stack contains lists of changed variables and packages per each subxact level */ static dlist_head *changesStack = NULL; @@ -596,6 +609,8 @@ variable_select(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); + + LastHSeqStatus = rstat; } funcctx = SRF_PERCALL_SETUP(); @@ -613,6 +628,7 @@ variable_select(PG_FUNCTION_ARGS) } else { + LastHSeqStatus = NULL; pfree(rstat); SRF_RETURN_DONE(funcctx); } @@ -1187,6 +1203,8 @@ get_packages_stats(PG_FUNCTION_ARGS) hash_seq_init(pstat, packagesHash); funcctx->user_fctx = pstat; + + LastHSeqStatus = pstat; } else funcctx->user_fctx = NULL; @@ -1232,6 +1250,7 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { + LastHSeqStatus = NULL; pfree(pstat); SRF_RETURN_DONE(funcctx); } @@ -2093,6 +2112,23 @@ pgvTransCallback(XactEvent event, void *arg) } } +/* + * ExecutorEnd hook: clean up hash table sequential scan status + */ +static void +variable_ExecutorEnd(QueryDesc *queryDesc) +{ + if (LastHSeqStatus) + { + hash_seq_term(LastHSeqStatus); + LastHSeqStatus = NULL; + } + if (prev_ExecutorEnd) + prev_ExecutorEnd(queryDesc); + else + standard_ExecutorEnd(queryDesc); +} + /* * Register callback function when module starts */ @@ -2101,6 +2137,10 @@ _PG_init(void) { RegisterXactCallback(pgvTransCallback, NULL); RegisterSubXactCallback(pgvSubTransCallback, NULL); + + /* Install hooks. */ + prev_ExecutorEnd = ExecutorEnd_hook; + ExecutorEnd_hook = variable_ExecutorEnd; } /* @@ -2111,4 +2151,5 @@ _PG_fini(void) { UnregisterXactCallback(pgvTransCallback, NULL); UnregisterSubXactCallback(pgvSubTransCallback, NULL); + ExecutorEnd_hook = prev_ExecutorEnd; } From 5cdf6a049c6de3b1c253aff6d86aeb9b2ba711ae Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 25 Jan 2019 18:56:21 +0300 Subject: [PATCH 061/147] Improve tests --- expected/pg_variables.out | 14 ++++++++++++++ sql/pg_variables.sql | 3 +++ 2 files changed, 17 insertions(+) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 7293c6c..2bc5520 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -553,6 +553,20 @@ SELECT pgv_insert('vars3', 'r1', row(1, 1)); ERROR: new record structure differs from variable "r1" structure SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); ERROR: new record structure differs from variable "r1" structure +SELECT pgv_select('vars3', 'r1') LIMIT 2; + pgv_select +------------ + (,strNULL) + (1,str11) +(2 rows) + +SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + pgv_select +------------ + (2,) + (0,str00) +(2 rows) + SELECT pgv_select('vars3', 'r1'); pgv_select ------------ diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 36778ad..9d5fcf3 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -153,6 +153,9 @@ SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); SELECT pgv_insert('vars3', 'r1', row(1, 1)); SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); +SELECT pgv_select('vars3', 'r1') LIMIT 2; +SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + SELECT pgv_select('vars3', 'r1'); SELECT pgv_select('vars3', 'r1', 1); SELECT pgv_select('vars3', 'r1', 0); From dd9f391a0587c48a91c2fc76828195f9d319fdbf Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 28 Jan 2019 15:14:40 +0300 Subject: [PATCH 062/147] Delete a variable if an error occurs during its creation --- expected/pg_variables_trans.out | 5 +++++ pg_variables.c | 13 ++++++++----- pg_variables.h | 1 + pg_variables_record.c | 12 ++++++++++++ sql/pg_variables_trans.sql | 4 ++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 87741f9..58468c7 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1854,6 +1854,11 @@ SELECT pgv_insert('package', 'errs',row(1), true); (1 row) +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT pgv_select('vars4', 'r1', 0); +ERROR: unrecognized variable "r1" SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index 971b488..be27be7 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -68,7 +68,6 @@ static void copyValue(VarState *src, VarState *dest, Variable *destVar); static void freeValue(VarState *varstate, Oid typid); static void removeState(TransObject *object, TransObjectType type, TransState *stateToDelete); -static void removeObject(TransObject *object, TransObjectType type); static bool isObjectChangedInCurrentTrans(TransObject *object); static bool isObjectChangedInUpperTrans(TransObject *object); @@ -1626,13 +1625,14 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) static void freeValue(VarState *varstate, Oid typid) { - if (typid == RECORDOID) + if (typid == RECORDOID && varstate->value.record.hctx) { /* All records will be freed */ MemoryContextDelete(varstate->value.record.hctx); } else if (varstate->value.scalar.typbyval == false && - varstate->value.scalar.is_null == false) + varstate->value.scalar.is_null == false && + varstate->value.scalar.value) { pfree(DatumGetPointer(varstate->value.scalar.value)); } @@ -1651,7 +1651,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete pfree(stateToDelete); } -static void +/* Remove package or variable (either transactional or regular) */ +void removeObject(TransObject *object, TransObjectType type) { bool found; @@ -1672,7 +1673,9 @@ removeObject(TransObject *object, TransObjectType type) hash = packagesHash; } else - hash = ((Variable *) object)->package->varHashTransact; + hash = ((Variable *) object)->is_transactional ? + ((Variable *) object)->package->varHashTransact : + ((Variable *) object)->package->varHashRegular; /* Remove all object's states */ while (!dlist_is_empty(&object->states)) diff --git a/pg_variables.h b/pg_variables.h index cc01445..a26eac6 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -155,6 +155,7 @@ extern void check_record_key(Variable *variable, Oid typid); extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool delete_record(Variable *variable, Datum value, bool is_null); +extern void removeObject(TransObject *object, TransObjectType type); #define GetActualState(object) \ (dlist_head_element(TransState, node, &((TransObject *) object)->states)) diff --git a/pg_variables_record.c b/pg_variables_record.c index 2749b23..2bb40b5 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -81,17 +81,29 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) TYPECACHE_HASH_PROC_FINFO | TYPECACHE_CMP_PROC_FINFO); + /* + * In case something went wrong, you need to roll back the changes before + * completing the transaction, because the variable may be regular + * and not present in list of changed vars. + */ if (!OidIsValid(typentry->hash_proc_finfo.fn_oid)) + { + /* At this point variable is just created, so we simply remove it. */ + removeObject(&variable->transObject, TRANS_VARIABLE); ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a hash function for type %s", format_type_be(keyid)))); + } if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) + { + removeObject(&variable->transObject, TRANS_VARIABLE); ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a matching function for type %s", format_type_be(keyid)))); + } /* Initialize the record */ diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 069b7cd..a66feaf 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -467,4 +467,8 @@ SELECT pgv_insert('package', 'errs',row(n), true) FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; SELECT pgv_insert('package', 'errs',row(1), true); +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +SELECT pgv_select('vars4', 'r1', 0); + SELECT pgv_free(); From 20fee6329681d07cf293e04badae4fd59fbecb59 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 29 Jan 2019 16:29:53 +0300 Subject: [PATCH 063/147] Remove variable from changes list in case If variable created and removed in same transaction level --- expected/pg_variables_trans.out | 21 +++++++++++++ pg_variables.c | 55 +++++++++++++++++++-------------- sql/pg_variables_trans.sql | 11 +++++++ 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 58468c7..d5b8fb5 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1859,6 +1859,27 @@ SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); ERROR: could not identify a hash function for type unknown SELECT pgv_select('vars4', 'r1', 0); ERROR: unrecognized variable "r1" +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized variable "any1" +COMMIT; SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index be27be7..54f543b 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -73,7 +73,7 @@ static bool isObjectChangedInUpperTrans(TransObject *object); static void addToChangesStack(TransObject *object, TransObjectType type); static void pushChangesStack(void); -static void removeFromChangedVars(Package *package); +static void removeFromChangesStack(TransObject *transObj, TransObjectType type); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); @@ -1658,16 +1658,16 @@ removeObject(TransObject *object, TransObjectType type) bool found; HTAB *hash; + /* + * Delete an object from the change history of the overlying + * transaction level (head of 'changesStack' at this point). + */ + if (!dlist_is_empty(changesStack)) + removeFromChangesStack(object, type); if (type == TRANS_PACKAGE) { Package *package = (Package *) object; - /* - * Delete a variable from the change history of the overlying - * transaction level (head of 'changesStack' at this point) - */ - if (!dlist_is_empty(changesStack)) - removeFromChangedVars(package); /* Regular variables had already removed */ MemoryContextDelete(package->hctxTransact); hash = packagesHash; @@ -1939,33 +1939,40 @@ addToChangesStack(TransObject *transObj, TransObjectType type) * Remove from the changes list a deleted package */ static void -removeFromChangedVars(Package *package) +removeFromChangesStack(TransObject *object, TransObjectType type) { dlist_mutable_iter var_miter, - pack_miter; - dlist_head *changedVarsList, - *changedPacksList; + pack_miter; + dlist_head *changesList; + ChangesStackNode *csn = get_actual_changes_list(); - /* First remove corresponding variables from changedVarsList */ - changedVarsList = get_actual_changes_list()->changedVarsList; - dlist_foreach_modify(var_miter, changedVarsList) + /* + * If we remove package, we should remove corresponding variables + * from changedVarsList first. + */ + if (type == TRANS_PACKAGE) { - ChangedObject *co_cur = dlist_container(ChangedObject, node, - var_miter.cur); - Variable *var = (Variable *) co_cur->object; + changesList = csn->changedVarsList; + dlist_foreach_modify(var_miter, changesList) + { + ChangedObject *co_cur = dlist_container(ChangedObject, node, + var_miter.cur); + Variable *var = (Variable *) co_cur->object; - if (var->package == package) - dlist_delete(&co_cur->node); + if (var->package == (Package *)object) + dlist_delete(&co_cur->node); + } } - /* Now remove package itself from changedPacksList */ - changedPacksList = get_actual_changes_list()->changedPacksList; - dlist_foreach_modify(pack_miter, changedPacksList) + /* Now remove object itself from changes list */ + changesList = (type == TRANS_PACKAGE ? csn->changedPacksList : + csn->changedVarsList); + dlist_foreach_modify(pack_miter, changesList) { ChangedObject *co_cur = dlist_container(ChangedObject, node, pack_miter.cur); - Package *pack = (Package *) co_cur->object; + TransObject *obj = co_cur->object; - if (pack == package) + if (obj == object) { dlist_delete(&co_cur->node); break; diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index a66feaf..fc0b876 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -471,4 +471,15 @@ SELECT pgv_insert('package', 'errs',row(1), true); SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); SELECT pgv_select('vars4', 'r1', 0); +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +COMMIT; + SELECT pgv_free(); From 4f3d9ec80e6c65f281e7b81632da05044ecbba26 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 29 Jan 2019 16:51:47 +0300 Subject: [PATCH 064/147] Unify functions interface for working with TransObject type --- pg_variables.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 54f543b..74d0341 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -73,7 +73,7 @@ static bool isObjectChangedInUpperTrans(TransObject *object); static void addToChangesStack(TransObject *object, TransObjectType type); static void pushChangesStack(void); -static void removeFromChangesStack(TransObject *transObj, TransObjectType type); +static void removeFromChangesStack(TransObject *object, TransObjectType type); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); @@ -1691,24 +1691,24 @@ removeObject(TransObject *object, TransObjectType type) * Create a new state of object */ static void -createSavepoint(TransObject *transObj, TransObjectType type) +createSavepoint(TransObject *object, TransObjectType type) { TransState *newState, *prevState; - prevState = GetActualState(transObj); + prevState = GetActualState(object); if (type == TRANS_PACKAGE) newState = (TransState *) MemoryContextAllocZero(ModuleContext, sizeof(PackState)); else { - Variable *var = (Variable *) transObj; + Variable *var = (Variable *) object; newState = (TransState *) MemoryContextAllocZero(var->package->hctxTransact, sizeof(VarState)); copyValue((VarState *) prevState, (VarState *) newState, var); } - dlist_push_head(&transObj->states, &newState->node); + dlist_push_head(&object->states, &newState->node); newState->is_valid = prevState->is_valid; } @@ -1809,14 +1809,14 @@ releaseSavepoint(TransObject *object, TransObjectType type) * Check if object was changed in current transaction level */ static bool -isObjectChangedInCurrentTrans(TransObject *transObj) +isObjectChangedInCurrentTrans(TransObject *object) { TransState *state; if (!changesStack) return false; - state = GetActualState(transObj); + state = GetActualState(object); return state->level == GetCurrentTransactionNestLevel(); } @@ -1916,22 +1916,22 @@ makeChangedObject(TransObject *object, MemoryContext ctx) * in current transaction level */ static void -addToChangesStack(TransObject *transObj, TransObjectType type) +addToChangesStack(TransObject *object, TransObjectType type) { prepareChangesStack(); - if (!isObjectChangedInCurrentTrans(transObj)) + if (!isObjectChangedInCurrentTrans(object)) { ChangesStackNode *csn; ChangedObject *co; csn = get_actual_changes_list(); - co = makeChangedObject(transObj, csn->ctx); + co = makeChangedObject(object, csn->ctx); dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : csn->changedVarsList, &co->node); /* Give this object current subxact level */ - GetActualState(transObj)->level = GetCurrentTransactionNestLevel(); + GetActualState(object)->level = GetCurrentTransactionNestLevel(); } } From 61ea0bd12a80a5237d06be7d8dfac36f8e3a929b Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 30 Jan 2019 16:46:20 +0300 Subject: [PATCH 065/147] Clarify the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87867e8..d7d897f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Typical installation procedure may look like this: ## Module functions The functions provided by the **pg_variables** module are shown in the tables -below. The module supports the following scalar and record types. +below. To use **pgv_get()** function required package and variable must exists. It is necessary to set variable with **pgv_set()** function to use **pgv_get()** From 68edd02456eb298d302104ba143b83bb1e0a8bcd Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 31 Jan 2019 15:19:54 +0300 Subject: [PATCH 066/147] Fix for 8766f08eae: Use xact callback instead of the hook, it allowes to clean up after rollback --- pg_variables.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 74d0341..7a299e8 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -80,9 +80,6 @@ static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, MemoryContext ctx); -/* Hook functions */ -static void variable_ExecutorEnd(QueryDesc *queryDesc); - #define CHECK_ARGS_FOR_NULL() \ do { \ if (fcinfo->argnull[0]) \ @@ -113,9 +110,6 @@ static Oid LastTypeId = InvalidOid; */ static HASH_SEQ_STATUS *LastHSeqStatus = NULL; -/* Saved hook values for recall */ -static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; - /* This stack contains lists of changed variables and packages per each subxact level */ static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; @@ -2120,23 +2114,15 @@ pgvTransCallback(XactEvent event, void *arg) break; } } -} -/* - * ExecutorEnd hook: clean up hash table sequential scan status - */ -static void -variable_ExecutorEnd(QueryDesc *queryDesc) -{ - if (LastHSeqStatus) - { - hash_seq_term(LastHSeqStatus); - LastHSeqStatus = NULL; - } - if (prev_ExecutorEnd) - prev_ExecutorEnd(queryDesc); - else - standard_ExecutorEnd(queryDesc); + if (event == XACT_EVENT_PARALLEL_COMMIT || event == XACT_EVENT_COMMIT || + event == XACT_EVENT_PREPARE || + event == XACT_EVENT_PARALLEL_ABORT || event == XACT_EVENT_ABORT) + if (LastHSeqStatus) + { + hash_seq_term(LastHSeqStatus); + LastHSeqStatus = NULL; + } } /* @@ -2147,10 +2133,6 @@ _PG_init(void) { RegisterXactCallback(pgvTransCallback, NULL); RegisterSubXactCallback(pgvSubTransCallback, NULL); - - /* Install hooks. */ - prev_ExecutorEnd = ExecutorEnd_hook; - ExecutorEnd_hook = variable_ExecutorEnd; } /* @@ -2161,5 +2143,4 @@ _PG_fini(void) { UnregisterXactCallback(pgvTransCallback, NULL); UnregisterSubXactCallback(pgvSubTransCallback, NULL); - ExecutorEnd_hook = prev_ExecutorEnd; } From e5af4d33bcc16f026086f55c078af97df3c59d89 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 31 Jan 2019 15:28:02 +0300 Subject: [PATCH 067/147] Fix for 68edd02456eb: We need the hook anyway if we use long transactions --- pg_variables.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index 7a299e8..85a90d8 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -80,6 +80,9 @@ static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, MemoryContext ctx); +/* Hook functions */ +static void variable_ExecutorEnd(QueryDesc *queryDesc); + #define CHECK_ARGS_FOR_NULL() \ do { \ if (fcinfo->argnull[0]) \ @@ -110,6 +113,9 @@ static Oid LastTypeId = InvalidOid; */ static HASH_SEQ_STATUS *LastHSeqStatus = NULL; +/* Saved hook values for recall */ +static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; + /* This stack contains lists of changed variables and packages per each subxact level */ static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; @@ -2125,6 +2131,23 @@ pgvTransCallback(XactEvent event, void *arg) } } +/* + * ExecutorEnd hook: clean up hash table sequential scan status + */ +static void +variable_ExecutorEnd(QueryDesc *queryDesc) +{ + if (LastHSeqStatus) + { + hash_seq_term(LastHSeqStatus); + LastHSeqStatus = NULL; + } + if (prev_ExecutorEnd) + prev_ExecutorEnd(queryDesc); + else + standard_ExecutorEnd(queryDesc); +} + /* * Register callback function when module starts */ @@ -2133,6 +2156,10 @@ _PG_init(void) { RegisterXactCallback(pgvTransCallback, NULL); RegisterSubXactCallback(pgvSubTransCallback, NULL); + + /* Install hooks. */ + prev_ExecutorEnd = ExecutorEnd_hook; + ExecutorEnd_hook = variable_ExecutorEnd; } /* @@ -2143,4 +2170,5 @@ _PG_fini(void) { UnregisterXactCallback(pgvTransCallback, NULL); UnregisterSubXactCallback(pgvSubTransCallback, NULL); + ExecutorEnd_hook = prev_ExecutorEnd; } From 1b0b3bec601a177ebf8947e51382925c11901d07 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 31 Jan 2019 15:44:28 +0300 Subject: [PATCH 068/147] Fix for 68edd02456eb: Do not call hash_seq_term(), we can't trust to its pointer in the callback function --- expected/pg_variables.out | 43 ++++++++++++++++++++++++++------------- pg_variables.c | 7 +------ sql/pg_variables.sql | 14 ++++++++++--- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 2bc5520..0afdb44 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -553,20 +553,6 @@ SELECT pgv_insert('vars3', 'r1', row(1, 1)); ERROR: new record structure differs from variable "r1" structure SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); ERROR: new record structure differs from variable "r1" structure -SELECT pgv_select('vars3', 'r1') LIMIT 2; - pgv_select ------------- - (,strNULL) - (1,str11) -(2 rows) - -SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; - pgv_select ------------- - (2,) - (0,str00) -(2 rows) - SELECT pgv_select('vars3', 'r1'); pgv_select ------------ @@ -657,6 +643,35 @@ SELECT pgv_exists('vars3', 'r1'); SELECT pgv_select('vars2', 'j1'); ERROR: variable "j1" requires "jsonb" value +-- Tests for SRF's sequential scan of an internal hash table +DO +$$BEGIN + PERFORM pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + PERFORM pgv_select('vars3', 'r3'); +END$$; +ERROR: unrecognized variable "r3" +CONTEXT: SQL statement "SELECT pgv_select('vars3', 'r3')" +PL/pgSQL function inline_code_block line 3 at PERFORM +-- Check that the hash table was cleaned up after rollback +SELECT pgv_select('vars3', 'r1', 1); + pgv_select +------------ + +(1 row) + +SELECT pgv_select('vars3', 'r1') LIMIT 2; + pgv_select +------------ + (,strNULL) + (2,) +(2 rows) + +SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + pgv_select +------------ + (0,str00) +(1 row) + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; package | name | is_transactional diff --git a/pg_variables.c b/pg_variables.c index 85a90d8..33626b3 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -2121,14 +2121,9 @@ pgvTransCallback(XactEvent event, void *arg) } } - if (event == XACT_EVENT_PARALLEL_COMMIT || event == XACT_EVENT_COMMIT || - event == XACT_EVENT_PREPARE || - event == XACT_EVENT_PARALLEL_ABORT || event == XACT_EVENT_ABORT) + if (event == XACT_EVENT_PARALLEL_ABORT || event == XACT_EVENT_ABORT) if (LastHSeqStatus) - { - hash_seq_term(LastHSeqStatus); LastHSeqStatus = NULL; - } } /* diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 9d5fcf3..9838fb5 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -153,9 +153,6 @@ SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); SELECT pgv_insert('vars3', 'r1', row(1, 1)); SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); -SELECT pgv_select('vars3', 'r1') LIMIT 2; -SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; - SELECT pgv_select('vars3', 'r1'); SELECT pgv_select('vars3', 'r1', 1); SELECT pgv_select('vars3', 'r1', 0); @@ -175,6 +172,17 @@ SELECT pgv_exists('vars3', 'r3'); SELECT pgv_exists('vars3', 'r1'); SELECT pgv_select('vars2', 'j1'); +-- Tests for SRF's sequential scan of an internal hash table +DO +$$BEGIN + PERFORM pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + PERFORM pgv_select('vars3', 'r3'); +END$$; +-- Check that the hash table was cleaned up after rollback +SELECT pgv_select('vars3', 'r1', 1); +SELECT pgv_select('vars3', 'r1') LIMIT 2; +SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; SELECT package FROM pgv_stats() order by package; From e9d74c452f07941e63dbf1cf46348130c40feb49 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 1 Feb 2019 16:04:46 +0300 Subject: [PATCH 069/147] Remove package automatically when it becomes empty --- README.md | 4 +++ expected/pg_variables_trans.out | 10 +++---- pg_variables.c | 53 +++++++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d7d897f..0c584c0 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ SELECT pgv_get('vars', 'trans_int', NULL::int); 101 ``` +You can aggregate variables into packages. This is done to be able to have +variables with different names or to quickly remove the whole batch of +variables. If the package becomes empty, it is automatically deleted. + ## License This module available under the [license](LICENSE) similar to diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index d5b8fb5..397afac 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1035,7 +1035,7 @@ SELECT pgv_get('vars2', 'any1',NULL::text); ROLLBACK TO sp1; COMMIT; SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized variable "any1" +ERROR: unrecognized package "vars2" SELECT pgv_free(); pgv_free ---------- @@ -1385,7 +1385,7 @@ SELECT pgv_get('vars2', 'any1',NULL::text); ROLLBACK; SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized variable "any1" +ERROR: unrecognized package "vars2" SELECT pgv_free(); pgv_free ---------- @@ -1652,7 +1652,7 @@ SELECT pgv_exists('vars', 'any1'); (1 row) SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized variable "any1" +ERROR: unrecognized package "vars" SELECT * FROM pgv_list() ORDER BY package, name; package | name | is_transactional ---------+------+------------------ @@ -1858,7 +1858,7 @@ SELECT pgv_insert('package', 'errs',row(1), true); SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); ERROR: could not identify a hash function for type unknown SELECT pgv_select('vars4', 'r1', 0); -ERROR: unrecognized variable "r1" +ERROR: unrecognized package "vars4" -- If variable created and removed in same transaction level, -- it should be totally removed and should not be present -- in changes list and cache. @@ -1878,7 +1878,7 @@ SELECT pgv_remove('vars', 'any1'); RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized variable "any1" +ERROR: unrecognized package "vars" COMMIT; SELECT pgv_free(); pgv_free diff --git a/pg_variables.c b/pg_variables.c index 33626b3..1c6318e 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -955,6 +955,7 @@ removePackageInternal(Package *package) { MemoryContextDelete(package->hctxRegular); package->hctxRegular = NULL; + package->varHashRegular = NULL; } /* Add to changes list */ @@ -967,6 +968,17 @@ removePackageInternal(Package *package) GetActualState(package)->is_valid = false; } +static bool +isPackageEmpty(Package *package) +{ + int var_num = hash_get_num_entries(package->varHashTransact); + + if (package->varHashRegular) + var_num += hash_get_num_entries(package->varHashRegular); + + return var_num == 0; +} + /* * Reset cache variables to their default values. It is necessary to do in case * of some changes: removing, rollbacking, etc. @@ -1657,6 +1669,7 @@ removeObject(TransObject *object, TransObjectType type) { bool found; HTAB *hash; + Package *package = NULL; /* * Delete an object from the change history of the overlying @@ -1666,16 +1679,20 @@ removeObject(TransObject *object, TransObjectType type) removeFromChangesStack(object, type); if (type == TRANS_PACKAGE) { - Package *package = (Package *) object; + package = (Package *) object; /* Regular variables had already removed */ MemoryContextDelete(package->hctxTransact); hash = packagesHash; } else - hash = ((Variable *) object)->is_transactional ? - ((Variable *) object)->package->varHashTransact : - ((Variable *) object)->package->varHashRegular; + { + Variable *var = (Variable *) object; + package = var->package; + hash = var->is_transactional ? + var->package->varHashTransact : + var->package->varHashRegular; + } /* Remove all object's states */ while (!dlist_is_empty(&object->states)) @@ -1684,6 +1701,12 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); + /* Remove package if it is became empty */ + if (type == TRANS_VARIABLE && + isObjectChangedInCurrentTrans(&package->transObject) && + isPackageEmpty(package)) + GetActualState(&package->transObject)->is_valid = false; + resetVariablesCache(true); } @@ -1725,8 +1748,19 @@ rollbackSavepoint(TransObject *object, TransObjectType type) { if (!state->is_valid) { - dlist_pop_head_node(&object->states); - pfree(state); + if (isPackageEmpty((Package *)object)) + { + removeObject(object, TRANS_PACKAGE); + return; + } + + if (dlist_has_next(&object->states, &state->node)) + { + dlist_pop_head_node(&object->states); + pfree(state); + } + else + state->is_valid = true; /* Restore regular vars HTAB */ makePackHTAB((Package *) object, false); } @@ -1797,6 +1831,13 @@ releaseSavepoint(TransObject *object, TransObjectType type) !dlist_has_next(states, dlist_head_node(states))) { removeObject(object, type); + /* Remove package if it becomes empty */ + if (type == TRANS_VARIABLE) + { + Package *pack = ((Variable *) object)->package; + if (isPackageEmpty(pack)) + (GetActualState(&pack->transObject))->is_valid = false; + } return; } } From 51f3ace266e381a85ceae82ddd3a77f7a29ae848 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Fri, 1 Feb 2019 22:49:43 +0300 Subject: [PATCH 070/147] Fix adding package to changes list after last variable removal --- expected/pg_variables.out | 26 +++++++++++++++ pg_variables.c | 68 ++++++++++++++++++--------------------- sql/pg_variables.sql | 9 ++++++ 3 files changed, 67 insertions(+), 36 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 0afdb44..3ef1026 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -672,6 +672,32 @@ SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; (0,str00) (1 row) +-- Clean memory after unsuccessful creation of a variable +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT package FROM pgv_stats() WHERE package = 'vars4'; + package +--------- +(0 rows) + +-- Remove package if it is empty +SELECT pgv_insert('vars4', 'r2', row(1, 'str1', 'str2')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_remove('vars4', 'r2'); + pgv_remove +------------ + +(1 row) + +SELECT package FROM pgv_stats() WHERE package = 'vars4'; + package +--------- +(0 rows) + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; package | name | is_transactional diff --git a/pg_variables.c b/pg_variables.c index 1c6318e..f970435 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -52,7 +52,7 @@ static void getKeyFromName(text *name, char *key); static Package *getPackageByName(text *name, bool create, bool strict); static Variable *getVariableInternal(Package *package, text *name, Oid typid, - bool strict); + bool strict, bool type_strict); static Variable *createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional); @@ -197,7 +197,7 @@ variable_get(text *package_name, text *var_name, return 0; } - variable = getVariableInternal(package, var_name, typid, strict); + variable = getVariableInternal(package, var_name, typid, strict, true); if (variable == NULL) { @@ -455,7 +455,7 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); LastVariable = variable; } else @@ -543,7 +543,7 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); LastVariable = variable; } else @@ -592,7 +592,7 @@ variable_select(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); record = &(GetActualValue(variable).record); @@ -667,7 +667,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) } package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); if (!value_is_null) check_record_key(variable, value_type); @@ -736,7 +736,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -858,12 +858,11 @@ package_exists(PG_FUNCTION_ARGS) Datum remove_variable(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - Package *package; - Variable *variable; - bool found; - char key[NAMEDATALEN]; + text *package_name; + text *var_name; + Package *package; + Variable *variable; + TransObject *transObject; CHECK_ARGS_FOR_NULL(); @@ -871,30 +870,18 @@ remove_variable(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - getKeyFromName(var_name, key); + variable = getVariableInternal(package, var_name, 0, true, false); - variable = (Variable *) hash_search(package->varHashRegular, - key, HASH_REMOVE, &found); - if (found) + /* Add package to changes list, so we can remove it if it */ + if (!isObjectChangedInCurrentTrans(&package->transObject)) { - /* Regular variable */ - removeState(&variable->transObject, TRANS_VARIABLE, - GetActualState(variable)); + createSavepoint(&package->transObject, TRANS_PACKAGE); + addToChangesStack(&package->transObject, TRANS_PACKAGE); } - else - { - TransObject *transObject; - variable = (Variable *) hash_search(package->varHashTransact, - key, HASH_FIND, &found); - /* Variable doesn't exist in both HTAB */ - if (!found) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized variable \"%s\"", key))); - - /* Transactional variable */ - transObject = &variable->transObject; + transObject = &variable->transObject; + if (variable->is_transactional) + { if (!isObjectChangedInCurrentTrans(transObject)) { createSavepoint(transObject, TRANS_VARIABLE); @@ -902,6 +889,8 @@ remove_variable(PG_FUNCTION_ARGS) } GetActualState(variable)->is_valid = false; } + else + removeObject(&variable->transObject, TRANS_VARIABLE); resetVariablesCache(false); @@ -1449,7 +1438,8 @@ getPackageByName(text *name, bool create, bool strict) * flag 'is_transactional' of this variable is unknown. */ static Variable * -getVariableInternal(Package *package, text *name, Oid typid, bool strict) +getVariableInternal(Package *package, text *name, Oid typid, bool strict, + bool type_strict) { Variable *variable; char key[NAMEDATALEN]; @@ -1466,7 +1456,7 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) /* Check variable type */ if (found) { - if (variable->typid != typid) + if (type_strict && variable->typid != typid) { char *var_type = DatumGetCString(DirectFunctionCall1(regtypeout, ObjectIdGetDatum(variable->typid))); @@ -1574,6 +1564,12 @@ createVariableInternal(Package *package, text *name, Oid typid, &scalar->typbyval); varState->value.scalar.is_null = true; } + + if (!isObjectChangedInCurrentTrans(&package->transObject)) + { + createSavepoint(&package->transObject, TRANS_PACKAGE); + addToChangesStack(&package->transObject, TRANS_PACKAGE); + } } GetActualState(variable)->is_valid = true; @@ -1675,7 +1671,7 @@ removeObject(TransObject *object, TransObjectType type) * Delete an object from the change history of the overlying * transaction level (head of 'changesStack' at this point). */ - if (!dlist_is_empty(changesStack)) + if (changesStack && !dlist_is_empty(changesStack)) removeFromChangesStack(object, type); if (type == TRANS_PACKAGE) { diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 9838fb5..b4d7ce3 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -183,6 +183,15 @@ SELECT pgv_select('vars3', 'r1', 1); SELECT pgv_select('vars3', 'r1') LIMIT 2; SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; +-- Clean memory after unsuccessful creation of a variable +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +SELECT package FROM pgv_stats() WHERE package = 'vars4'; + +-- Remove package if it is empty +SELECT pgv_insert('vars4', 'r2', row(1, 'str1', 'str2')); +SELECT pgv_remove('vars4', 'r2'); +SELECT package FROM pgv_stats() WHERE package = 'vars4'; + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; SELECT package FROM pgv_stats() order by package; From 4dd3fd56f07d63692c42b70150f69f08ca5c83d0 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 9 Feb 2019 17:38:01 +0300 Subject: [PATCH 071/147] Remove redundant argument from getVariableInternal() --- pg_variables.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index f970435..3e811b0 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -52,7 +52,7 @@ static void getKeyFromName(text *name, char *key); static Package *getPackageByName(text *name, bool create, bool strict); static Variable *getVariableInternal(Package *package, text *name, Oid typid, - bool strict, bool type_strict); + bool strict); static Variable *createVariableInternal(Package *package, text *name, Oid typid, bool is_transactional); @@ -197,7 +197,7 @@ variable_get(text *package_name, text *var_name, return 0; } - variable = getVariableInternal(package, var_name, typid, strict, true); + variable = getVariableInternal(package, var_name, typid, strict); if (variable == NULL) { @@ -455,7 +455,7 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); LastVariable = variable; } else @@ -543,7 +543,7 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); LastVariable = variable; } else @@ -592,7 +592,7 @@ variable_select(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); record = &(GetActualValue(variable).record); @@ -667,7 +667,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) } package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); if (!value_is_null) check_record_key(variable, value_type); @@ -736,7 +736,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, true); + variable = getVariableInternal(package, var_name, RECORDOID, true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -870,9 +870,9 @@ remove_variable(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, 0, true, false); + variable = getVariableInternal(package, var_name, InvalidOid, true); - /* Add package to changes list, so we can remove it if it */ + /* Add package to changes list, so we can remove it if it is empty */ if (!isObjectChangedInCurrentTrans(&package->transObject)) { createSavepoint(&package->transObject, TRANS_PACKAGE); @@ -1438,8 +1438,7 @@ getPackageByName(text *name, bool create, bool strict) * flag 'is_transactional' of this variable is unknown. */ static Variable * -getVariableInternal(Package *package, text *name, Oid typid, bool strict, - bool type_strict) +getVariableInternal(Package *package, text *name, Oid typid, bool strict) { Variable *variable; char key[NAMEDATALEN]; @@ -1456,7 +1455,7 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict, /* Check variable type */ if (found) { - if (type_strict && variable->typid != typid) + if (typid != InvalidOid && variable->typid != typid) { char *var_type = DatumGetCString(DirectFunctionCall1(regtypeout, ObjectIdGetDatum(variable->typid))); From 2f955d4962dc6cd96636ec6fb7e2782a96e96743 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Sat, 9 Feb 2019 18:16:44 +0300 Subject: [PATCH 072/147] Fix comment --- pg_variables.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 3e811b0..deb085e 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1696,7 +1696,7 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); - /* Remove package if it is became empty */ + /* Remove package if it became empty */ if (type == TRANS_VARIABLE && isObjectChangedInCurrentTrans(&package->transObject) && isPackageEmpty(package)) @@ -1826,7 +1826,7 @@ releaseSavepoint(TransObject *object, TransObjectType type) !dlist_has_next(states, dlist_head_node(states))) { removeObject(object, type); - /* Remove package if it becomes empty */ + /* Remove package if it became empty */ if (type == TRANS_VARIABLE) { Package *pack = ((Variable *) object)->package; From 93162b5abcd868dd25b8d75a45c1b5d1fe1859d6 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 13 Feb 2019 16:25:03 +0300 Subject: [PATCH 073/147] PGPRO-2440: Use destination's context to add into the hash table --- pg_variables.c | 26 +++++++------------------- pg_variables.h | 2 ++ pg_variables_record.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index deb085e..4833523 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1582,36 +1582,24 @@ createVariableInternal(Package *package, text *name, Oid typid, static void copyValue(VarState *src, VarState *dest, Variable *destVar) { - MemoryContext oldcxt, - destctx; + MemoryContext oldcxt; - destctx = destVar->package->hctxTransact; - oldcxt = MemoryContextSwitchTo(destctx); + oldcxt = MemoryContextSwitchTo(destVar->package->hctxTransact); if (destVar->typid == RECORDOID) /* copy record value */ { - bool found; - HASH_SEQ_STATUS *rstat; - HashRecordEntry *item_prev, - *item_new; + HASH_SEQ_STATUS rstat; + HashRecordEntry *item_src; RecordVar *record_src = &src->value.record; RecordVar *record_dest = &dest->value.record; init_record(record_dest, record_src->tupdesc, destVar); /* Copy previous history entry into the new one */ - rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); - hash_seq_init(rstat, record_src->rhash); - while ((item_prev = (HashRecordEntry *) hash_seq_search(rstat)) != NULL) - { - HashRecordKey k; - - k = item_prev->key; - item_new = (HashRecordEntry *) hash_search(record_dest->rhash, &k, - HASH_ENTER, &found); - item_new->tuple = heap_copytuple(item_prev->tuple); - } + hash_seq_init(&rstat, record_src->rhash); + while ((item_src = (HashRecordEntry *) hash_seq_search(&rstat)) != NULL) + copy_record(record_dest, item_src->tuple, destVar); } else /* copy scalar value */ diff --git a/pg_variables.h b/pg_variables.h index a26eac6..96689c2 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -151,6 +151,8 @@ typedef struct ChangesStackNode extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable); extern void check_attributes(Variable *variable, TupleDesc tupdesc); extern void check_record_key(Variable *variable, Oid typid); +extern void copy_record(RecordVar *dest_record, HeapTuple src_tuple, + Variable *variable); extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); diff --git a/pg_variables_record.c b/pg_variables_record.c index 2bb40b5..341dca2 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -153,6 +153,47 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) MemoryContextSwitchTo(oldcxt); } +/* + * Copy record using src_tuple. + */ +void +copy_record(RecordVar *dest_record, HeapTuple src_tuple, Variable *variable) +{ + HeapTuple tuple; + Datum value; + bool isnull; + HashRecordKey k; + HashRecordEntry *item; + bool found; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(dest_record->hctx); + + /* Inserting a new record into dest_record */ + tuple = heap_copytuple(src_tuple); + value = fastgetattr(tuple, 1, dest_record->tupdesc, &isnull); + + k.value = value; + k.is_null = isnull; + k.hash_proc = &dest_record->hash_proc; + k.cmp_proc = &dest_record->cmp_proc; + + item = (HashRecordEntry *) hash_search(dest_record->rhash, &k, + HASH_ENTER, &found); + if (found) + { + heap_freetuple(tuple); + MemoryContextSwitchTo(oldcxt); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("there is a record in the variable \"%s\" with same " + "key", GetName(variable)))); + } + item->tuple = tuple; + + MemoryContextSwitchTo(oldcxt); +} + /* * New record structure should be the same as the first record. */ From c163b97cd04f3112e0c4342a2dba65eff723c8bf Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 13 Feb 2019 17:26:40 +0300 Subject: [PATCH 074/147] Improve tests --- expected/pg_variables.out | 49 +++++++++++++++++++++++++++++++-- expected/pg_variables_trans.out | 28 +++++++++++++++++++ sql/pg_variables.sql | 18 ++++++++++++ sql/pg_variables_trans.sql | 9 ++++++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 3ef1026..3e067ee 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -1,4 +1,19 @@ CREATE EXTENSION pg_variables; +-- Test packages - sanity checks +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_exists(NULL); -- fail +ERROR: package name can not be NULL +SELECT pgv_remove(NULL); -- fail +ERROR: package name can not be NULL +SELECT pgv_remove('vars'); -- fail +ERROR: unrecognized package "vars" +SELECT pgv_exists('vars111111111111111111111111111111111111111111111111111111111111'); -- fail +ERROR: name "vars111111111111111111111111111111111111111111111111111111111111" is too long -- Integer variables SELECT pgv_get_int('vars', 'int1'); ERROR: unrecognized package "vars" @@ -553,6 +568,34 @@ SELECT pgv_insert('vars3', 'r1', row(1, 1)); ERROR: new record structure differs from variable "r1" structure SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); ERROR: new record structure differs from variable "r1" structure +SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail +ERROR: searching for elements in multidimensional arrays is not supported +-- Test variables caching +SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_update('vars3', 'r2', row(1, 'str2', 'str1')); + pgv_update +------------ + t +(1 row) + +-- Test NULL values +SELECT pgv_insert('vars3', 'r2', NULL); -- fail +ERROR: record argument can not be NULL +SELECT pgv_update('vars3', 'r2', NULL); -- fail +ERROR: record argument can not be NULL +select pgv_delete('vars3', 'r2', NULL::int); + pgv_delete +------------ + f +(1 row) + +SELECT pgv_select('vars3', 'r1', NULL::int[]); -- fail +ERROR: array argument can not be NULL SELECT pgv_select('vars3', 'r1'); pgv_select ------------ @@ -724,7 +767,8 @@ SELECT * FROM pgv_list() order by package, name; vars2 | j1 | f vars2 | j2 | f vars3 | r1 | f -(22 rows) + vars3 | r2 | f +(23 rows) SELECT package FROM pgv_stats() order by package; package @@ -786,7 +830,8 @@ SELECT * FROM pgv_list() order by package, name; vars | tstz2 | f vars | tstzNULL | f vars3 | r1 | f -(19 rows) + vars3 | r2 | f +(20 rows) SELECT pgv_free(); pgv_free diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 397afac..c4c2495 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1880,6 +1880,34 @@ RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); ERROR: unrecognized package "vars" COMMIT; +-- Test for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + pgv_delete +------------ + t +(1 row) + SELECT pgv_free(); pgv_free ---------- diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index b4d7ce3..a262a21 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -1,5 +1,12 @@ CREATE EXTENSION pg_variables; +-- Test packages - sanity checks +SELECT pgv_free(); +SELECT pgv_exists(NULL); -- fail +SELECT pgv_remove(NULL); -- fail +SELECT pgv_remove('vars'); -- fail +SELECT pgv_exists('vars111111111111111111111111111111111111111111111111111111111111'); -- fail + -- Integer variables SELECT pgv_get_int('vars', 'int1'); SELECT pgv_get_int('vars', 'int1', false); @@ -152,6 +159,17 @@ SELECT pgv_insert('vars3', 'r1', tab) FROM tab; SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); SELECT pgv_insert('vars3', 'r1', row(1, 1)); SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); +SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail + +-- Test variables caching +SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2')); +SELECT pgv_update('vars3', 'r2', row(1, 'str2', 'str1')); + +-- Test NULL values +SELECT pgv_insert('vars3', 'r2', NULL); -- fail +SELECT pgv_update('vars3', 'r2', NULL); -- fail +select pgv_delete('vars3', 'r2', NULL::int); +SELECT pgv_select('vars3', 'r1', NULL::int[]); -- fail SELECT pgv_select('vars3', 'r1'); SELECT pgv_select('vars3', 'r1', 1); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index fc0b876..a5af1b2 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -482,4 +482,13 @@ RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); COMMIT; +-- Test for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + SELECT pgv_free(); From 660968a6be46693fff510fd8e7b2c3c81d4c5ccd Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 13 Feb 2019 17:48:08 +0300 Subject: [PATCH 075/147] Improve tests --- expected/pg_variables.out | 18 ++++++++++++++++-- pg_variables.c | 10 +--------- sql/pg_variables.sql | 5 ++++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 3e067ee..393bf06 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -577,10 +577,16 @@ SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2')); (1 row) -SELECT pgv_update('vars3', 'r2', row(1, 'str2', 'str1')); +SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar)); pgv_update ------------ - t + f +(1 row) + +select pgv_delete('vars3', 'r2', NULL::int); + pgv_delete +------------ + f (1 row) -- Test NULL values @@ -611,6 +617,8 @@ SELECT pgv_select('vars3', 'r1', 1); (1,str11) (1 row) +SELECT pgv_select('vars3', 'r1', 1::float); -- fail +ERROR: requested value type differs from variable "r1" key type SELECT pgv_select('vars3', 'r1', 0); pgv_select ------------ @@ -641,6 +649,12 @@ SELECT pgv_update('vars3', 'r1', tab) FROM tab; t (4 rows) +SELECT pgv_update('vars3', 'r1', row(4, 'str44'::varchar)); + pgv_update +------------ + f +(1 row) + SELECT pgv_select('vars3', 'r1'); pgv_select ------------ diff --git a/pg_variables.c b/pg_variables.c index 4833523..d28aa92 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -918,15 +918,7 @@ remove_package(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); package = getPackageByName(package_name, false, true); - if (package) - removePackageInternal(package); - else - { - getKeyFromName(package_name, key); - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); - } + removePackageInternal(package); resetVariablesCache(true); diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index a262a21..76d1f9e 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -163,7 +163,8 @@ SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail -- Test variables caching SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2')); -SELECT pgv_update('vars3', 'r2', row(1, 'str2', 'str1')); +SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar)); +select pgv_delete('vars3', 'r2', NULL::int); -- Test NULL values SELECT pgv_insert('vars3', 'r2', NULL); -- fail @@ -173,12 +174,14 @@ SELECT pgv_select('vars3', 'r1', NULL::int[]); -- fail SELECT pgv_select('vars3', 'r1'); SELECT pgv_select('vars3', 'r1', 1); +SELECT pgv_select('vars3', 'r1', 1::float); -- fail SELECT pgv_select('vars3', 'r1', 0); SELECT pgv_select('vars3', 'r1', NULL::int); SELECT pgv_select('vars3', 'r1', ARRAY[1, 0, NULL]); UPDATE tab SET t = 'str33' WHERE id = 1; SELECT pgv_update('vars3', 'r1', tab) FROM tab; +SELECT pgv_update('vars3', 'r1', row(4, 'str44'::varchar)); SELECT pgv_select('vars3', 'r1'); SELECT pgv_delete('vars3', 'r1', 1); From ac709b4109bff6e4f29cb2fe363fe8c6558c4369 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 13 Feb 2019 18:14:57 +0300 Subject: [PATCH 076/147] Improve tests --- regression.diffs | 24 ++++++++++++++++++++++++ regression.out | 3 +++ sql/pg_variables.sql | 2 ++ 3 files changed, 29 insertions(+) create mode 100644 regression.diffs create mode 100644 regression.out diff --git a/regression.diffs b/regression.diffs new file mode 100644 index 0000000..d45b1a0 --- /dev/null +++ b/regression.diffs @@ -0,0 +1,24 @@ +*** /home/artur/source/pg/pg_variables/expected/pg_variables.out 2019-02-13 17:46:10.430008139 +0300 +--- /home/artur/source/pg/pg_variables/results/pg_variables.out 2019-02-13 18:14:44.927663951 +0300 +*************** +*** 583,594 **** +--- 583,598 ---- + f + (1 row) + ++ SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail ++ ERROR: unrecognized package "vars4" + select pgv_delete('vars3', 'r2', NULL::int); + pgv_delete + ------------ + f + (1 row) + ++ select pgv_delete('vars4', 'r2', NULL::int); -- fail ++ ERROR: unrecognized package "vars4" + -- Test NULL values + SELECT pgv_insert('vars3', 'r2', NULL); -- fail + ERROR: record argument can not be NULL + +====================================================================== + diff --git a/regression.out b/regression.out new file mode 100644 index 0000000..d1a58f3 --- /dev/null +++ b/regression.out @@ -0,0 +1,3 @@ +test pg_variables ... FAILED +test pg_variables_any ... ok +test pg_variables_trans ... ok diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 76d1f9e..64158cd 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -164,7 +164,9 @@ SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail -- Test variables caching SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2')); SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar)); +SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail select pgv_delete('vars3', 'r2', NULL::int); +select pgv_delete('vars4', 'r2', NULL::int); -- fail -- Test NULL values SELECT pgv_insert('vars3', 'r2', NULL); -- fail From d00692b87677c6f3e2b47ab9c1a8c5529a8556d0 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 13 Feb 2019 18:22:04 +0300 Subject: [PATCH 077/147] Improve tests --- expected/pg_variables.out | 4 ++++ regression.diffs | 24 ------------------------ regression.out | 3 --- 3 files changed, 4 insertions(+), 27 deletions(-) delete mode 100644 regression.diffs delete mode 100644 regression.out diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 393bf06..725810f 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -583,12 +583,16 @@ SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar)); f (1 row) +SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail +ERROR: unrecognized package "vars4" select pgv_delete('vars3', 'r2', NULL::int); pgv_delete ------------ f (1 row) +select pgv_delete('vars4', 'r2', NULL::int); -- fail +ERROR: unrecognized package "vars4" -- Test NULL values SELECT pgv_insert('vars3', 'r2', NULL); -- fail ERROR: record argument can not be NULL diff --git a/regression.diffs b/regression.diffs deleted file mode 100644 index d45b1a0..0000000 --- a/regression.diffs +++ /dev/null @@ -1,24 +0,0 @@ -*** /home/artur/source/pg/pg_variables/expected/pg_variables.out 2019-02-13 17:46:10.430008139 +0300 ---- /home/artur/source/pg/pg_variables/results/pg_variables.out 2019-02-13 18:14:44.927663951 +0300 -*************** -*** 583,594 **** ---- 583,598 ---- - f - (1 row) - -+ SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail -+ ERROR: unrecognized package "vars4" - select pgv_delete('vars3', 'r2', NULL::int); - pgv_delete - ------------ - f - (1 row) - -+ select pgv_delete('vars4', 'r2', NULL::int); -- fail -+ ERROR: unrecognized package "vars4" - -- Test NULL values - SELECT pgv_insert('vars3', 'r2', NULL); -- fail - ERROR: record argument can not be NULL - -====================================================================== - diff --git a/regression.out b/regression.out deleted file mode 100644 index d1a58f3..0000000 --- a/regression.out +++ /dev/null @@ -1,3 +0,0 @@ -test pg_variables ... FAILED -test pg_variables_any ... ok -test pg_variables_trans ... ok From dfe6ac2f0f77898d1ef6595e9e5549cd3256afec Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Thu, 14 Feb 2019 13:26:04 +0300 Subject: [PATCH 078/147] Allow to store record type as a scalar variable --- expected/pg_variables.out | 30 +++++++++++-- pg_variables.c | 89 +++++++++++++++++++++++---------------- pg_variables.h | 5 +++ sql/pg_variables.sql | 10 +++++ 4 files changed, 95 insertions(+), 39 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 725810f..592c401 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -759,6 +759,27 @@ SELECT package FROM pgv_stats() WHERE package = 'vars4'; --------- (0 rows) +-- Record variables as scalar +SELECT pgv_set('vars5', 'r1', row(1, 'str11')); + pgv_set +--------- + +(1 row) + +SELECT pgv_get('vars5', 'r1', NULL::record); + pgv_get +----------- + (1,str11) +(1 row) + +SELECT pgv_set('vars5', 'r1', row(1, 'str11'), true); -- fail +ERROR: variable "r1" already created as NOT TRANSACTIONAL +SELECT pgv_insert('vars5', 'r1', row(1, 'str11')); -- fail +ERROR: "r1" isn't a record variable +SELECT pgv_select('vars5', 'r1'); -- fail +ERROR: "r1" isn't a record variable +SELECT pgv_get('vars3', 'r1', NULL::record); -- fail +ERROR: "r1" isn't a scalar variable -- Manipulate variables SELECT * FROM pgv_list() order by package, name; package | name | is_transactional @@ -786,7 +807,8 @@ SELECT * FROM pgv_list() order by package, name; vars2 | j2 | f vars3 | r1 | f vars3 | r2 | f -(23 rows) + vars5 | r1 | f +(24 rows) SELECT package FROM pgv_stats() order by package; package @@ -794,7 +816,8 @@ SELECT package FROM pgv_stats() order by package; vars vars2 vars3 -(3 rows) + vars5 +(4 rows) SELECT pgv_remove('vars', 'int3'); ERROR: unrecognized variable "int3" @@ -849,7 +872,8 @@ SELECT * FROM pgv_list() order by package, name; vars | tstzNULL | f vars3 | r1 | f vars3 | r2 | f -(20 rows) + vars5 | r1 | f +(21 rows) SELECT pgv_free(); pgv_free diff --git a/pg_variables.c b/pg_variables.c index d28aa92..d561817 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -50,12 +50,10 @@ static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); static Package *getPackageByName(text *name, bool create, bool strict); -static Variable *getVariableInternal(Package *package, - text *name, Oid typid, - bool strict); -static Variable *createVariableInternal(Package *package, - text *name, Oid typid, - bool is_transactional); +static Variable *getVariableInternal(Package *package, text *name, + Oid typid, bool is_record, bool strict); +static Variable *createVariableInternal(Package *package, text *name, Oid typid, + bool is_record, bool is_transactional); static void removePackageInternal(Package *package); static void resetVariablesCache(bool with_package); @@ -65,7 +63,7 @@ static void releaseSavepoint(TransObject *object, TransObjectType type); static void rollbackSavepoint(TransObject *object, TransObjectType type); static void copyValue(VarState *src, VarState *dest, Variable *destVar); -static void freeValue(VarState *varstate, Oid typid); +static void freeValue(VarState *varstate, bool is_record); static void removeState(TransObject *object, TransObjectType type, TransState *stateToDelete); static bool isObjectChangedInCurrentTrans(TransObject *object); @@ -160,7 +158,7 @@ variable_set(text *package_name, text *var_name, ScalarVar *scalar; package = getPackageByName(package_name, true, false); - variable = createVariableInternal(package, var_name, typid, + variable = createVariableInternal(package, var_name, typid, false, is_transactional); scalar = &(GetActualValue(variable).scalar); @@ -197,7 +195,7 @@ variable_get(text *package_name, text *var_name, return 0; } - variable = getVariableInternal(package, var_name, typid, strict); + variable = getVariableInternal(package, var_name, typid, false, strict); if (variable == NULL) { @@ -343,7 +341,7 @@ variable_insert(PG_FUNCTION_ARGS) VARSIZE_ANY_EXHDR(var_name)) != 0) { variable = createVariableInternal(package, var_name, RECORDOID, - is_transactional); + true, is_transactional); LastVariable = variable; } else @@ -455,7 +453,8 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); LastVariable = variable; } else @@ -543,7 +542,8 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(var_name), GetName(LastVariable), VARSIZE_ANY_EXHDR(var_name)) != 0) { - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); LastVariable = variable; } else @@ -592,7 +592,8 @@ variable_select(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); record = &(GetActualValue(variable).record); @@ -667,7 +668,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) } package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, true); if (!value_is_null) check_record_key(variable, value_type); @@ -736,7 +737,8 @@ variable_select_by_values(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, RECORDOID, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); check_record_key(variable, ARR_ELEMTYPE(values)); @@ -870,7 +872,7 @@ remove_variable(PG_FUNCTION_ARGS) var_name = PG_GETARG_TEXT_PP(1); package = getPackageByName(package_name, false, true); - variable = getVariableInternal(package, var_name, InvalidOid, true); + variable = getVariableInternal(package, var_name, InvalidOid, false, true); /* Add package to changes list, so we can remove it if it is empty */ if (!isObjectChangedInCurrentTrans(&package->transObject)) @@ -908,7 +910,6 @@ remove_package(PG_FUNCTION_ARGS) { Package *package; text *package_name; - char key[NAMEDATALEN]; if (PG_ARGISNULL(0)) ereport(ERROR, @@ -1430,7 +1431,8 @@ getPackageByName(text *name, bool create, bool strict) * flag 'is_transactional' of this variable is unknown. */ static Variable * -getVariableInternal(Package *package, text *name, Oid typid, bool strict) +getVariableInternal(Package *package, text *name, Oid typid, bool is_record, + bool strict) { Variable *variable; char key[NAMEDATALEN]; @@ -1447,15 +1449,25 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) /* Check variable type */ if (found) { - if (typid != InvalidOid && variable->typid != typid) + if (typid != InvalidOid) { - char *var_type = DatumGetCString(DirectFunctionCall1(regtypeout, - ObjectIdGetDatum(variable->typid))); + 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))); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" requires \"%s\" value", + key, var_type))); + } + + if (variable->is_record != is_record) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" isn't a %s variable", + key, is_record ? "record" : "scalar"))); } if (!GetActualState(variable)->is_valid && strict) ereport(ERROR, @@ -1475,11 +1487,11 @@ getVariableInternal(Package *package, text *name, Oid typid, bool strict) /* * 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. + * Function is useful to set new value to variable and flag 'is_transactional' + * is known. */ static Variable * -createVariableInternal(Package *package, text *name, Oid typid, +createVariableInternal(Package *package, text *name, Oid typid, bool is_record, bool is_transactional) { Variable *variable; @@ -1521,6 +1533,12 @@ createVariableInternal(Package *package, text *name, Oid typid, key, var_type))); } + if (variable->is_record != is_record) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" isn't a %s variable", + key, is_record ? "record" : "scalar"))); + /* * Savepoint must be created when variable changed in current * transaction. For each transaction level there should be a @@ -1540,6 +1558,7 @@ createVariableInternal(Package *package, text *name, Oid typid, /* Variable entry was created, so initialize new variable. */ variable->typid = typid; variable->package = package; + variable->is_record = is_record; variable->is_transactional = is_transactional; dlist_init(GetStateStorage(variable)); @@ -1547,7 +1566,7 @@ createVariableInternal(Package *package, text *name, Oid typid, sizeof(VarState)); dlist_push_head(GetStateStorage(variable), &varState->state.node); - if (typid != RECORDOID) + if (!variable->is_record) { ScalarVar *scalar = &(varState->value.scalar); @@ -1578,7 +1597,7 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) oldcxt = MemoryContextSwitchTo(destVar->package->hctxTransact); - if (destVar->typid == RECORDOID) + if (destVar->is_record) /* copy record value */ { HASH_SEQ_STATUS rstat; @@ -1610,19 +1629,17 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) } static void -freeValue(VarState *varstate, Oid typid) +freeValue(VarState *varstate, bool is_record) { - if (typid == RECORDOID && varstate->value.record.hctx) + if (is_record && varstate->value.record.hctx) { /* All records will be freed */ MemoryContextDelete(varstate->value.record.hctx); } - else if (varstate->value.scalar.typbyval == false && + else if (!is_record && varstate->value.scalar.typbyval == false && varstate->value.scalar.is_null == false && varstate->value.scalar.value) - { pfree(DatumGetPointer(varstate->value.scalar.value)); - } } static void @@ -1632,7 +1649,7 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete { Variable *var = (Variable *) object; - freeValue((VarState *) stateToDelete, var->typid); + freeValue((VarState *) stateToDelete, var->is_record); } dlist_delete(&stateToDelete->node); pfree(stateToDelete); diff --git a/pg_variables.h b/pg_variables.h index 96689c2..ce61df1 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -101,6 +101,11 @@ typedef struct Variable TransObject transObject; Package *package; Oid typid; + /* + * We need an additional flag to determine variable's type since we can + * store record type DATUM within scalar variable + */ + bool is_record; /* * The flag determines the further behavior of the variable. Can be diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 64158cd..9e32468 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -215,6 +215,16 @@ SELECT pgv_insert('vars4', 'r2', row(1, 'str1', 'str2')); SELECT pgv_remove('vars4', 'r2'); SELECT package FROM pgv_stats() WHERE package = 'vars4'; +-- Record variables as scalar +SELECT pgv_set('vars5', 'r1', row(1, 'str11')); +SELECT pgv_get('vars5', 'r1', NULL::record); +SELECT pgv_set('vars5', 'r1', row(1, 'str11'), true); -- fail + +SELECT pgv_insert('vars5', 'r1', row(1, 'str11')); -- fail +SELECT pgv_select('vars5', 'r1'); -- fail + +SELECT pgv_get('vars3', 'r1', NULL::record); -- fail + -- Manipulate variables SELECT * FROM pgv_list() order by package, name; SELECT package FROM pgv_stats() order by package; From 1a7be6a3273f9e0c91b29876ae28ce62b7631dca Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Thu, 21 Feb 2019 17:21:05 +0300 Subject: [PATCH 079/147] Don't create empty HTAB in package, if it is not necessary --- expected/pg_variables_trans.out | 3 +- pg_variables.c | 210 +++++++++++++++++--------------- 2 files changed, 113 insertions(+), 100 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index c4c2495..a709f6b 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1794,7 +1794,8 @@ SELECT package FROM pgv_stats() ORDER BY package; package --------- vars -(1 row) + vars2 +(2 rows) SELECT * FROM pgv_list() ORDER BY package, name; package | name | is_transactional diff --git a/pg_variables.c b/pg_variables.c index d561817..f7e34e7 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -49,7 +49,8 @@ extern void _PG_fini(void); static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); -static Package *getPackageByName(text *name, bool create, bool strict); +static Package *getPackage(text *name, bool strict); +static Package *createPackage(text *name, bool is_trans); static Variable *getVariableInternal(Package *package, text *name, Oid typid, bool is_record, bool strict); static Variable *createVariableInternal(Package *package, text *name, Oid typid, @@ -157,7 +158,7 @@ variable_set(text *package_name, text *var_name, Variable *variable; ScalarVar *scalar; - package = getPackageByName(package_name, true, false); + package = createPackage(package_name, is_transactional); variable = createVariableInternal(package, var_name, typid, false, is_transactional); @@ -188,7 +189,7 @@ variable_get(text *package_name, text *var_name, Variable *variable; ScalarVar *scalar; - package = getPackageByName(package_name, false, strict); + package = getPackage(package_name, strict); if (package == NULL) { *is_null = true; @@ -325,9 +326,10 @@ variable_insert(PG_FUNCTION_ARGS) if (LastPackage == NULL || VARSIZE_ANY_EXHDR(package_name) != strlen(GetName(LastPackage)) || strncmp(VARDATA_ANY(package_name), GetName(LastPackage), - VARSIZE_ANY_EXHDR(package_name)) != 0) + VARSIZE_ANY_EXHDR(package_name)) != 0 || + !pack_htab(LastPackage, is_transactional)) { - package = getPackageByName(package_name, true, false); + package = createPackage(package_name, is_transactional); LastPackage = package; LastVariable = NULL; } @@ -440,7 +442,7 @@ variable_update(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(package_name), GetName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); LastPackage = package; LastVariable = NULL; } @@ -529,7 +531,7 @@ variable_delete(PG_FUNCTION_ARGS) strncmp(VARDATA_ANY(package_name), GetName(LastPackage), VARSIZE_ANY_EXHDR(package_name)) != 0) { - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); LastPackage = package; LastVariable = NULL; } @@ -591,7 +593,7 @@ variable_select(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); variable = getVariableInternal(package, var_name, RECORDOID, true, true); @@ -667,7 +669,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) value = 0; } - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); variable = getVariableInternal(package, var_name, RECORDOID, true, true); if (!value_is_null) @@ -736,7 +738,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); variable = getVariableInternal(package, var_name, RECORDOID, true, true); @@ -802,14 +804,14 @@ variable_exists(PG_FUNCTION_ARGS) Package *package; Variable *variable; char key[NAMEDATALEN]; - bool found; + bool found = false; CHECK_ARGS_FOR_NULL(); package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); - package = getPackageByName(package_name, false, false); + package = getPackage(package_name, false); if (package == NULL) { PG_FREE_IF_COPY(package_name, 0); @@ -820,9 +822,10 @@ variable_exists(PG_FUNCTION_ARGS) getKeyFromName(var_name, key); - variable = (Variable *) hash_search(package->varHashRegular, - key, HASH_FIND, &found); - if (!found) + if (package->varHashRegular) + variable = (Variable *) hash_search(package->varHashRegular, + key, HASH_FIND, &found); + if (!found && package->varHashTransact) variable = (Variable *) hash_search(package->varHashTransact, key, HASH_FIND, &found); @@ -848,7 +851,7 @@ package_exists(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); - res = getPackageByName(package_name, false, false) != NULL; + res = getPackage(package_name, false) != NULL; PG_FREE_IF_COPY(package_name, 0); PG_RETURN_BOOL(res); @@ -871,7 +874,7 @@ remove_variable(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); var_name = PG_GETARG_TEXT_PP(1); - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); variable = getVariableInternal(package, var_name, InvalidOid, false, true); /* Add package to changes list, so we can remove it if it is empty */ @@ -918,7 +921,7 @@ remove_package(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); - package = getPackageByName(package_name, false, true); + package = getPackage(package_name, true); removePackageInternal(package); resetVariablesCache(true); @@ -953,7 +956,10 @@ removePackageInternal(Package *package) static bool isPackageEmpty(Package *package) { - int var_num = hash_get_num_entries(package->varHashTransact); + int var_num = 0; + + if (package->varHashTransact) + var_num += hash_get_num_entries(package->varHashTransact); if (package->varHashRegular) var_num += hash_get_num_entries(package->varHashRegular); @@ -1065,8 +1071,10 @@ get_packages_and_variables(PG_FUNCTION_ARGS) /* Get variables list for package */ for (i = 0; i < 2; i++) { - hash_seq_init(&vstat, i ? package->varHashTransact : - package->varHashRegular); + HTAB *htab = pack_htab(package, i); + if (!htab) + continue; + hash_seq_init(&vstat, htab); while ((variable = (Variable *) hash_seq_search(&vstat)) != NULL) { @@ -1228,9 +1236,10 @@ get_packages_stats(PG_FUNCTION_ARGS) /* Fill data */ values[0] = PointerGetDatum(cstring_to_text(GetName(package))); - if (GetActualState(package)->is_valid) + if (package->hctxRegular) getMemoryTotalSpace(package->hctxRegular, 0, ®ularSpace); - getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); + if (package->hctxTransact) + getMemoryTotalSpace(package->hctxTransact, 0, &transactSpace); totalSpace = regularSpace + transactSpace; values[1] = Int64GetDatum(totalSpace); @@ -1300,6 +1309,7 @@ makePackHTAB(Package *package, bool is_trans) HTAB **htab; MemoryContext *context; + // maybe we should use macro pack_hctx? htab = is_trans ? &package->varHashTransact : &package->varHashRegular; context = is_trans ? &package->hctxTransact : &package->hctxRegular; @@ -1317,40 +1327,48 @@ makePackHTAB(Package *package, bool is_trans) } static Package * -getPackageByName(text *name, bool create, bool strict) +getPackage(text *name, bool strict) { Package *package; - PackState *packState; char key[NAMEDATALEN]; bool found; getKeyFromName(name, key); - if (create) - ensurePackagesHashExists(); - else + /* Find a package entry */ + if (packagesHash) { - if (!packagesHash) - { - if (strict) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + package = (Package *) hash_search(packagesHash, key, HASH_FIND, &found); - return NULL; - } + if (found && GetActualState(package)->is_valid) + return package; } + /* Package not found or it's current state is "invalid" */ + if (strict) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized package \"%s\"", key))); + + return NULL; +} + +static Package * +createPackage(text *name, bool is_trans) +{ + Package *package; + char key[NAMEDATALEN]; + bool found; + + getKeyFromName(name, key); + ensurePackagesHashExists(); /* Find or create a package entry */ - package = (Package *) hash_search(packagesHash, key, - create ? HASH_ENTER : HASH_FIND, - &found); + package = (Package *) hash_search(packagesHash, key, HASH_ENTER, &found); if (found) { - if (GetActualState(package)->is_valid) - return package; - else if (create) + if (!GetActualState(package)->is_valid) + /* Make package valid again */ { HASH_SEQ_STATUS vstat; Variable *variable; @@ -1358,71 +1376,57 @@ getPackageByName(text *name, bool create, bool strict) /* Make new history entry of package */ if (!isObjectChangedInCurrentTrans(transObj)) - { createSavepoint(transObj, TRANS_PACKAGE); - addToChangesStack(transObj, TRANS_PACKAGE); - } GetActualState(package)->is_valid = true; - /* XXX Check is this necessary */ - - /* Restore previously removed HTAB for regular variables */ - makePackHTAB(package, false); - /* Mark all transactional variables in package as removed */ - hash_seq_init(&vstat, package->varHashTransact); - while ((variable = - (Variable *) hash_seq_search(&vstat)) != NULL) + if (package->varHashTransact) { - transObj = &variable->transObject; - - if (!isObjectChangedInCurrentTrans(transObj)) + hash_seq_init(&vstat, package->varHashTransact); + while ((variable = + (Variable *) hash_seq_search(&vstat)) != NULL) { - createSavepoint(transObj, TRANS_VARIABLE); - addToChangesStack(transObj, TRANS_VARIABLE); + transObj = &variable->transObject; + + if (!isObjectChangedInCurrentTrans(transObj)) + { + createSavepoint(transObj, TRANS_VARIABLE); + addToChangesStack(transObj, TRANS_VARIABLE); + } + GetActualState(variable)->is_valid = false; } - GetActualState(variable)->is_valid = false; } - - return package; } - else if (strict) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); - else - return NULL; - } - else if (!create) - { - if (strict) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); - else - return package; } else { - /* - * Package entry was created, so we need create hash table for - * variables. - */ - makePackHTAB(package, false); - makePackHTAB(package, true); + PackState *packState; + //memset(package, 0, sizeof(Package)); + package->varHashRegular = NULL; + package->varHashTransact = NULL; + package->hctxRegular = NULL; + package->hctxTransact = NULL; /* Initialize history */ dlist_init(GetStateStorage(package)); packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); dlist_push_head(GetStateStorage(package), &(packState->state.node)); packState->state.is_valid = true; + } - /* Add to changes list */ + /* Create corresponding HTAB if not exists */ + if (!pack_htab(package, is_trans)) + makePackHTAB(package, is_trans); + /* Add to changes list */ + //addToChangesStack(&package->transObject, TRANS_PACKAGE); + if (!isObjectChangedInCurrentTrans(&package->transObject)) + { + createSavepoint(&package->transObject, TRANS_PACKAGE); addToChangesStack(&package->transObject, TRANS_PACKAGE); - - return package; } + + return package; } /* @@ -1434,15 +1438,16 @@ static Variable * getVariableInternal(Package *package, text *name, Oid typid, bool is_record, bool strict) { - Variable *variable; + Variable *variable = NULL; char key[NAMEDATALEN]; - bool found; + bool found = false; getKeyFromName(name, key); - variable = (Variable *) hash_search(package->varHashRegular, - key, HASH_FIND, &found); - if (!found) + if (package->varHashRegular) + variable = (Variable *) hash_search(package->varHashRegular, + key, HASH_FIND, &found); + if (!found && package->varHashTransact) variable = (Variable *) hash_search(package->varHashTransact, key, HASH_FIND, &found); @@ -1496,6 +1501,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, { Variable *variable; TransObject *transObject; + HTAB *htab; char key[NAMEDATALEN]; bool found; @@ -1505,14 +1511,16 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, * Reverse check: for non-transactional variable search in regular table * and vice versa. */ - hash_search(is_transactional ? - package->varHashRegular : package->varHashTransact, - key, HASH_FIND, &found); - if (found) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("variable \"%s\" already created as %sTRANSACTIONAL", - key, is_transactional ? "NOT " : ""))); + htab = pack_htab(package, !is_transactional); + if (htab) + { + hash_search(htab, key, HASH_FIND, &found); + if (found) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + key, is_transactional ? "NOT " : ""))); + } variable = (Variable *) hash_search(pack_htab(package, is_transactional), key, HASH_ENTER, &found); @@ -1674,7 +1682,11 @@ removeObject(TransObject *object, TransObjectType type) package = (Package *) object; /* Regular variables had already removed */ - MemoryContextDelete(package->hctxTransact); + //Here we should think, when regular HTAB should be removed + if (package->hctxRegular) + MemoryContextDelete(package->hctxRegular); + if (package->hctxTransact) + MemoryContextDelete(package->hctxTransact); hash = packagesHash; } else From b53645f3c1777fedf2b6bc8600bc5567a924ba9d Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 25 Feb 2019 12:03:28 +0300 Subject: [PATCH 080/147] Optimize objects removal in subtransaction commit --- expected/pg_variables_trans.out | 2 +- pg_variables.c | 124 +++++++------------------------- 2 files changed, 28 insertions(+), 98 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index a709f6b..a13e338 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1879,7 +1879,7 @@ SELECT pgv_remove('vars', 'any1'); RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" +ERROR: unrecognized variable "any1" COMMIT; -- Test for PGPRO-2440 SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); diff --git a/pg_variables.c b/pg_variables.c index f7e34e7..e309922 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -72,7 +72,6 @@ static bool isObjectChangedInUpperTrans(TransObject *object); static void addToChangesStack(TransObject *object, TransObjectType type); static void pushChangesStack(void); -static void removeFromChangesStack(TransObject *object, TransObjectType type); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); @@ -1309,7 +1308,6 @@ makePackHTAB(Package *package, bool is_trans) HTAB **htab; MemoryContext *context; - // maybe we should use macro pack_hctx? htab = is_trans ? &package->varHashTransact : &package->varHashRegular; context = is_trans ? &package->hctxTransact : &package->hctxRegular; @@ -1403,7 +1401,6 @@ createPackage(text *name, bool is_trans) { PackState *packState; - //memset(package, 0, sizeof(Package)); package->varHashRegular = NULL; package->varHashTransact = NULL; package->hctxRegular = NULL; @@ -1419,7 +1416,6 @@ createPackage(text *name, bool is_trans) if (!pack_htab(package, is_trans)) makePackHTAB(package, is_trans); /* Add to changes list */ - //addToChangesStack(&package->transObject, TRANS_PACKAGE); if (!isObjectChangedInCurrentTrans(&package->transObject)) { createSavepoint(&package->transObject, TRANS_PACKAGE); @@ -1671,12 +1667,6 @@ removeObject(TransObject *object, TransObjectType type) HTAB *hash; Package *package = NULL; - /* - * Delete an object from the change history of the overlying - * transaction level (head of 'changesStack' at this point). - */ - if (changesStack && !dlist_is_empty(changesStack)) - removeFromChangesStack(object, type); if (type == TRANS_PACKAGE) { package = (Package *) object; @@ -1789,17 +1779,38 @@ rollbackSavepoint(TransObject *object, TransObjectType type) static void releaseSavepoint(TransObject *object, TransObjectType type) { - dlist_head *states; + dlist_head *states = &object->states; Assert(GetActualState(object)->level == GetCurrentTransactionNestLevel()); - /* Mark object as changed in parent transaction... */ - if (!dlist_is_empty(changesStack) /* ...if there is an upper level... */ - /* ...and object is not yet in list of that level changes. */ - && !isObjectChangedInUpperTrans(object)) + /* + * If the object is not valid and does not exist at a higher level + * (or if we complete the transaction) - remove object. + */ + if (!GetActualState(object)->is_valid && + (!dlist_has_next(states, dlist_head_node(states)) || + dlist_is_empty(changesStack)) + ) + { + removeObject(object, type); + return; + } + + /* If object has been changed in upper level - + * replace state of that level with the current one. */ + if (isObjectChangedInUpperTrans(object)) + { + TransState *stateToDelete; + dlist_node *nodeToDelete; + nodeToDelete = dlist_next_node(states, dlist_head_node(states)); + stateToDelete = dlist_container(TransState, node, nodeToDelete); + removeState(object, type, stateToDelete); + } + /* If the object does not yet have a record in previous level changesStack, + * create it. */ + else if (!dlist_is_empty(changesStack)) { ChangedObject *co_new; ChangesStackNode *csn; - /* * Impossible to push in upper list existing node * because it was created in another context @@ -1809,43 +1820,7 @@ releaseSavepoint(TransObject *object, TransObjectType type) dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : csn->changedVarsList, &co_new->node); - } - else - { - states = &object->states; - - /* If object existed in parent transaction... */ - if (dlist_has_next(states, dlist_head_node(states))) - { - TransState *stateToDelete; - dlist_node *nodeToDelete; - - /* ...remove its previous state */ - nodeToDelete = dlist_next_node(states, dlist_head_node(states)); - stateToDelete = dlist_container(TransState, node, nodeToDelete); - removeState(object, type, stateToDelete); - } - - /* - * Object has no more previous states and can be completely removed if - * necessary - */ - if (!GetActualState(object)->is_valid && - !dlist_has_next(states, dlist_head_node(states))) - { - removeObject(object, type); - /* Remove package if it became empty */ - if (type == TRANS_VARIABLE) - { - Package *pack = ((Variable *) object)->package; - if (isPackageEmpty(pack)) - (GetActualState(&pack->transObject))->is_valid = false; - } - return; - } - } - /* Change subxact level due to release */ GetActualState(object)->level--; } @@ -1980,51 +1955,6 @@ addToChangesStack(TransObject *object, TransObjectType type) } } -/* - * Remove from the changes list a deleted package - */ -static void -removeFromChangesStack(TransObject *object, TransObjectType type) -{ - dlist_mutable_iter var_miter, - pack_miter; - dlist_head *changesList; - ChangesStackNode *csn = get_actual_changes_list(); - - /* - * If we remove package, we should remove corresponding variables - * from changedVarsList first. - */ - if (type == TRANS_PACKAGE) - { - changesList = csn->changedVarsList; - dlist_foreach_modify(var_miter, changesList) - { - ChangedObject *co_cur = dlist_container(ChangedObject, node, - var_miter.cur); - Variable *var = (Variable *) co_cur->object; - - if (var->package == (Package *)object) - dlist_delete(&co_cur->node); - } - } - /* Now remove object itself from changes list */ - changesList = (type == TRANS_PACKAGE ? csn->changedPacksList : - csn->changedVarsList); - dlist_foreach_modify(pack_miter, changesList) - { - ChangedObject *co_cur = dlist_container(ChangedObject, node, - pack_miter.cur); - TransObject *obj = co_cur->object; - - if (obj == object) - { - dlist_delete(&co_cur->node); - break; - } - } -} - /* * Possible actions on variables. * Savepoints are created in setters so we don't need a CREATE_SAVEPOINT action. From 21df5c367aa1f9bd1e381b6967de00f38965412a Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 25 Feb 2019 15:03:29 +0300 Subject: [PATCH 081/147] Optimize objects removal in subtransaction rollback --- expected/pg_variables_trans.out | 28 +++++++++++++++++++++++++++- pg_variables.c | 16 +++++----------- sql/pg_variables_trans.sql | 11 ++++++++++- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index a13e338..b352c33 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1881,7 +1881,7 @@ RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); ERROR: unrecognized variable "any1" COMMIT; --- Test for PGPRO-2440 +-- Tests for PGPRO-2440 SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); pgv_insert ------------ @@ -1909,6 +1909,32 @@ SELECT pgv_delete('vars3', 'r3', 3); t (1 row) +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 't2', ''::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ERROR; +ERROR: syntax error at or near "ERROR" +LINE 1: ERROR; + ^ +COMMIT; SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index e309922..03dd7b4 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1672,7 +1672,6 @@ removeObject(TransObject *object, TransObjectType type) package = (Package *) object; /* Regular variables had already removed */ - //Here we should think, when regular HTAB should be removed if (package->hctxRegular) MemoryContextDelete(package->hctxRegular); if (package->hctxTransact) @@ -1696,10 +1695,11 @@ removeObject(TransObject *object, TransObjectType type) hash_search(hash, object->name, HASH_REMOVE, &found); /* Remove package if it became empty */ - if (type == TRANS_VARIABLE && - isObjectChangedInCurrentTrans(&package->transObject) && - isPackageEmpty(package)) + if (type == TRANS_VARIABLE && isPackageEmpty(package)) + { + Assert(isObjectChangedInCurrentTrans(&package->transObject)); GetActualState(&package->transObject)->is_valid = false; + } resetVariablesCache(true); } @@ -1740,14 +1740,8 @@ rollbackSavepoint(TransObject *object, TransObjectType type) state = GetActualState(object); if (type == TRANS_PACKAGE) { - if (!state->is_valid) + if (!state->is_valid && !isPackageEmpty((Package *)object)) { - if (isPackageEmpty((Package *)object)) - { - removeObject(object, TRANS_PACKAGE); - return; - } - if (dlist_has_next(&object->states, &state->node)) { dlist_pop_head_node(&object->states); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index a5af1b2..ed4a69f 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -482,7 +482,7 @@ RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); COMMIT; --- Test for PGPRO-2440 +-- Tests for PGPRO-2440 SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); BEGIN; SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); @@ -491,4 +491,13 @@ SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); COMMIT; SELECT pgv_delete('vars3', 'r3', 3); +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); +SELECT pgv_set('vars2', 't2', ''::text, true); +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); +ERROR; +COMMIT; + SELECT pgv_free(); From ab951025db7a89f4b080862ffba71436db1e40f0 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 4 Mar 2019 11:03:20 +0300 Subject: [PATCH 082/147] Fix gcc warning in variable_exists() --- pg_variables.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 03dd7b4..b89d213 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -801,7 +801,7 @@ variable_exists(PG_FUNCTION_ARGS) text *package_name; text *var_name; Package *package; - Variable *variable; + Variable *variable = NULL; char key[NAMEDATALEN]; bool found = false; @@ -831,7 +831,7 @@ variable_exists(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - PG_RETURN_BOOL(found ? GetActualState(variable)->is_valid : found); + PG_RETURN_BOOL(variable ? GetActualState(variable)->is_valid : false); } /* From a682cc3e161bf6ecaaa819e16fc4e054d0629294 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 26 Feb 2019 20:50:41 +0300 Subject: [PATCH 083/147] Count valid variables to display package does not exists if there is no valid variables --- expected/pg_variables_trans.out | 2 +- pg_variables.c | 134 ++++++++++++++++++++++---------- pg_variables.h | 6 +- 3 files changed, 100 insertions(+), 42 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index b352c33..41aeab0 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1879,7 +1879,7 @@ SELECT pgv_remove('vars', 'any1'); RELEASE comm; SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized variable "any1" +ERROR: unrecognized package "vars" COMMIT; -- Tests for PGPRO-2440 SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); diff --git a/pg_variables.c b/pg_variables.c index b89d213..3b93bdb 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -71,8 +71,14 @@ static bool isObjectChangedInCurrentTrans(TransObject *object); static bool isObjectChangedInUpperTrans(TransObject *object); static void addToChangesStack(TransObject *object, TransObjectType type); +static void addToChangesStackUpperLevel(TransObject *object, + TransObjectType type); static void pushChangesStack(void); +static int numOfRegVars(Package *package); +/* Debug function */ +static int _numOfTransVars(Package *package); + /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, @@ -892,10 +898,13 @@ remove_variable(PG_FUNCTION_ARGS) addToChangesStack(transObject, TRANS_VARIABLE); } GetActualState(variable)->is_valid = false; + numOfTransVars(package)--; } else removeObject(&variable->transObject, TRANS_VARIABLE); + Assert (numOfTransVars(package) == _numOfTransVars(package)); + resetVariablesCache(false); PG_FREE_IF_COPY(package_name, 0); @@ -950,6 +959,7 @@ removePackageInternal(Package *package) addToChangesStack(transObject, TRANS_PACKAGE); } GetActualState(package)->is_valid = false; + numOfTransVars(package) = 0; } static bool @@ -1338,7 +1348,8 @@ getPackage(text *name, bool strict) { package = (Package *) hash_search(packagesHash, key, HASH_FIND, &found); - if (found && GetActualState(package)->is_valid) + if (found && GetActualState(package)->is_valid && + numOfTransVars(package) + numOfRegVars(package)) return package; } /* Package not found or it's current state is "invalid" */ @@ -1410,17 +1421,15 @@ createPackage(text *name, bool is_trans) packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); dlist_push_head(GetStateStorage(package), &(packState->state.node)); packState->state.is_valid = true; + packState->trans_var_num = 0; + /* Add to changes list */ + if (!isObjectChangedInCurrentTrans(&package->transObject)) + addToChangesStack(&package->transObject, TRANS_PACKAGE); } /* Create corresponding HTAB if not exists */ if (!pack_htab(package, is_trans)) makePackHTAB(package, is_trans); - /* Add to changes list */ - if (!isObjectChangedInCurrentTrans(&package->transObject)) - { - createSavepoint(&package->transObject, TRANS_PACKAGE); - addToChangesStack(&package->transObject, TRANS_PACKAGE); - } return package; } @@ -1586,7 +1595,13 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, } } + if (is_transactional && + (!found || !GetActualState(variable)->is_valid)) + numOfTransVars(package)++; GetActualState(variable)->is_valid = true; + + Assert (numOfTransVars(package) == _numOfTransVars(package)); + /* If it is necessary, put variable to changedVars */ if (is_transactional) addToChangesStack(transObject, TRANS_VARIABLE); @@ -1715,8 +1730,11 @@ createSavepoint(TransObject *object, TransObjectType type) prevState = GetActualState(object); if (type == TRANS_PACKAGE) + { newState = (TransState *) MemoryContextAllocZero(ModuleContext, sizeof(PackState)); + ((PackState *)newState)->trans_var_num = ((PackState *)prevState)->trans_var_num; + } else { Variable *var = (Variable *) object; @@ -1729,6 +1747,15 @@ createSavepoint(TransObject *object, TransObjectType type) newState->is_valid = prevState->is_valid; } +static int +numOfRegVars(Package *package) +{ + if (package->varHashRegular) + return hash_get_num_entries(package->varHashRegular); + else + return 0; +} + /* * Rollback object to its previous state */ @@ -1738,32 +1765,27 @@ rollbackSavepoint(TransObject *object, TransObjectType type) TransState *state; state = GetActualState(object); - if (type == TRANS_PACKAGE) + removeState(object, type, state); + + if (dlist_is_empty(&object->states)) { - if (!state->is_valid && !isPackageEmpty((Package *)object)) + if (type == TRANS_PACKAGE && numOfRegVars((Package *)object)) { - if (dlist_has_next(&object->states, &state->node)) + PackState *packState; + + packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); + dlist_push_head(&object->states, &(packState->state.node)); + packState->state.is_valid = true; + packState->state.level = GetCurrentTransactionNestLevel() - 1; + packState->trans_var_num = 0; + + if (!dlist_is_empty(changesStack)) { - dlist_pop_head_node(&object->states); - pfree(state); + addToChangesStackUpperLevel(object, type); } - else - state->is_valid = true; - /* Restore regular vars HTAB */ - makePackHTAB((Package *) object, false); } else - /* Pass current state to parent level */ - releaseSavepoint(object, TRANS_PACKAGE); - } - else - { - /* Remove current state */ - removeState(object, TRANS_VARIABLE, state); - - /* Remove variable if it was created in rolled back transaction */ - if (dlist_is_empty(&object->states)) - removeObject(object, TRANS_VARIABLE); + removeObject(object, type); } } @@ -1802,21 +1824,31 @@ releaseSavepoint(TransObject *object, TransObjectType type) /* If the object does not yet have a record in previous level changesStack, * create it. */ else if (!dlist_is_empty(changesStack)) - { - ChangedObject *co_new; - ChangesStackNode *csn; - /* - * Impossible to push in upper list existing node - * because it was created in another context - */ - csn = dlist_head_element(ChangesStackNode, node, changesStack); - co_new = makeChangedObject(object, csn->ctx); - dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : - csn->changedVarsList, - &co_new->node); - } + addToChangesStackUpperLevel(object, type); + /* Change subxact level due to release */ GetActualState(object)->level--; + if (type == TRANS_PACKAGE) + { + Package *package = (Package *)object; + Assert (numOfTransVars(package) == _numOfTransVars(package)); + } +} + +static void +addToChangesStackUpperLevel(TransObject *object, TransObjectType type) +{ + ChangedObject *co_new; + ChangesStackNode *csn; + /* + * Impossible to push in upper list existing node + * because it was created in another context + */ + csn = dlist_head_element(ChangesStackNode, node, changesStack); + co_new = makeChangedObject(object, csn->ctx); + dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : + csn->changedVarsList, + &co_new->node); } /* @@ -2136,3 +2168,25 @@ _PG_fini(void) UnregisterSubXactCallback(pgvSubTransCallback, NULL); ExecutorEnd_hook = prev_ExecutorEnd; } + +/* Get exact count of valid variables in package. For debug only. */ +static int +_numOfTransVars(Package *package) +{ + HASH_SEQ_STATUS vstat; + Variable *variable; + unsigned long res = 0; + + if (package->varHashTransact) + { + hash_seq_init(&vstat, package->varHashTransact); + while ((variable = (Variable *) hash_seq_search(&vstat)) != NULL) + { + if (GetActualState(variable)->is_valid && + GetActualState(package)->is_valid) + res++; + } + } + + return res; +} diff --git a/pg_variables.h b/pg_variables.h index ce61df1..7c6129b 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -63,7 +63,8 @@ typedef struct TransState /* List node that stores one of the package's states */ typedef struct PackState { - TransState state; + TransState state; + unsigned long trans_var_num; /* Number of valid transactional variables */ } PackState; /* List node that stores one of the variable's states */ @@ -170,6 +171,9 @@ extern void removeObject(TransObject *object, TransObjectType type); #define GetActualValue(variable) \ (((VarState *) GetActualState(variable))->value) +#define numOfTransVars(package) \ + (((PackState *) GetActualState(package))->trans_var_num) + #define GetName(object) \ (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ object->transObject.name) From a57056aa6f18d959cf26df6873587a26ff6a9848 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 27 Feb 2019 11:44:41 +0300 Subject: [PATCH 084/147] Refactoring of object's history initialization --- pg_variables.c | 71 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 3b93bdb..594aa32 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -83,6 +83,7 @@ static int _numOfTransVars(Package *package); static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, MemoryContext ctx); +static void initObjectHistory(TransObject *object, TransObjectType type); /* Hook functions */ static void variable_ExecutorEnd(QueryDesc *queryDesc); @@ -1334,6 +1335,37 @@ makePackHTAB(Package *package, bool is_trans) HASH_ELEM | HASH_CONTEXT); } +static void +initObjectHistory(TransObject *object, TransObjectType type) +{ + /* Initialize history */ + TransState *state; + int size; + + size = (type == TRANS_PACKAGE ? sizeof(PackState) : sizeof(VarState)); + dlist_init(&object->states); + state = MemoryContextAllocZero(ModuleContext, size); + dlist_push_head(&object->states, &(state->node)); + + /* Initialize state */ + state->is_valid = true; + if (type == TRANS_PACKAGE) + ((PackState *)state)->trans_var_num = 0; + else + { + Variable *variable = (Variable *) object; + if (!variable->is_record) + { + VarState * varState = (VarState *) state; + ScalarVar *scalar = &(varState->value.scalar); + + get_typlenbyval(variable->typid, &scalar->typlen, + &scalar->typbyval); + varState->value.scalar.is_null = true; + } + } +} + static Package * getPackage(text *name, bool strict) { @@ -1410,18 +1442,12 @@ createPackage(text *name, bool is_trans) } else { - PackState *packState; - + /* Package entry was created, so initialize it. */ package->varHashRegular = NULL; package->varHashTransact = NULL; package->hctxRegular = NULL; package->hctxTransact = NULL; - /* Initialize history */ - dlist_init(GetStateStorage(package)); - packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); - dlist_push_head(GetStateStorage(package), &(packState->state.node)); - packState->state.is_valid = true; - packState->trans_var_num = 0; + initObjectHistory(&package->transObject, TRANS_PACKAGE); /* Add to changes list */ if (!isObjectChangedInCurrentTrans(&package->transObject)) addToChangesStack(&package->transObject, TRANS_PACKAGE); @@ -1566,27 +1592,12 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, } else { - VarState *varState; - /* Variable entry was created, so initialize new variable. */ variable->typid = typid; variable->package = package; variable->is_record = is_record; variable->is_transactional = is_transactional; - - dlist_init(GetStateStorage(variable)); - varState = MemoryContextAllocZero(pack_hctx(package, is_transactional), - sizeof(VarState)); - - dlist_push_head(GetStateStorage(variable), &varState->state.node); - if (!variable->is_record) - { - ScalarVar *scalar = &(varState->value.scalar); - - get_typlenbyval(variable->typid, &scalar->typlen, - &scalar->typbyval); - varState->value.scalar.is_null = true; - } + initObjectHistory(transObject, TRANS_VARIABLE); if (!isObjectChangedInCurrentTrans(&package->transObject)) { @@ -1771,18 +1782,10 @@ rollbackSavepoint(TransObject *object, TransObjectType type) { if (type == TRANS_PACKAGE && numOfRegVars((Package *)object)) { - PackState *packState; - - packState = MemoryContextAllocZero(ModuleContext, sizeof(PackState)); - dlist_push_head(&object->states, &(packState->state.node)); - packState->state.is_valid = true; - packState->state.level = GetCurrentTransactionNestLevel() - 1; - packState->trans_var_num = 0; - + initObjectHistory(object, type); + GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; if (!dlist_is_empty(changesStack)) - { addToChangesStackUpperLevel(object, type); - } } else removeObject(object, type); From cc5ec13024905d37db9a2ad04834b61fa29f023a Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 27 Feb 2019 17:36:09 +0300 Subject: [PATCH 085/147] Fix createPackage --- pg_variables.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 594aa32..5bb6274 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1408,19 +1408,17 @@ createPackage(text *name, bool is_trans) if (found) { + TransObject *transObj = &package->transObject; + + if (!isObjectChangedInCurrentTrans(transObj)) + createSavepoint(transObj, TRANS_PACKAGE); + if (!GetActualState(package)->is_valid) - /* Make package valid again */ { HASH_SEQ_STATUS vstat; Variable *variable; - TransObject *transObj = &package->transObject; - - /* Make new history entry of package */ - if (!isObjectChangedInCurrentTrans(transObj)) - createSavepoint(transObj, TRANS_PACKAGE); GetActualState(package)->is_valid = true; - /* Mark all transactional variables in package as removed */ if (package->varHashTransact) { @@ -1448,14 +1446,14 @@ createPackage(text *name, bool is_trans) package->hctxRegular = NULL; package->hctxTransact = NULL; initObjectHistory(&package->transObject, TRANS_PACKAGE); - /* Add to changes list */ - if (!isObjectChangedInCurrentTrans(&package->transObject)) - addToChangesStack(&package->transObject, TRANS_PACKAGE); } /* Create corresponding HTAB if not exists */ if (!pack_htab(package, is_trans)) makePackHTAB(package, is_trans); + /* Add to changes list */ + if (!isObjectChangedInCurrentTrans(&package->transObject)) + addToChangesStack(&package->transObject, TRANS_PACKAGE); return package; } From a289ae2b3ec47a5009052d291b1803675f82ffb0 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 27 Feb 2019 18:47:32 +0300 Subject: [PATCH 086/147] Replace package emptiness check to remove_variable() --- pg_variables.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 5bb6274..1c3df0c 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -900,6 +900,8 @@ remove_variable(PG_FUNCTION_ARGS) } GetActualState(variable)->is_valid = false; numOfTransVars(package)--; + if ((numOfTransVars(package) + numOfRegVars(package)) == 0) + GetActualState(package)->is_valid = false; } else removeObject(&variable->transObject, TRANS_VARIABLE); @@ -1380,9 +1382,11 @@ getPackage(text *name, bool strict) { package = (Package *) hash_search(packagesHash, key, HASH_FIND, &found); - if (found && GetActualState(package)->is_valid && - numOfTransVars(package) + numOfRegVars(package)) + if (found && GetActualState(package)->is_valid) + { + Assert (numOfTransVars(package) + numOfRegVars(package) > 0); return package; + } } /* Package not found or it's current state is "invalid" */ if (strict) @@ -1778,7 +1782,7 @@ rollbackSavepoint(TransObject *object, TransObjectType type) if (dlist_is_empty(&object->states)) { - if (type == TRANS_PACKAGE && numOfRegVars((Package *)object)) + if (type == TRANS_PACKAGE && numOfRegVars((Package *)object) > 0) { initObjectHistory(object, type); GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; From 53e6b3f2c2769f1f2a1658895112c40c3adfc49d Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 27 Feb 2019 18:59:18 +0300 Subject: [PATCH 087/147] Rename macro numOfTransVars() --- pg_variables.c | 11 ++++++----- pg_variables.h | 8 ++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 1c3df0c..d7cc333 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -899,8 +899,8 @@ remove_variable(PG_FUNCTION_ARGS) addToChangesStack(transObject, TRANS_VARIABLE); } GetActualState(variable)->is_valid = false; - numOfTransVars(package)--; - if ((numOfTransVars(package) + numOfRegVars(package)) == 0) + GetPackState(package)->trans_var_num--; + if ((GetPackState(package)->trans_var_num + numOfRegVars(package)) == 0) GetActualState(package)->is_valid = false; } else @@ -962,7 +962,7 @@ removePackageInternal(Package *package) addToChangesStack(transObject, TRANS_PACKAGE); } GetActualState(package)->is_valid = false; - numOfTransVars(package) = 0; + GetPackState(package)->trans_var_num = 0; } static bool @@ -1384,7 +1384,8 @@ getPackage(text *name, bool strict) if (found && GetActualState(package)->is_valid) { - Assert (numOfTransVars(package) + numOfRegVars(package) > 0); + Assert (GetPackState(package)->trans_var_num + + numOfRegVars(package) > 0); return package; } } @@ -1610,7 +1611,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, if (is_transactional && (!found || !GetActualState(variable)->is_valid)) - numOfTransVars(package)++; + GetPackState(package)->trans_var_num++; GetActualState(variable)->is_valid = true; Assert (numOfTransVars(package) == _numOfTransVars(package)); diff --git a/pg_variables.h b/pg_variables.h index 7c6129b..fd54315 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -171,15 +171,11 @@ extern void removeObject(TransObject *object, TransObjectType type); #define GetActualValue(variable) \ (((VarState *) GetActualState(variable))->value) -#define numOfTransVars(package) \ - (((PackState *) GetActualState(package))->trans_var_num) +#define GetPackState(package) \ + (((PackState *) GetActualState(package))) #define GetName(object) \ (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ object->transObject.name) -#define GetStateStorage(object) \ - (AssertVariableIsOfTypeMacro(object->transObject, TransObject), \ - &(object->transObject.states)) - #endif /* __PG_VARIABLES_H__ */ From ead49524a0f1bcd1770f948cad3241051f510a0b Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 27 Feb 2019 18:59:46 +0300 Subject: [PATCH 088/147] Remove debug function --- pg_variables.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index d7cc333..47c22c2 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -76,8 +76,6 @@ static void addToChangesStackUpperLevel(TransObject *object, static void pushChangesStack(void); static int numOfRegVars(Package *package); -/* Debug function */ -static int _numOfTransVars(Package *package); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); @@ -906,8 +904,6 @@ remove_variable(PG_FUNCTION_ARGS) else removeObject(&variable->transObject, TRANS_VARIABLE); - Assert (numOfTransVars(package) == _numOfTransVars(package)); - resetVariablesCache(false); PG_FREE_IF_COPY(package_name, 0); @@ -1614,8 +1610,6 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, GetPackState(package)->trans_var_num++; GetActualState(variable)->is_valid = true; - Assert (numOfTransVars(package) == _numOfTransVars(package)); - /* If it is necessary, put variable to changedVars */ if (is_transactional) addToChangesStack(transObject, TRANS_VARIABLE); @@ -1834,11 +1828,6 @@ releaseSavepoint(TransObject *object, TransObjectType type) /* Change subxact level due to release */ GetActualState(object)->level--; - if (type == TRANS_PACKAGE) - { - Package *package = (Package *)object; - Assert (numOfTransVars(package) == _numOfTransVars(package)); - } } static void @@ -2174,25 +2163,3 @@ _PG_fini(void) UnregisterSubXactCallback(pgvSubTransCallback, NULL); ExecutorEnd_hook = prev_ExecutorEnd; } - -/* Get exact count of valid variables in package. For debug only. */ -static int -_numOfTransVars(Package *package) -{ - HASH_SEQ_STATUS vstat; - Variable *variable; - unsigned long res = 0; - - if (package->varHashTransact) - { - hash_seq_init(&vstat, package->varHashTransact); - while ((variable = (Variable *) hash_seq_search(&vstat)) != NULL) - { - if (GetActualState(variable)->is_valid && - GetActualState(package)->is_valid) - res++; - } - } - - return res; -} From 56076bea1da2fa93c3bfdb76289010aaa0f1dcf5 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 5 Mar 2019 14:52:03 +0300 Subject: [PATCH 089/147] Add test for proper package state level assignment --- expected/pg_variables_trans.out | 27 +++++++++++++++++++++++++++ sql/pg_variables_trans.sql | 9 +++++++++ 2 files changed, 36 insertions(+) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 41aeab0..0d0fd14 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1935,6 +1935,33 @@ ERROR: syntax error at or near "ERROR" LINE 1: ERROR; ^ COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + SELECT pgv_free(); pgv_free ---------- diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index ed4a69f..aa3ebcc 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -500,4 +500,13 @@ SELECT pgv_free(); ERROR; COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); +SELECT pgv_free(); +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + SELECT pgv_free(); From 89ac2eecfd093ad559ebda07d4dd2e1852d8e53e Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Tue, 5 Mar 2019 15:20:48 +0300 Subject: [PATCH 090/147] Add missing comment --- pg_variables.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index 47c22c2..dcf3606 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -1605,6 +1605,10 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, } } + /* + * If the variable has been created or has just become valid, + * increment the counter of valid transactional variables. + */ if (is_transactional && (!found || !GetActualState(variable)->is_valid)) GetPackState(package)->trans_var_num++; From 745ea14f6fb17220438594f26a8d6d5525d41805 Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Wed, 6 Mar 2019 18:29:20 +0300 Subject: [PATCH 091/147] Fix package removal in rollback --- expected/pg_variables_trans.out | 43 +++++++++++++++++++++ pg_variables.c | 68 ++++++++++++++++++++++++--------- sql/pg_variables_trans.sql | 16 ++++++++ 3 files changed, 109 insertions(+), 18 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 0d0fd14..83d7893 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1962,6 +1962,49 @@ SELECT package FROM pgv_stats() ORDER BY package; --------- (0 rows) +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); + pgv_exists +------------ + f +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index dcf3606..db01921 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -961,13 +961,11 @@ removePackageInternal(Package *package) GetPackState(package)->trans_var_num = 0; } +/* Check if package has any valid variables */ static bool isPackageEmpty(Package *package) { - int var_num = 0; - - if (package->varHashTransact) - var_num += hash_get_num_entries(package->varHashTransact); + int var_num = GetPackState(package)->trans_var_num; if (package->varHashRegular) var_num += hash_get_num_entries(package->varHashRegular); @@ -1357,7 +1355,7 @@ initObjectHistory(TransObject *object, TransObjectType type) VarState * varState = (VarState *) state; ScalarVar *scalar = &(varState->value.scalar); - get_typlenbyval(variable->typid, &scalar->typlen, + get_typlenbyval(variable->typid, &scalar->typlen, &scalar->typbyval); varState->value.scalar.is_null = true; } @@ -1613,7 +1611,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, (!found || !GetActualState(variable)->is_valid)) GetPackState(package)->trans_var_num++; GetActualState(variable)->is_valid = true; - + /* If it is necessary, put variable to changedVars */ if (is_transactional) addToChangesStack(transObject, TRANS_VARIABLE); @@ -1779,16 +1777,49 @@ rollbackSavepoint(TransObject *object, TransObjectType type) state = GetActualState(object); removeState(object, type, state); - if (dlist_is_empty(&object->states)) + if (type == TRANS_PACKAGE) { - if (type == TRANS_PACKAGE && numOfRegVars((Package *)object) > 0) + /* If there is no more states... */ + if (dlist_is_empty(&object->states)) + { + /* ...but object is a package and has some regular variables... */ + if (numOfRegVars((Package *)object) > 0) + { + /* ...create a new state to make package valid. */ + initObjectHistory(object, type); + GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; + if (!dlist_is_empty(changesStack)) + addToChangesStackUpperLevel(object, type); + } + else + /* ...or remove an object if it is no longer needed. */ + removeObject(object, type); + } + /* + * But if a package has more states, but hasn't valid variables, + * mark it as not valid or remove at top level transaction. + */ + else if (isPackageEmpty((Package *)object)) { - initObjectHistory(object, type); - GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; - if (!dlist_is_empty(changesStack)) + if (dlist_is_empty(changesStack)) + { + removeObject(object, type); + return; + } + else if (!isObjectChangedInUpperTrans(object) && + !dlist_is_empty(changesStack)) + { + createSavepoint(object, type); addToChangesStackUpperLevel(object, type); + GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; + } + GetActualState(object)->is_valid = false; } - else + } + else + { + if (dlist_is_empty(&object->states)) + /* Remove a variable if it is no longer needed. */ removeObject(object, type); } } @@ -1840,9 +1871,9 @@ addToChangesStackUpperLevel(TransObject *object, TransObjectType type) ChangedObject *co_new; ChangesStackNode *csn; /* - * Impossible to push in upper list existing node - * because it was created in another context - */ + * Impossible to push in upper list existing node + * because it was created in another context + */ csn = dlist_head_element(ChangesStackNode, node, changesStack); co_new = makeChangedObject(object, csn->ctx); dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : @@ -1875,13 +1906,14 @@ isObjectChangedInUpperTrans(TransObject *object) *prev_state; cur_state = GetActualState(object); - if (dlist_has_next(&object->states, &cur_state->node)) + if (dlist_has_next(&object->states, &cur_state->node) && + cur_state->level == GetCurrentTransactionNestLevel()) { prev_state = dlist_container(TransState, node, cur_state->node.next); return prev_state->level == GetCurrentTransactionNestLevel() - 1; } - - return false; + else + return cur_state->level == GetCurrentTransactionNestLevel() - 1; } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index aa3ebcc..41ea22b 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -509,4 +509,20 @@ ROLLBACK TO sp_to_rollback; COMMIT; SELECT package FROM pgv_stats() ORDER BY package; +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); +SELECT package FROM pgv_stats() ORDER BY package; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + SELECT pgv_free(); From a6948c3bb18369bbe06ead585d43775a438a609e Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Thu, 7 Mar 2019 16:08:14 +0300 Subject: [PATCH 092/147] Add test --- expected/pg_variables_trans.out | 19 +++++++++++++++++++ sql/pg_variables_trans.sql | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 83d7893..b3576fd 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2005,6 +2005,25 @@ SELECT package FROM pgv_stats() ORDER BY package; --------- (0 rows) +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + SELECT pgv_free(); pgv_free ---------- diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 41ea22b..a3b6319 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -525,4 +525,10 @@ SELECT package FROM pgv_stats() ORDER BY package; COMMIT; SELECT package FROM pgv_stats() ORDER BY package; +SELECT pgv_set('vars', 'any1', 'some value'::text); +BEGIN; +SELECT pgv_remove('vars'); +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + SELECT pgv_free(); From 931961185d2f79490e8217ee06ebe6c67882e74a Mon Sep 17 00:00:00 2001 From: Sergey Cherkashin <4erkashin@list.ru> Date: Mon, 1 Apr 2019 17:53:14 +0300 Subject: [PATCH 093/147] Clear LastHSeqStatus when subtransaction ends --- pg_variables.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index db01921..1d8ce8f 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -2124,6 +2124,7 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } + LastHSeqStatus = NULL; } /* @@ -2152,10 +2153,7 @@ pgvTransCallback(XactEvent event, void *arg) break; } } - - if (event == XACT_EVENT_PARALLEL_ABORT || event == XACT_EVENT_ABORT) - if (LastHSeqStatus) - LastHSeqStatus = NULL; + LastHSeqStatus = NULL; } /* From 6fa76381631bb69b3eea4306cdfc2e7e7f310793 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Tue, 2 Apr 2019 18:12:46 +0300 Subject: [PATCH 094/147] PGPRO-2601: Copy flattened HeapTuple --- pg_variables.c | 28 +++--- pg_variables.h | 6 +- pg_variables_record.c | 200 ++++++++++++++++++++++++++---------------- 3 files changed, 142 insertions(+), 92 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 1d8ce8f..be0c7bc 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -606,7 +606,7 @@ variable_select(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - funcctx->tuple_desc = CreateTupleDescCopy(record->tupdesc); + funcctx->tuple_desc = record->tupdesc; rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); hash_seq_init(rstat, record->rhash); @@ -626,11 +626,10 @@ variable_select(PG_FUNCTION_ARGS) item = (HashRecordEntry *) hash_seq_search(rstat); if (item != NULL) { - Datum result; - - result = HeapTupleGetDatum(item->tuple); + Assert(!HeapTupleHeaderHasExternal( + (HeapTupleHeader) DatumGetPointer(item->tuple))); - SRF_RETURN_NEXT(funcctx, result); + SRF_RETURN_NEXT(funcctx, item->tuple); } else { @@ -694,7 +693,12 @@ variable_select_by_value(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(var_name, 1); if (found) - PG_RETURN_DATUM(HeapTupleGetDatum(item->tuple)); + { + Assert(!HeapTupleHeaderHasExternal( + (HeapTupleHeader) DatumGetPointer(item->tuple))); + + PG_RETURN_DATUM(item->tuple); + } else PG_RETURN_NULL(); } @@ -751,7 +755,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - funcctx->tuple_desc = CreateTupleDescCopy(GetActualValue(variable).record.tupdesc); + funcctx->tuple_desc = GetActualValue(variable).record.tupdesc; var = (VariableIteratorRec *) palloc(sizeof(VariableIteratorRec)); var->iterator = array_create_iterator(values, 0, NULL); @@ -784,11 +788,9 @@ variable_select_by_values(PG_FUNCTION_ARGS) HASH_FIND, &found); if (found) { - Datum result; - - result = HeapTupleGetDatum(item->tuple); - - SRF_RETURN_NEXT(funcctx, result); + Assert(!HeapTupleHeaderHasExternal( + (HeapTupleHeader) DatumGetPointer(item->tuple))); + SRF_RETURN_NEXT(funcctx, item->tuple); } } @@ -1639,7 +1641,7 @@ copyValue(VarState *src, VarState *dest, Variable *destVar) /* Copy previous history entry into the new one */ hash_seq_init(&rstat, record_src->rhash); while ((item_src = (HashRecordEntry *) hash_seq_search(&rstat)) != NULL) - copy_record(record_dest, item_src->tuple, destVar); + insert_record_copy(record_dest, item_src->tuple, destVar); } else /* copy scalar value */ diff --git a/pg_variables.h b/pg_variables.h index fd54315..3e233bd 100755 --- a/pg_variables.h +++ b/pg_variables.h @@ -128,7 +128,7 @@ typedef struct HashRecordKey typedef struct HashRecordEntry { HashRecordKey key; - HeapTuple tuple; + Datum tuple; } HashRecordEntry; /* Element of list with objects created, changed or removed within transaction */ @@ -157,12 +157,12 @@ typedef struct ChangesStackNode extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable); extern void check_attributes(Variable *variable, TupleDesc tupdesc); extern void check_record_key(Variable *variable, Oid typid); -extern void copy_record(RecordVar *dest_record, HeapTuple src_tuple, - Variable *variable); extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool delete_record(Variable *variable, Datum value, bool is_null); +extern void insert_record_copy(RecordVar *dest_record, Datum src_tuple, + Variable *variable); extern void removeObject(TransObject *object, TransObjectType type); #define GetActualState(object) \ diff --git a/pg_variables_record.c b/pg_variables_record.c index 341dca2..1ae1279 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -8,8 +8,10 @@ *------------------------------------------------------------------------- */ #include "postgres.h" +#include "funcapi.h" #include "access/htup_details.h" +#include "access/tuptoaster.h" #include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "utils/builtins.h" @@ -134,7 +136,11 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) #endif oldcxt = MemoryContextSwitchTo(record->hctx); - record->tupdesc = CreateTupleDescCopyConstr(tupdesc); + record->tupdesc = CreateTupleDescCopy(tupdesc); + record->tupdesc->tdhasoid = false; + record->tupdesc->tdtypeid = RECORDOID; + record->tupdesc->tdtypmod = -1; + record->tupdesc = BlessTupleDesc(record->tupdesc); /* Initialize hash table. */ ctl.keysize = sizeof(HashRecordKey); @@ -153,47 +159,6 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) MemoryContextSwitchTo(oldcxt); } -/* - * Copy record using src_tuple. - */ -void -copy_record(RecordVar *dest_record, HeapTuple src_tuple, Variable *variable) -{ - HeapTuple tuple; - Datum value; - bool isnull; - HashRecordKey k; - HashRecordEntry *item; - bool found; - MemoryContext oldcxt; - - oldcxt = MemoryContextSwitchTo(dest_record->hctx); - - /* Inserting a new record into dest_record */ - tuple = heap_copytuple(src_tuple); - value = fastgetattr(tuple, 1, dest_record->tupdesc, &isnull); - - k.value = value; - k.is_null = isnull; - k.hash_proc = &dest_record->hash_proc; - k.cmp_proc = &dest_record->cmp_proc; - - item = (HashRecordEntry *) hash_search(dest_record->rhash, &k, - HASH_ENTER, &found); - if (found) - { - heap_freetuple(tuple); - MemoryContextSwitchTo(oldcxt); - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("there is a record in the variable \"%s\" with same " - "key", GetName(variable)))); - } - item->tuple = tuple; - - MemoryContextSwitchTo(oldcxt); -} - /* * New record structure should be the same as the first record. */ @@ -247,15 +212,78 @@ check_record_key(Variable *variable, Oid typid) "key type", GetName(variable)))); } +static Datum +copy_record_tuple(RecordVar *record, HeapTupleHeader tupleHeader) +{ + TupleDesc tupdesc; + HeapTupleHeader result; + int tuple_len; + + tupdesc = record->tupdesc; + + /* + * If the tuple contains any external TOAST pointers, we have to inline + * those fields to meet the conventions for composite-type Datums. + */ + if (HeapTupleHeaderHasExternal(tupleHeader)) + return toast_flatten_tuple_to_datum(tupleHeader, + HeapTupleHeaderGetDatumLength(tupleHeader), + tupdesc); + + /* + * Fast path for easy case: just make a palloc'd copy and insert the + * correct composite-Datum header fields (since those may not be set if + * the given tuple came from disk, rather than from heap_form_tuple). + */ + tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader); + result = (HeapTupleHeader) palloc(tuple_len); + memcpy((char *) result, (char *) tupleHeader, tuple_len); + + HeapTupleHeaderSetDatumLength(result, tuple_len); + HeapTupleHeaderSetTypeId(result, tupdesc->tdtypeid); + HeapTupleHeaderSetTypMod(result, tupdesc->tdtypmod); + + return PointerGetDatum(result); +} + +static Datum +get_record_key(Datum tuple, TupleDesc tupdesc, bool *isnull) +{ + HeapTupleHeader th = (HeapTupleHeader) DatumGetPointer(tuple); + bool hasnulls = th->t_infomask & HEAP_HASNULL; + bits8 *bp = th->t_bits; /* ptr to null bitmap in tuple */ + char *tp; /* ptr to tuple data */ + long off; /* offset in tuple data */ + int keyatt = 0; + Form_pg_attribute attr = GetTupleDescAttr(tupdesc, keyatt); + + if (hasnulls && att_isnull(keyatt, bp)) + { + *isnull = true; + return (Datum) NULL; + } + + tp = (char *) th + th->t_hoff; + off = 0; + if (attr->attlen == -1) + off = att_align_pointer(off, attr->attalign, -1, tp + off); + else + { + /* not varlena, so safe to use att_align_nominal */ + off = att_align_nominal(off, attr->attalign); + } + + *isnull = false; + return fetchatt(attr, tp + off); +} + /* * Insert a new record. New record key should be unique in the variable. */ void insert_record(Variable *variable, HeapTupleHeader tupleHeader) { - TupleDesc tupdesc; - HeapTuple tuple; - int tuple_len; + Datum tuple; Datum value; bool isnull; RecordVar *record; @@ -270,20 +298,10 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) oldcxt = MemoryContextSwitchTo(record->hctx); - tupdesc = record->tupdesc; - - /* Build a HeapTuple control structure */ - tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader); - - tuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple_len); - tuple->t_len = tuple_len; - ItemPointerSetInvalid(&(tuple->t_self)); - tuple->t_tableOid = InvalidOid; - tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE); - memcpy((char *) tuple->t_data, (char *) tupleHeader, tuple_len); + tuple = copy_record_tuple(record, tupleHeader); /* Inserting a new record */ - value = fastgetattr(tuple, 1, tupdesc, &isnull); + value = get_record_key(tuple, record->tupdesc, &isnull); /* First, check if there is a record with same key */ k.value = value; k.is_null = isnull; @@ -294,7 +312,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) HASH_ENTER, &found); if (found) { - heap_freetuple(tuple); + pfree(DatumGetPointer(tuple)); MemoryContextSwitchTo(oldcxt); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -313,9 +331,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader) bool update_record(Variable *variable, HeapTupleHeader tupleHeader) { - TupleDesc tupdesc; - HeapTuple tuple; - int tuple_len; + Datum tuple; Datum value; bool isnull; RecordVar *record; @@ -330,20 +346,10 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader) oldcxt = MemoryContextSwitchTo(record->hctx); - tupdesc = record->tupdesc; - - /* Build a HeapTuple control structure */ - tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader); - - tuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple_len); - tuple->t_len = tuple_len; - ItemPointerSetInvalid(&(tuple->t_self)); - tuple->t_tableOid = InvalidOid; - tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE); - memcpy((char *) tuple->t_data, (char *) tupleHeader, tuple_len); + tuple = copy_record_tuple(record, tupleHeader); /* Update a record */ - value = fastgetattr(tuple, 1, tupdesc, &isnull); + value = get_record_key(tuple, record->tupdesc, &isnull); k.value = value; k.is_null = isnull; k.hash_proc = &record->hash_proc; @@ -353,13 +359,13 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader) HASH_FIND, &found); if (!found) { - heap_freetuple(tuple); + pfree(DatumGetPointer(tuple)); MemoryContextSwitchTo(oldcxt); return false; } /* Release old tuple */ - heap_freetuple(item->tuple); + pfree(DatumGetPointer(item->tuple)); item->tuple = tuple; MemoryContextSwitchTo(oldcxt); @@ -387,7 +393,49 @@ delete_record(Variable *variable, Datum value, bool is_null) item = (HashRecordEntry *) hash_search(record->rhash, &k, HASH_REMOVE, &found); if (found) - heap_freetuple(item->tuple); + pfree(DatumGetPointer(item->tuple)); return found; } + +/* + * Copy record using src_tuple. + */ +void +insert_record_copy(RecordVar *dest_record, Datum src_tuple, Variable *variable) +{ + Datum tuple; + Datum value; + bool isnull; + HashRecordKey k; + HashRecordEntry *item; + bool found; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(dest_record->hctx); + + /* Inserting a new record into dest_record */ + tuple = copy_record_tuple(dest_record, + (HeapTupleHeader) DatumGetPointer(src_tuple)); + value = get_record_key(tuple, dest_record->tupdesc, &isnull); + + k.value = value; + k.is_null = isnull; + k.hash_proc = &dest_record->hash_proc; + k.cmp_proc = &dest_record->cmp_proc; + + item = (HashRecordEntry *) hash_search(dest_record->rhash, &k, + HASH_ENTER, &found); + if (found) + { + pfree(DatumGetPointer(tuple)); + MemoryContextSwitchTo(oldcxt); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("there is a record in the variable \"%s\" with same " + "key", GetName(variable)))); + } + item->tuple = tuple; + + MemoryContextSwitchTo(oldcxt); +} From 68989c24b009890461aadee7764fad8d7f679f81 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Tue, 2 Apr 2019 20:48:36 +0300 Subject: [PATCH 095/147] PGPRO-2601: Test pgv_select() on TupleDesc of dropped table --- expected/pg_variables.out | 10 +++++++ expected/pg_variables_trans.out | 48 +++++++++++++++++---------------- sql/pg_variables.sql | 4 +++ sql/pg_variables_trans.sql | 2 ++ 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 592c401..c456d60 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -704,6 +704,16 @@ SELECT pgv_exists('vars3', 'r1'); SELECT pgv_select('vars2', 'j1'); ERROR: variable "j1" requires "jsonb" value +-- PGPRO-2601 - Test pgv_select() on TupleDesc of dropped table +DROP TABLE tab; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (0,str00) +(3 rows) + -- Tests for SRF's sequential scan of an internal hash table DO $$BEGIN diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index b3576fd..025cc77 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -416,6 +416,8 @@ SELECT pgv_get_jsonb('vars2', 'j2'); (1 row) COMMIT; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); BEGIN; SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; pgv_insert @@ -452,8 +454,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -462,8 +464,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -473,8 +475,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -483,8 +485,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -824,8 +826,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -833,8 +835,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -843,8 +845,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -853,8 +855,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -1132,8 +1134,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -1142,8 +1144,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -1153,8 +1155,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -1163,8 +1165,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -1230,8 +1232,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -1239,8 +1241,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -1249,8 +1251,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (5,str55) (0,str00) (5 rows) @@ -1259,8 +1261,8 @@ SELECT pgv_select('vars3', 'r2'); pgv_select ------------ (,strNULL) - (2,) (1,str33) + (2,) (0,str00) (4 rows) @@ -1436,8 +1438,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select --------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"after savepoint sp1") (0,str00) (5 rows) @@ -1447,8 +1449,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select --------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"after savepoint sp1") (0,str00) (5 rows) @@ -1458,8 +1460,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select --------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"after savepoint sp1") (0,str00) (5 rows) @@ -1469,8 +1471,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select -------------------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"after savepoint sp1") (0,str00) (7,"row after sp2 to remove in sp4") @@ -1481,8 +1483,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select -------------------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"after savepoint sp1") (0,str00) (7,"row after sp2 to remove in sp4") @@ -1493,8 +1495,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ---------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"before savepoint sp1") (0,str00) (5 rows) @@ -1504,8 +1506,8 @@ SELECT pgv_select('vars3', 'r1'); pgv_select ---------------------------- (,strNULL) - (2,) (1,str33) + (2,) (5,"before savepoint sp1") (0,str00) (5 rows) diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 9e32468..3bafdbd 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -195,6 +195,10 @@ SELECT pgv_exists('vars3', 'r3'); SELECT pgv_exists('vars3', 'r1'); SELECT pgv_select('vars2', 'j1'); +-- PGPRO-2601 - Test pgv_select() on TupleDesc of dropped table +DROP TABLE tab; +SELECT pgv_select('vars3', 'r1'); + -- Tests for SRF's sequential scan of an internal hash table DO $$BEGIN diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index a3b6319..c4effd9 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -80,6 +80,8 @@ SELECT pgv_get_jsonb('vars2', 'j1'); SELECT pgv_get_jsonb('vars2', 'j2'); COMMIT; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); BEGIN; SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab; From 2a4bba31f679992afb54aa40b4e16b25715f5b91 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 3 Apr 2019 11:09:03 +0300 Subject: [PATCH 096/147] PGPRO-2601: Remove LastHSeqStatus and add test a cursor with the hash table --- expected/pg_variables.out | 31 +++++++++++++++++++++++++++++-- pg_variables.c | 21 --------------------- sql/pg_variables.sql | 13 +++++++++++-- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index c456d60..6ded8c8 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -730,7 +730,8 @@ SELECT pgv_select('vars3', 'r1', 1); (1 row) -SELECT pgv_select('vars3', 'r1') LIMIT 2; +SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning +WARNING: leaked hash_seq_search scan for hash table 0x561738129110 pgv_select ------------ (,strNULL) @@ -743,8 +744,34 @@ SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; (0,str00) (1 row) +-- PGPRO-2601 - Test a cursor with the hash table +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('vars3', 'r1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (,strNULL) +(1 row) + +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (2,) + (0,str00) +(3 rows) + +FETCH 1 in r1_cur; + pgv_select +------------ + (2,) +(1 row) + +CLOSE r1_cur; +COMMIT; -- warning +WARNING: leaked hash_seq_search scan for hash table 0x561738129110 -- Clean memory after unsuccessful creation of a variable -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail ERROR: could not identify a hash function for type unknown SELECT package FROM pgv_stats() WHERE package = 'vars4'; package diff --git a/pg_variables.c b/pg_variables.c index be0c7bc..a43c3c9 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -108,14 +108,6 @@ static Variable *LastVariable = NULL; /* Recent row type id */ static Oid LastTypeId = InvalidOid; -/* - * Cache sequentially search through hash table status. It is necessary for - * clean up if hash_seq_term() wasn't called or if we didn't scan the whole - * table. In this case we need to manually call hash_seq_term() within - * variable_ExecutorEnd(). - */ -static HASH_SEQ_STATUS *LastHSeqStatus = NULL; - /* Saved hook values for recall */ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; @@ -615,8 +607,6 @@ variable_select(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); - - LastHSeqStatus = rstat; } funcctx = SRF_PERCALL_SETUP(); @@ -633,7 +623,6 @@ variable_select(PG_FUNCTION_ARGS) } else { - LastHSeqStatus = NULL; pfree(rstat); SRF_RETURN_DONE(funcctx); } @@ -1212,8 +1201,6 @@ get_packages_stats(PG_FUNCTION_ARGS) hash_seq_init(pstat, packagesHash); funcctx->user_fctx = pstat; - - LastHSeqStatus = pstat; } else funcctx->user_fctx = NULL; @@ -1260,7 +1247,6 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - LastHSeqStatus = NULL; pfree(pstat); SRF_RETURN_DONE(funcctx); } @@ -2126,7 +2112,6 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } - LastHSeqStatus = NULL; } /* @@ -2155,7 +2140,6 @@ pgvTransCallback(XactEvent event, void *arg) break; } } - LastHSeqStatus = NULL; } /* @@ -2164,11 +2148,6 @@ pgvTransCallback(XactEvent event, void *arg) static void variable_ExecutorEnd(QueryDesc *queryDesc) { - if (LastHSeqStatus) - { - hash_seq_term(LastHSeqStatus); - LastHSeqStatus = NULL; - } if (prev_ExecutorEnd) prev_ExecutorEnd(queryDesc); else diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index 3bafdbd..a373e93 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -207,11 +207,20 @@ $$BEGIN END$$; -- Check that the hash table was cleaned up after rollback SELECT pgv_select('vars3', 'r1', 1); -SELECT pgv_select('vars3', 'r1') LIMIT 2; +SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; +-- PGPRO-2601 - Test a cursor with the hash table +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('vars3', 'r1'); +FETCH 1 in r1_cur; +SELECT pgv_select('vars3', 'r1'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +COMMIT; -- warning + -- Clean memory after unsuccessful creation of a variable -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail SELECT package FROM pgv_stats() WHERE package = 'vars4'; -- Remove package if it is empty From 97e236540a4d8abf924c8e3369817f2d7d2858c1 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Wed, 3 Apr 2019 11:29:46 +0300 Subject: [PATCH 097/147] PGPRO-2601: Fix tests --- expected/pg_variables.out | 4 ++-- sql/pg_variables.sql | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 6ded8c8..ba0b64a 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -724,6 +724,7 @@ ERROR: unrecognized variable "r3" CONTEXT: SQL statement "SELECT pgv_select('vars3', 'r3')" PL/pgSQL function inline_code_block line 3 at PERFORM -- Check that the hash table was cleaned up after rollback +SET client_min_messages to 'ERROR'; SELECT pgv_select('vars3', 'r1', 1); pgv_select ------------ @@ -731,7 +732,6 @@ SELECT pgv_select('vars3', 'r1', 1); (1 row) SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning -WARNING: leaked hash_seq_search scan for hash table 0x561738129110 pgv_select ------------ (,strNULL) @@ -769,7 +769,7 @@ FETCH 1 in r1_cur; CLOSE r1_cur; COMMIT; -- warning -WARNING: leaked hash_seq_search scan for hash table 0x561738129110 +RESET client_min_messages; -- Clean memory after unsuccessful creation of a variable SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail ERROR: could not identify a hash function for type unknown diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index a373e93..a9fdbbd 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -206,6 +206,7 @@ $$BEGIN PERFORM pgv_select('vars3', 'r3'); END$$; -- Check that the hash table was cleaned up after rollback +SET client_min_messages to 'ERROR'; SELECT pgv_select('vars3', 'r1', 1); SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2; @@ -218,6 +219,7 @@ SELECT pgv_select('vars3', 'r1'); FETCH 1 in r1_cur; CLOSE r1_cur; COMMIT; -- warning +RESET client_min_messages; -- Clean memory after unsuccessful creation of a variable SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail From 152cc15da2f6b503511397f33d7c5f21216918f5 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Tue, 9 Apr 2019 13:55:06 +0300 Subject: [PATCH 098/147] PGPRO-2564: Add ATX compatibility check --- pg_variables.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index a43c3c9..ad3f67e 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -2088,6 +2088,15 @@ processChanges(Action action) } } +static void +compatibility_check(void) +{ +#ifdef PGPRO_EE + if (getNestLevelATX() != 0) + elog(ERROR, "pg_variable extension is not compatible with autonomous transactions and connection pooling"); +#endif +} + /* * Intercept execution during subtransaction processing */ @@ -2101,6 +2110,7 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, { case SUBXACT_EVENT_START_SUB: pushChangesStack(); + compatibility_check(); break; case SUBXACT_EVENT_COMMIT_SUB: processChanges(RELEASE_SAVEPOINT); @@ -2125,6 +2135,7 @@ pgvTransCallback(XactEvent event, void *arg) switch (event) { case XACT_EVENT_PRE_COMMIT: + compatibility_check(); processChanges(RELEASE_SAVEPOINT); break; case XACT_EVENT_ABORT: From 8f62247c94d0dc8645e67608ebd8ef4581b25c98 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Tue, 25 Jun 2019 17:36:23 +0300 Subject: [PATCH 099/147] Add /log/ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ee52e69..03329b5 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ lib*.pc /pgsql.sln.cache /Debug/ /Release/ +/log/ /tmp_install/ Dockerfile pg_variables--1.1.sql From 0e25dd1b2b20fc6df62236a342575844b4e3c6a5 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Tue, 30 Jul 2019 16:38:42 +0300 Subject: [PATCH 100/147] Improve support of PostgreSQL 12 --- pg_variables.c | 14 ++++++++++++++ pg_variables_record.c | 2 ++ 2 files changed, 16 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index ad3f67e..781b460 100755 --- a/pg_variables.c +++ b/pg_variables.c @@ -86,6 +86,19 @@ static void initObjectHistory(TransObject *object, TransObjectType type); /* Hook functions */ static void variable_ExecutorEnd(QueryDesc *queryDesc); +#if PG_VERSION_NUM >= 120000 +#define CHECK_ARGS_FOR_NULL() \ +do { \ + if (fcinfo->args[0].isnull) \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ + errmsg("package name can not be NULL"))); \ + if (fcinfo->args[1].isnull) \ + ereport(ERROR, \ + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ + errmsg("variable name can not be NULL"))); \ +} while(0) +#else /* PG_VERSION_NUM < 120000 */ #define CHECK_ARGS_FOR_NULL() \ do { \ if (fcinfo->argnull[0]) \ @@ -97,6 +110,7 @@ do { \ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("variable name can not be NULL"))); \ } while(0) +#endif /* PG_VERSION_NUM */ static HTAB *packagesHash = NULL; static MemoryContext ModuleContext = NULL; diff --git a/pg_variables_record.c b/pg_variables_record.c index 1ae1279..beba0d6 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -137,7 +137,9 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) oldcxt = MemoryContextSwitchTo(record->hctx); record->tupdesc = CreateTupleDescCopy(tupdesc); +#if PG_VERSION_NUM < 120000 record->tupdesc->tdhasoid = false; +#endif record->tupdesc->tdtypeid = RECORDOID; record->tupdesc->tdtypmod = -1; record->tupdesc = BlessTupleDesc(record->tupdesc); From b01b0f9e75a9e7d43e60cf5545651a6186deafc8 Mon Sep 17 00:00:00 2001 From: Arthur Zakirov Date: Fri, 9 Aug 2019 13:41:57 +0300 Subject: [PATCH 101/147] Issue #26: Add pgv_set() and pgv_get() arguments description --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 0c584c0..01be495 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,28 @@ Function | Returns `pgv_set(package text, name text, value anyarray, is_transactional bool default false)` | `void` `pgv_get(package text, name text, var_type anyarray, strict bool default true)` | `anyarray` +`pgv_set` arguments: +- `package` - name of the package, it will be created if it doesn't exist. +- `name` - name of the variable, it will be created if it doesn't exist. +`pgv_set` fails if the variable already exists and its transactionality doesn't +match `is_transactional` argument. +- `value` - new value for the variable. `pgv_set` fails if the variable already +exists and its type doesn't match new value's type. +- `is_transactional` - transactionality of the newly created variable, by +default it is false. + +`pgv_get` arguments: +- `package` - name of the existing package. If the package doesn't exist result +depends on `strict` argument: if it is false then `pgv_get` returns NULL +otherwise it fails. +- `name` - name of the the existing variable. If the variable doesn't exist +result depends on `strict` argument: if it is false then `pgv_get` returns NULL +otherwise it fails. +- `var_type` - type of the existing variable. It is necessary to pass it to get +correct return type. +- `strict` - pass false if `pgv_get` shouldn't raise an error if a variable or a +package didn't created before, by default it is true. + ## **Deprecated** scalar variables functions ### Integer variables From 7c5081ee57d216694b90572e7aa0804238c9c49d Mon Sep 17 00:00:00 2001 From: Victor Wagner Date: Thu, 2 Apr 2020 16:52:03 +0300 Subject: [PATCH 102/147] Remove executable build from C sources because rpmbuild complains about it --- pg_variables.c | 0 pg_variables.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 pg_variables.c mode change 100755 => 100644 pg_variables.h diff --git a/pg_variables.c b/pg_variables.c old mode 100755 new mode 100644 diff --git a/pg_variables.h b/pg_variables.h old mode 100755 new mode 100644 From 13a77d594b258a0baf0623a8eb1c7f791bbe91ee Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 8 Oct 2020 15:57:23 +0300 Subject: [PATCH 103/147] Add support for PostgreSQL 13. --- pg_variables.c | 7 +++++-- pg_variables_record.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 781b460..0c6dca2 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2106,8 +2106,11 @@ static void compatibility_check(void) { #ifdef PGPRO_EE - if (getNestLevelATX() != 0) - elog(ERROR, "pg_variable extension is not compatible with autonomous transactions and connection pooling"); +# if (PG_VERSION_NUM < 130000) || \ + ((PG_VERSION_NUM >= 130000) && (defined PGPRO_FEATURE_ATX)) + if (getNestLevelATX() != 0) + elog(ERROR, "pg_variable extension is not compatible with autonomous transactions and connection pooling"); +# endif #endif } diff --git a/pg_variables_record.c b/pg_variables_record.c index beba0d6..b97ffca 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -11,7 +11,18 @@ #include "funcapi.h" #include "access/htup_details.h" -#include "access/tuptoaster.h" +/* + * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=8b94dab06617ef80a0901ab103ebd8754427ef + * + * Split tuptoaster.c into three separate files. + */ +#if PG_VERSION_NUM >= 130000 +# include "access/detoast.h" +# include "access/heaptoast.h" +#else +# include "access/tuptoaster.h" +#endif + #include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "utils/builtins.h" From e1a502a32ad5cab838813011898387931b10c622 Mon Sep 17 00:00:00 2001 From: ziva777 Date: Wed, 14 Oct 2020 20:01:38 +0300 Subject: [PATCH 104/147] Issue #27: Error with transactional pgv_insert/pgv_remove (#28) Authored-by: Maxim Orlov --- expected/pg_variables_trans.out | 335 ++++++++++++++++++++++++++++++++ pg_variables.c | 51 +++-- pg_variables.h | 1 + sql/pg_variables_trans.sql | 80 ++++++++ 4 files changed, 456 insertions(+), 11 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 025cc77..d73ef69 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2032,3 +2032,338 @@ SELECT pgv_free(); (1 row) +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | z | t +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 0c6dca2..ee1fa3d 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -56,7 +56,7 @@ static Variable *getVariableInternal(Package *package, text *name, static Variable *createVariableInternal(Package *package, text *name, Oid typid, bool is_record, bool is_transactional); static void removePackageInternal(Package *package); -static void resetVariablesCache(bool with_package); +static void resetVariablesCache(void); /* Functions to work with transactional objects */ static void createSavepoint(TransObject *object, TransObjectType type); @@ -403,7 +403,15 @@ variable_insert(PG_FUNCTION_ARGS) * record type or if last record has different id. */ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - check_attributes(variable, tupdesc); + if (variable->is_deleted) + { + init_record(record, tupdesc, variable); + variable->is_deleted = false; + } + else + { + check_attributes(variable, tupdesc); + } } LastTypeId = tupType; @@ -901,6 +909,7 @@ remove_variable(PG_FUNCTION_ARGS) createSavepoint(transObject, TRANS_VARIABLE); addToChangesStack(transObject, TRANS_VARIABLE); } + variable->is_deleted = true; GetActualState(variable)->is_valid = false; GetPackState(package)->trans_var_num--; if ((GetPackState(package)->trans_var_num + numOfRegVars(package)) == 0) @@ -909,7 +918,7 @@ remove_variable(PG_FUNCTION_ARGS) else removeObject(&variable->transObject, TRANS_VARIABLE); - resetVariablesCache(false); + resetVariablesCache(); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -936,7 +945,7 @@ remove_package(PG_FUNCTION_ARGS) package = getPackage(package_name, true); removePackageInternal(package); - resetVariablesCache(true); + resetVariablesCache(); PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); @@ -945,7 +954,27 @@ remove_package(PG_FUNCTION_ARGS) static void removePackageInternal(Package *package) { - TransObject *transObject; + TransObject *transObject; + Variable *variable; + HTAB *htab; + HASH_SEQ_STATUS vstat; + int i; + + /* Mark all the valid variables from package as deleted */ + for (i = 0; i < 2; i++) + { + if ((htab = pack_htab(package, i)) != NULL) + { + hash_seq_init(&vstat, htab); + + while ((variable = + (Variable *) hash_seq_search(&vstat)) != NULL) + { + if (GetActualState(variable)->is_valid) + variable->is_deleted = true; + } + } + } /* All regular variables will be freed */ if (package->hctxRegular) @@ -983,11 +1012,10 @@ isPackageEmpty(Package *package) * of some changes: removing, rollbacking, etc. */ static void -resetVariablesCache(bool with_package) +resetVariablesCache(void) { /* Remove package and variable from cache */ - if (with_package) - LastPackage = NULL; + LastPackage = NULL; LastVariable = NULL; LastTypeId = InvalidOid; } @@ -1013,7 +1041,7 @@ remove_packages(PG_FUNCTION_ARGS) removePackageInternal(package); } - resetVariablesCache(true); + resetVariablesCache(); PG_RETURN_VOID(); } @@ -1596,6 +1624,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, variable->package = package; variable->is_record = is_record; variable->is_transactional = is_transactional; + variable->is_deleted = false; initObjectHistory(transObject, TRANS_VARIABLE); if (!isObjectChangedInCurrentTrans(&package->transObject)) @@ -1728,7 +1757,7 @@ removeObject(TransObject *object, TransObjectType type) GetActualState(&package->transObject)->is_valid = false; } - resetVariablesCache(true); + resetVariablesCache(); } /* @@ -2096,7 +2125,7 @@ processChanges(Action action) MemoryContextDelete(ModuleContext); packagesHash = NULL; ModuleContext = NULL; - resetVariablesCache(true); + resetVariablesCache(); changesStack = NULL; changesStackContext = NULL; } diff --git a/pg_variables.h b/pg_variables.h index 3e233bd..b08faa6 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -113,6 +113,7 @@ typedef struct Variable * specified only when creating a variable. */ bool is_transactional; + bool is_deleted; } Variable; typedef struct HashRecordKey diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index c4effd9..c4b8c90 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -534,3 +534,83 @@ ROLLBACK; SELECT package FROM pgv_stats() ORDER BY package; SELECT pgv_free(); + +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'x'); +ROLLBACK; + +SELECT * FROM pgv_list() order by package, name; + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'x'); +COMMIT; + +SELECT * FROM pgv_list() order by package, name; +SELECT pgv_select('test', 'x'); + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'y'); +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'y'); +ROLLBACK; + +SELECT * FROM pgv_list() order by package, name; + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'y'); +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'y'); +COMMIT; + +SELECT * FROM pgv_list() order by package, name; +SELECT pgv_select('test', 'y'); + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'z'); +SELECT pgv_free(); +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'z'); +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'z'); +ROLLBACK; + +SELECT * FROM pgv_list() order by package, name; + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); +SELECT pgv_select('test', 'z'); +SELECT pgv_free(); +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); +SELECT pgv_select('test', 'z'); +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); +SELECT pgv_select('test', 'z'); +COMMIT; + +SELECT * FROM pgv_list() order by package, name; +SELECT pgv_select('test', 'z'); + +SELECT pgv_free(); From 30e072295e8dfdf588eabfe7e38bcd7366d2cfd8 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Thu, 15 Oct 2020 14:30:11 +0300 Subject: [PATCH 105/147] Run CI tests fro Postgres 12 and 13 --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7edda6c..495cf4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,12 @@ notifications: on_failure: always env: + - PG_VERSION=13 LEVEL=nightmare + - PG_VERSION=13 LEVEL=hardcore + - PG_VERSION=13 + - PG_VERSION=12 LEVEL=nightmare + - PG_VERSION=12 LEVEL=hardcore + - PG_VERSION=12 - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 @@ -27,4 +33,4 @@ env: matrix: allow_failures: - - env: PG_VERSION=11 LEVEL=nightmare + - env: LEVEL=nightmare From 49807a2e1fe454c84aa5303d0b79090eebd5cb86 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Thu, 15 Oct 2020 15:21:19 +0300 Subject: [PATCH 106/147] Fix .travis.yml --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 495cf4b..5882736 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -sudo: required - language: c services: @@ -21,16 +19,19 @@ env: - PG_VERSION=13 LEVEL=nightmare - PG_VERSION=13 LEVEL=hardcore - PG_VERSION=13 - - PG_VERSION=12 LEVEL=nightmare + # - PG_VERSION=12 LEVEL=nightmare - PG_VERSION=12 LEVEL=hardcore - PG_VERSION=12 - - PG_VERSION=11 LEVEL=nightmare + # - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 +# XXX: consider fixing nightmare mode matrix: allow_failures: - - env: LEVEL=nightmare + - env: PG_VERSION=11 LEVEL=nightmare + - env: PG_VERSION=12 LEVEL=nightmare + - env: PG_VERSION=13 LEVEL=nightmare From a1356048b680b88cd9ba11f83e5e680a531421e6 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Thu, 15 Oct 2020 15:54:32 +0300 Subject: [PATCH 107/147] Try to fix nightmare mode --- Dockerfile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 1760377..0bcd176 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -5,7 +5,7 @@ RUN apk add --no-cache \ openssl curl \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ - zlib-dev libedit-dev \ + zlib-dev libedit-dev linux-headers \ clang clang-analyzer; # Install fresh valgrind From 080cb1c68708591f73e0d95d447a2f7ae201620b Mon Sep 17 00:00:00 2001 From: ziva777 Date: Fri, 30 Oct 2020 13:27:33 +0300 Subject: [PATCH 108/147] Bugfix/issue 27 (#29) * Issue #27: Error with transactional pgv_insert/pgv_remove * Issue #27: Error with transactional pgv_insert/pgv_remove - review * Issue #27: Add fix and improve tests. * Issue #27: Fix for backend termination on transaction commit. * Issue #27: Add fix for leaked hash_seq_search. Fix warning: leaked hash_seq_search scan for hash table. * Issue #27: Add tests and comment. * Issue #27: Add fix for leaked hash_seq_search for pgv_stats. * Issue #27: Add fix for VALGRING failed test. * Issue #27: Add fix for Travis Ci failed tests. * Issue #27: Fix tests for pgv_stats on 9.5 * Fix some comments Co-authored-by: Maxim Orlov Co-authored-by: Alexey Kondratov --- expected/pg_variables_trans.out | 1286 ++++++++++ expected/pg_variables_trans_1.out | 3655 +++++++++++++++++++++++++++++ pg_variables.c | 262 ++- sql/pg_variables_trans.sql | 461 ++++ 4 files changed, 5639 insertions(+), 25 deletions(-) create mode 100644 expected/pg_variables_trans_1.out diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index d73ef69..2ce8138 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2367,3 +2367,1289 @@ SELECT pgv_free(); (1 row) +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +COMMIT; +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z2" +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z3" +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +COMMIT; +SELECT pgv_select('test', 'z3'); +ERROR: unrecognized variable "z3" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out new file mode 100644 index 0000000..bc55b84 --- /dev/null +++ b/expected/pg_variables_trans_1.out @@ -0,0 +1,3655 @@ +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; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); +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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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 +-- 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); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(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_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 COMMITTING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +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) + +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) + +-- 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) + +-- 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_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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + +-- 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) + +-- 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) + +-- 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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', '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_select('vars', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + 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; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- 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) + +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) + +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) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + begin +(1 row) + +ROLLBACK; +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; +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +COMMIT; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_free(); -- Check sequential package removal in one subtransaction + pgv_free +---------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans2 | t +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp4; +SAVEPOINT sp5; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp5; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp4; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +COMMIT; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +ERROR: division by zero +SELECT pgv_insert('package', 'errs',row(1), true); + pgv_insert +------------ + +(1 row) + +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT pgv_select('vars4', 'r1', 0); +ERROR: unrecognized package "vars4" +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +COMMIT; +-- Tests for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + pgv_delete +------------ + t +(1 row) + +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 't2', ''::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ERROR; +ERROR: syntax error at or near "ERROR" +LINE 1: ERROR; + ^ +COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); + pgv_exists +------------ + f +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | z | t +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +COMMIT; +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z2" +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z3" +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +COMMIT; +SELECT pgv_select('test', 'z3'); +ERROR: unrecognized variable "z3" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index ee1fa3d..9e83352 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -129,6 +129,165 @@ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; +/* + * List to store all the running hash_seq_search, variable and package scan for + * hash table. + * + * NOTE: In function variable_select we use hash_seq_search to find next tuple. + * So, in case user do not get all the data from set at once (use cursors or + * LIMIT) we have to call hash_seq_term to not to leak hash_seq_search scans. + * + * For doing this, we alloc all of the rstats in the TopTransactionContext and + * save pointers to the rstats into list. Once transaction ended (commited or + * aborted) we clear all the "active" hash_seq_search by calling hash_seq_term. + * + * TopTransactionContext is handy here, because it would not be reset by the + * time pgvTransCallback is called. + */ +static List *variables_stats = NIL; +static List *packages_stats = NIL; + +typedef struct tagHtabToStat { + HTAB *hash; + HASH_SEQ_STATUS *status; + Variable *variable; + Package *package; + int level; +} HtabToStat; + +/* + * A bunch of comp functions for HtabToStat members here. + */ +static bool +HtabToStat_status_eq(HtabToStat *entry, void *value) +{ + return entry->status == (HASH_SEQ_STATUS *) value; +} + +static bool +HtabToStat_variable_eq(HtabToStat *entry, void *value) +{ + return entry->variable == (Variable *) value; +} + +static bool +HtabToStat_package_eq(HtabToStat *entry, void *value) +{ + return entry->package == (Package *) value; +} + +static bool +HtabToStat_eq_all(HtabToStat *entry, void *value) +{ + return true; +} + +static bool +HtabToStat_level_eq(HtabToStat *entry, void *value) +{ + return entry->level == *(int *) value; +} + +/* + * Generic remove_if algorithm for HtabToStat. + * + * 'eq' - is a function pointer used to compare list entries to the 'value'. + * 'match_first' - if true return on first match. + */ +static void +HtabToStat_remove_if(List **l, void *value, + bool (*eq)(HtabToStat *, void *), + bool match_first) +{ +#if (PG_VERSION_NUM < 130000) + ListCell *cell, *next, *prev = NULL; + HtabToStat *entry = NULL; + + for (cell = list_head(*l); cell; cell = next) + { + entry = (HtabToStat *) lfirst(cell); + next = lnext(cell); + + if (eq(entry, value)) + { + *l = list_delete_cell(*l, cell, prev); + pfree(entry->status); + pfree(entry); + + if (match_first) + return; + } + else + { + prev = cell; + } + } +#else + /* + * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 + * + * Version > 13 have different lists interface. + */ + ListCell *cell; + HtabToStat *entry = NULL; + + foreach(cell, *l) + { + entry = (HtabToStat *) lfirst(cell); + + if (eq(entry, value)) + *l = foreach_delete_current(*l, cell); + } +#endif +} + +/* + * Remove first entry for status. + */ +static void +remove_variables_status(List **l, HASH_SEQ_STATUS *status) +{ + HtabToStat_remove_if(l, status, HtabToStat_status_eq, true); +} + +/* + * Remove first entry for variable. + */ +static void +remove_variables_variable(List **l, Variable *variable) +{ + HtabToStat_remove_if(l, variable, HtabToStat_variable_eq, true); +} + +/* + * Remove all the entries for package. + */ +static void +remove_variables_package(List **l, Package *package) +{ + HtabToStat_remove_if(l, package, HtabToStat_package_eq, false); +} + +/* + * Remove all the entries for level. + */ +static void +remove_variables_level(List **l, int level) +{ + HtabToStat_remove_if(l, &level, HtabToStat_level_eq, false); +} + +/* + * Remove all. + */ +static void +remove_variables_all(List **l) +{ + HtabToStat_remove_if(l, NULL, HtabToStat_eq_all, false); + *l = NIL; +} + +static void freeStatsLists(void); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -595,30 +754,31 @@ variable_select(PG_FUNCTION_ARGS) FuncCallContext *funcctx; HASH_SEQ_STATUS *rstat; HashRecordEntry *item; + text *package_name; + text *var_name; + Package *package; + Variable *variable; - if (SRF_IS_FIRSTCALL()) - { - text *package_name; - text *var_name; - Package *package; - Variable *variable; - MemoryContext oldcontext; - RecordVar *record; + CHECK_ARGS_FOR_NULL(); - CHECK_ARGS_FOR_NULL(); + /* Get arguments */ + package_name = PG_GETARG_TEXT_PP(0); + var_name = PG_GETARG_TEXT_PP(1); - /* Get arguments */ - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); + package = getPackage(package_name, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); - package = getPackage(package_name, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, - true); + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + RecordVar *record; + HtabToStat *htab_to_stat; record = &(GetActualValue(variable).record); - funcctx = SRF_FIRSTCALL_INIT(); - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + oldcontext = MemoryContextSwitchTo(TopTransactionContext); funcctx->tuple_desc = record->tupdesc; @@ -626,6 +786,14 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; + htab_to_stat = palloc0(sizeof(HtabToStat)); + htab_to_stat->hash = record->rhash; + htab_to_stat->status = rstat; + htab_to_stat->variable = variable; + htab_to_stat->package = package; + htab_to_stat->level = GetCurrentTransactionNestLevel(); + variables_stats = lcons((void *)htab_to_stat, variables_stats); + MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -645,7 +813,7 @@ variable_select(PG_FUNCTION_ARGS) } else { - pfree(rstat); + remove_variables_status(&variables_stats, rstat); SRF_RETURN_DONE(funcctx); } } @@ -947,6 +1115,8 @@ remove_package(PG_FUNCTION_ARGS) resetVariablesCache(); + remove_variables_package(&variables_stats, package); + PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); } @@ -1042,6 +1212,7 @@ remove_packages(PG_FUNCTION_ARGS) } resetVariablesCache(); + remove_variables_all(&variables_stats); PG_RETURN_VOID(); } @@ -1213,7 +1384,7 @@ get_packages_stats(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; MemoryContext oldcontext; - HASH_SEQ_STATUS *pstat; + HASH_SEQ_STATUS *rstat; Package *package; if (SRF_IS_FIRSTCALL()) @@ -1238,11 +1409,16 @@ get_packages_stats(PG_FUNCTION_ARGS) */ if (packagesHash) { - pstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); + MemoryContext ctx; + + ctx = MemoryContextSwitchTo(TopTransactionContext); + rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); /* Get packages list */ - hash_seq_init(pstat, packagesHash); + hash_seq_init(rstat, packagesHash); - funcctx->user_fctx = pstat; + funcctx->user_fctx = rstat; + packages_stats = lcons((void *)rstat, packages_stats); + MemoryContextSwitchTo(ctx); } else funcctx->user_fctx = NULL; @@ -1255,9 +1431,9 @@ get_packages_stats(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); /* Get packages list */ - pstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; + rstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; - package = (Package *) hash_seq_search(pstat); + package = (Package *) hash_seq_search(rstat); if (package != NULL) { Datum values[2]; @@ -1289,7 +1465,8 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - pfree(pstat); + packages_stats = list_delete(packages_stats, rstat); + pfree(rstat); SRF_RETURN_DONE(funcctx); } } @@ -1749,6 +1926,7 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); + remove_variables_variable(&variables_stats, (Variable *) object); /* Remove package if it became empty */ if (type == TRANS_VARIABLE && isPackageEmpty(package)) @@ -2168,6 +2346,8 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } + + remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); } /* @@ -2197,6 +2377,9 @@ pgvTransCallback(XactEvent event, void *arg) break; } } + + if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) + freeStatsLists(); } /* @@ -2209,6 +2392,35 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); + + freeStatsLists(); +} + +/* + * Free hash_seq_search scans + */ +static void +freeStatsLists(void) +{ + ListCell *cell; + HASH_SEQ_STATUS *status; + HtabToStat *htab_to_stat; + + foreach(cell, variables_stats) + { + htab_to_stat = (HtabToStat *) lfirst(cell); + hash_seq_term(htab_to_stat->status); + } + + variables_stats = NIL; + + foreach(cell, packages_stats) + { + status = (HASH_SEQ_STATUS *) lfirst(cell); + hash_seq_term(status); + } + + packages_stats = NIL; } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index c4b8c90..2b83ab5 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -614,3 +614,464 @@ SELECT * FROM pgv_list() order by package, name; SELECT pgv_select('test', 'z'); SELECT pgv_free(); + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_remove('test', 'x'); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_remove('test'); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_free(); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_remove('test', 'y'); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_remove('test'); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_free(); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- clear all +SELECT pgv_free(); + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +SELECT pgv_free(); +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test'); + +SELECT pgv_free(); +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test'); + +SELECT pgv_free(); +--- +--- Cursor test #4 +--- + +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +SELECT pgv_free(); +--- +--- Cursor test #5 +--- + +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +--- +--- Cursor test #6 +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'x'); +COMMIT; + +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +COMMIT; + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +FETCH 1 in r3_cur; +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; +FETCH 2 in r2_cur; +FETCH 2 in r3_cur; +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; +FETCH 3 in r2_cur; +FETCH 3 in r3_cur; +ROLLBACK; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +FETCH 1 in r3_cur; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; +FETCH 2 in r2_cur; +FETCH 2 in r3_cur; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; +FETCH 3 in r2_cur; +FETCH 3 in r3_cur; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 2 in r2_cur; +FETCH 3 in r3_cur; +ROLLBACK; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 2 in r2_cur; +FETCH 3 in r3_cur; +COMMIT; + +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'z1'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z1'); +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'z2'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z2'); +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'z3'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z3'); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'z3'); +COMMIT; +SELECT pgv_select('test', 'z3'); + +SELECT pgv_free(); +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +ROLLBACK; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +ROLLBACK; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +SELECT pgv_free(); From 3aa4c56b1190bb3d346c4bed76676c7981d2ba82 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Fri, 13 Nov 2020 20:02:00 +0300 Subject: [PATCH 109/147] Bugfix/issue 27 (#31) * Issue #27: Error with transactional pgv_insert/pgv_remove * Issue #27: Error with transactional pgv_insert/pgv_remove - review * Issue #27: Add fix and improve tests. * Issue #27: Fix for backend termination on transaction commit. * Issue #27: Add fix for leaked hash_seq_search. Fix warning: leaked hash_seq_search scan for hash table. * Issue #27: Add tests and comment. * Issue #27: Add fix for leaked hash_seq_search for pgv_stats. * Issue #27: Add fix for VALGRING failed test. * Issue #27: Add fix for Travis Ci failed tests. * Issue #27: Fix tests for pgv_stats on 9.5 * Fix some comments * Issue #27: Fix list remove generic algo. * Issue #27: Fix savepoint in transaction issue. * Issue #27: Fix savepoint in transaction issue, add sql. * Issue #27: Fix savepoint in transaction issue, add expected result for 9 and 10. * Issue #27: Fix savepoint in transaction issue, add expected result for 9.5. * Issue #27: refactoring. + use uniform algorithm for status lists + rename struct tagHtabToStat to tagVariableStatEntry + use context instead of bunch of args in list workaround routines * Issue #27: Make sanitizer happy. At the moment pg_variables can't handle cursors in a "proper" way. It's possible to remove pg_variables while have active cursor open, which seem to be erratic and couse sanitizer to be unpappy about it. * Issue #27: Improve compatibility check. + Compatibility check for Postgres Professional Enterprise. + Fix for cursors: switch off hash leak message. + Switch off cursor test, because cursors not supported completely. * Issue #27: Make sanitizer happy. Sanitizer did failed on query like "SELECT pgv_select(...) LIMIT 1" * Issue #27: Fix fail under sanitizer with connoll. * Issue #27: Improve compatibility check. Use pg_compatibility_check_no_error function to precheck compatibility in order to free memory. Co-authored-by: Maxim Orlov --- expected/pg_variables_trans.out | 1395 +++++++++++ expected/pg_variables_trans_1.out | 3764 +++++++++++++++++++++++++++++ expected/pg_variables_trans_2.out | 3764 +++++++++++++++++++++++++++++ pg_variables.c | 470 +++- sql/pg_variables_trans.sql | 519 ++++ 5 files changed, 9881 insertions(+), 31 deletions(-) create mode 100644 expected/pg_variables_trans_1.out create mode 100644 expected/pg_variables_trans_2.out diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index d73ef69..8113fc8 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2367,3 +2367,1398 @@ SELECT pgv_free(); (1 row) +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +--SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +--FETCH 1 in r1_cur; +--CLOSE r1_cur; +--SELECT pgv_remove('test', 'x'); +--COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +--FETCH 1 in r1_cur; +--SELECT pgv_remove('test', 'z3'); +--COMMIT; +--SELECT pgv_select('test', 'z3'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Some special cases +--- +-- 1 +BEGIN; +SAVEPOINT comm2; +SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +-- 2 +BEGIN; +SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm2; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,49152) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,49152) +(1 row) + +COMMIT; +-- 3 +BEGIN; +SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +SAVEPOINT comm2; +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,65536) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,65536) +(1 row) + +COMMIT; +-- 4 +BEGIN; +SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,81920) +(1 row) + +SAVEPOINT comm2; +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,81920) +(1 row) + +COMMIT; +-- 5 +BEGIN; +SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,98304) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,98304) +(1 row) + +SAVEPOINT comm2; +COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out new file mode 100644 index 0000000..b6f8132 --- /dev/null +++ b/expected/pg_variables_trans_1.out @@ -0,0 +1,3764 @@ +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; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); +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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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 +-- 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); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(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_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 COMMITTING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +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) + +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) + +-- 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) + +-- 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_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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + +-- 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) + +-- 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) + +-- 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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', '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_select('vars', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + 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; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- 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) + +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) + +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) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + begin +(1 row) + +ROLLBACK; +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; +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +COMMIT; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_free(); -- Check sequential package removal in one subtransaction + pgv_free +---------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans2 | t +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp4; +SAVEPOINT sp5; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp5; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp4; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +COMMIT; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +ERROR: division by zero +SELECT pgv_insert('package', 'errs',row(1), true); + pgv_insert +------------ + +(1 row) + +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT pgv_select('vars4', 'r1', 0); +ERROR: unrecognized package "vars4" +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +COMMIT; +-- Tests for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + pgv_delete +------------ + t +(1 row) + +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 't2', ''::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ERROR; +ERROR: syntax error at or near "ERROR" +LINE 1: ERROR; + ^ +COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); + pgv_exists +------------ + f +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | z | t +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +--SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +--FETCH 1 in r1_cur; +--CLOSE r1_cur; +--SELECT pgv_remove('test', 'x'); +--COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +--FETCH 1 in r1_cur; +--SELECT pgv_remove('test', 'z3'); +--COMMIT; +--SELECT pgv_select('test', 'z3'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Some special cases +--- +-- 1 +BEGIN; +SAVEPOINT comm2; +SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +-- 2 +BEGIN; +SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm2; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +-- 3 +BEGIN; +SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +SAVEPOINT comm2; +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +-- 4 +BEGIN; +SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +SAVEPOINT comm2; +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +COMMIT; +-- 5 +BEGIN; +SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +----------- + (test,0) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +----------- + (test,0) +(1 row) + +SAVEPOINT comm2; +COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_trans_2.out b/expected/pg_variables_trans_2.out new file mode 100644 index 0000000..a07ad7d --- /dev/null +++ b/expected/pg_variables_trans_2.out @@ -0,0 +1,3764 @@ +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; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); +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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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 +-- 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); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(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_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 COMMITTING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +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) + +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) + +-- 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) + +-- 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_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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + +-- 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) + +-- 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) + +-- 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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', '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_select('vars', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + 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; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- 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) + +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) + +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) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + begin +(1 row) + +ROLLBACK; +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; +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +COMMIT; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_free(); -- Check sequential package removal in one subtransaction + pgv_free +---------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans2 | t +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp4; +SAVEPOINT sp5; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp5; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp4; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +COMMIT; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +ERROR: division by zero +SELECT pgv_insert('package', 'errs',row(1), true); + pgv_insert +------------ + +(1 row) + +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT pgv_select('vars4', 'r1', 0); +ERROR: unrecognized package "vars4" +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +COMMIT; +-- Tests for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + pgv_delete +------------ + t +(1 row) + +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 't2', ''::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ERROR; +ERROR: syntax error at or near "ERROR" +LINE 1: ERROR; + ^ +COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); + pgv_exists +------------ + f +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | z | t +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +--SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +--FETCH 1 in r1_cur; +--CLOSE r1_cur; +--SELECT pgv_remove('test', 'x'); +--COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +--FETCH 1 in r1_cur; +--SELECT pgv_remove('test', 'z3'); +--COMMIT; +--SELECT pgv_select('test', 'z3'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Some special cases +--- +-- 1 +BEGIN; +SAVEPOINT comm2; +SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,24576) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,24576) +(1 row) + +COMMIT; +-- 2 +BEGIN; +SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm2; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,40960) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,40960) +(1 row) + +COMMIT; +-- 3 +BEGIN; +SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +SAVEPOINT comm2; +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,57344) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,57344) +(1 row) + +COMMIT; +-- 4 +BEGIN; +SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,73728) +(1 row) + +SAVEPOINT comm2; +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,73728) +(1 row) + +COMMIT; +-- 5 +BEGIN; +SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,90112) +(1 row) + +FETCH 1 in r2_cur; + pgv_stats +-------------- + (test,90112) +(1 row) + +SAVEPOINT comm2; +COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index ee1fa3d..4bd6bdd 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -10,11 +10,13 @@ #include "postgres.h" #include "fmgr.h" #include "funcapi.h" +#include "miscadmin.h" #include "access/htup_details.h" #include "access/xact.h" #include "catalog/pg_type.h" #include "parser/scansup.h" +#include "storage/proc.h" #include "utils/builtins.h" #include "utils/datum.h" #include "utils/lsyscache.h" @@ -129,6 +131,313 @@ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; +/* + * List to store all the running hash_seq_search, variable and package scan for + * hash table. + * + * NOTE: In function variable_select we use hash_seq_search to find next tuple. + * So, in case user do not get all the data from set at once (use cursors or + * LIMIT) we have to call hash_seq_term to not to leak hash_seq_search scans. + * + * For doing this, we alloc all of the rstats in the TopTransactionContext and + * save pointers to the rstats into list. Once transaction ended (commited or + * aborted) we clear all the "active" hash_seq_search by calling hash_seq_term. + * + * TopTransactionContext is handy here, because it would not be reset by the + * time pgvTransCallback is called. + */ +static List *variables_stats = NIL; +static List *packages_stats = NIL; + +typedef struct tagVariableStatEntry +{ + HTAB *hash; + HASH_SEQ_STATUS *status; + Variable *variable; + Package *package; + int level; +} VariableStatEntry; + +typedef struct tagPackageStatEntry +{ + HASH_SEQ_STATUS *status; + int level; +} PackageStatEntry; + +/* + * Compare functions for VariableStatEntry and PackageStatEntry members. + */ +static bool +VariableStatEntry_status_eq(void *entry, void *value) +{ + return ((VariableStatEntry *) entry)->status == (HASH_SEQ_STATUS *) value; +} + +static bool +VariableStatEntry_variable_eq(void *entry, void *value) +{ + return ((VariableStatEntry *) entry)->variable == (Variable *) value; +} + +static bool +VariableStatEntry_package_eq(void *entry, void *value) +{ + return ((VariableStatEntry *) entry)->package == (Package *) value; +} + +static bool +VariableStatEntry_eq_all(void *entry, void *value) +{ + return true; +} + +static bool +VariableStatEntry_level_eq(void *entry, void *value) +{ + return ((VariableStatEntry *) entry)->level == *(int *) value; +} + +static bool +PackageStatEntry_status_eq(void *entry, void *value) +{ + return ((PackageStatEntry *) entry)->status == (HASH_SEQ_STATUS *) value; +} + +static bool +PackageStatEntry_level_eq(void *entry, void *value) +{ + return ((PackageStatEntry *) entry)->level == *(int *) value; +} + +/* + * VariableStatEntry and PackageStatEntry status member getters. + */ +static HASH_SEQ_STATUS * +VariableStatEntry_status_ptr(void *entry) +{ + return ((VariableStatEntry *) entry)->status; +} + +static HASH_SEQ_STATUS * +PackageStatEntry_status_ptr(void *entry) +{ + return ((PackageStatEntry *) entry)->status; +} + +/* + * Generic remove_if algorithm. + * + * For every item in the list: + * 1. Comapare item with value by eq function call. + * 2. If eq return true, then step 3, else goto 7. + * 3. Delete item from list. + * 4. If term is true, call hash_seq_term. + * 5. Free memory. + * 6. If match_first if true return. + * 7. Fetch next item. + * + */ +typedef struct tagRemoveIfContext +{ + List **list; /* target list */ + void *value; /* value to compare with */ + bool (*eq)(void *, void *); /* list item eq to value func */ + HASH_SEQ_STATUS * (*getter)(void *); /* status getter */ + bool match_first; /* return on first match */ + bool term; /* hash_seq_term on match */ +} RemoveIfContext; + +static void +list_remove_if(RemoveIfContext ctx) +{ +#if (PG_VERSION_NUM < 130000) + ListCell *cell, *next, *prev = NULL; + void *entry = NULL; + + for (cell = list_head(*ctx.list); cell; cell = next) + { + entry = lfirst(cell); + next = lnext(cell); + + if (ctx.eq(entry, ctx.value)) + { + *ctx.list = list_delete_cell(*ctx.list, cell, prev); + + if (ctx.term) + hash_seq_term(ctx.getter(entry)); + + pfree(ctx.getter(entry)); + pfree(entry); + + if (ctx.match_first) + return; + } + else + { + prev = cell; + } + } +#else + /* + * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 + * + * Version >= 13 have different lists interface. + */ + ListCell *cell; + void *entry = NULL; + + foreach(cell, *ctx.list) + { + entry = lfirst(cell); + + if (ctx.eq(entry, ctx.value)) + { + *ctx.list = foreach_delete_current(*ctx.list, cell); + + if (ctx.term) + hash_seq_term(ctx.getter(entry)); + + pfree(ctx.getter(entry)); + pfree(entry); + + if (ctx.match_first) + return; + } + } +#endif +} + +/* + * Remove first entry for status. + */ +static void +remove_variables_status(List **list, HASH_SEQ_STATUS *status) +{ + RemoveIfContext ctx = + { + .list = list, + .value = status, + .eq = VariableStatEntry_status_eq, + .getter = VariableStatEntry_status_ptr, + .match_first = true, + .term = false + }; + list_remove_if(ctx); +} + +/* + * Remove first entry for variable. + */ +static void +remove_variables_variable(List **list, Variable *variable) +{ + /* + * It may be more than one item in the list for each variable in case of + * cursor. So match_first is false here. + */ + RemoveIfContext ctx = + { + .list = list, + .value = variable, + .eq = VariableStatEntry_variable_eq, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = true + }; + list_remove_if(ctx); +} + +/* + * Remove all the entries for package. + */ +static void +remove_variables_package(List **list, Package *package) +{ + RemoveIfContext ctx = + { + .list = list, + .value = package, + .eq = VariableStatEntry_package_eq, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = true + }; + list_remove_if(ctx); +} + +/* + * Remove all the entries for level. + */ +static void +remove_variables_level(List **list, int level) +{ + RemoveIfContext ctx = + { + .list = list, + .value = &level, + .eq = VariableStatEntry_level_eq, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = false + }; + list_remove_if(ctx); +} + +/* + * Delete variables stats list. + */ +static void +remove_variables_all(List **list) +{ + RemoveIfContext ctx = + { + .list = list, + .value = NULL, + .eq = VariableStatEntry_eq_all, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = true + }; + list_remove_if(ctx); +} + +/* + * Remove first entrie with status for packages list. + */ +static void +remove_packages_status(List **list, HASH_SEQ_STATUS *status) +{ + RemoveIfContext ctx = + { + .list = list, + .value = status, + .eq = PackageStatEntry_status_eq, + .getter = PackageStatEntry_status_ptr, + .match_first = true, + .term = false + }; + list_remove_if(ctx); +} + +/* + * Remove all the entries with level for packages list. + */ +static void +remove_packages_level(List **list, int level) +{ + RemoveIfContext ctx = + { + .list = list, + .value = &level, + .eq = PackageStatEntry_level_eq, + .getter = PackageStatEntry_status_ptr, + .match_first = false, + .term = true + }; + list_remove_if(ctx); +} + +static void freeStatsLists(void); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -595,30 +904,31 @@ variable_select(PG_FUNCTION_ARGS) FuncCallContext *funcctx; HASH_SEQ_STATUS *rstat; HashRecordEntry *item; + text *package_name; + text *var_name; + Package *package; + Variable *variable; - if (SRF_IS_FIRSTCALL()) - { - text *package_name; - text *var_name; - Package *package; - Variable *variable; - MemoryContext oldcontext; - RecordVar *record; + CHECK_ARGS_FOR_NULL(); - CHECK_ARGS_FOR_NULL(); + /* Get arguments */ + package_name = PG_GETARG_TEXT_PP(0); + var_name = PG_GETARG_TEXT_PP(1); - /* Get arguments */ - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); + package = getPackage(package_name, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); - package = getPackage(package_name, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, - true); + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + RecordVar *record; + VariableStatEntry *entry; record = &(GetActualValue(variable).record); - funcctx = SRF_FIRSTCALL_INIT(); - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + oldcontext = MemoryContextSwitchTo(TopTransactionContext); funcctx->tuple_desc = record->tupdesc; @@ -626,6 +936,14 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; + entry = palloc0(sizeof(VariableStatEntry)); + entry->hash = record->rhash; + entry->status = rstat; + entry->variable = variable; + entry->package = package; + entry->level = GetCurrentTransactionNestLevel(); + variables_stats = lcons((void *)entry, variables_stats); + MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -645,7 +963,7 @@ variable_select(PG_FUNCTION_ARGS) } else { - pfree(rstat); + remove_variables_status(&variables_stats, rstat); SRF_RETURN_DONE(funcctx); } } @@ -947,6 +1265,8 @@ remove_package(PG_FUNCTION_ARGS) resetVariablesCache(); + remove_variables_package(&variables_stats, package); + PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); } @@ -1042,6 +1362,7 @@ remove_packages(PG_FUNCTION_ARGS) } resetVariablesCache(); + remove_variables_all(&variables_stats); PG_RETURN_VOID(); } @@ -1213,7 +1534,7 @@ get_packages_stats(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; MemoryContext oldcontext; - HASH_SEQ_STATUS *pstat; + HASH_SEQ_STATUS *rstat; Package *package; if (SRF_IS_FIRSTCALL()) @@ -1238,11 +1559,20 @@ get_packages_stats(PG_FUNCTION_ARGS) */ if (packagesHash) { - pstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); - /* Get packages list */ - hash_seq_init(pstat, packagesHash); + MemoryContext ctx; + PackageStatEntry *entry; - funcctx->user_fctx = pstat; + ctx = MemoryContextSwitchTo(TopTransactionContext); + rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); + /* Get packages list */ + hash_seq_init(rstat, packagesHash); + + funcctx->user_fctx = rstat; + entry = palloc0(sizeof(PackageStatEntry)); + entry->status = rstat; + entry->level = GetCurrentTransactionNestLevel(); + packages_stats = lcons((void *)entry, packages_stats); + MemoryContextSwitchTo(ctx); } else funcctx->user_fctx = NULL; @@ -1255,9 +1585,9 @@ get_packages_stats(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); /* Get packages list */ - pstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; + rstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; - package = (Package *) hash_seq_search(pstat); + package = (Package *) hash_seq_search(rstat); if (package != NULL) { Datum values[2]; @@ -1289,7 +1619,7 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - pfree(pstat); + remove_packages_status(&packages_stats, rstat); SRF_RETURN_DONE(funcctx); } } @@ -1749,6 +2079,7 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); + remove_variables_variable(&variables_stats, (Variable *) object); /* Remove package if it became empty */ if (type == TRANS_VARIABLE && isPackageEmpty(package)) @@ -2131,16 +2462,60 @@ processChanges(Action action) } } +/* + * ATX and connection pooling are not compatible with pg_variables. + */ static void compatibility_check(void) { + /* + * | Edition | ConnPool | ATX | COMPAT_CHECK | + * ------------------------------------------- + * | std 9.6 | no | no | no | + * | std 10 | no | no | yes | + * | std 11 | no | no | yes | + * | std 12 | no | no | yes | + * | std 13 | no | no | yes | + * | ee 9.6 | no | yes | no | + * | ee 10 | no | yes | yes | + * | ee 11 | yes | yes | yes | + * | ee 12 | yes | yes | yes | + * | ee 13 | yes | yes | yes | + */ #ifdef PGPRO_EE -# if (PG_VERSION_NUM < 130000) || \ - ((PG_VERSION_NUM >= 130000) && (defined PGPRO_FEATURE_ATX)) + +# if (PG_VERSION_NUM < 100000) + /* + * This versions does not have dedicated macro to check compatibility. + * So, use simple check here for ATX. + */ if (getNestLevelATX() != 0) - elog(ERROR, "pg_variable extension is not compatible with autonomous transactions and connection pooling"); -# endif -#endif + { + freeStatsLists(); + elog(ERROR, "pg_variables extension is not compatible with " + "autonomous transactions"); + } +# else + /* + * Since ee10 there is PG_COMPATIBILITY_CHECK macro to check compatibility. + * But for some reasons it may not be present at the moment. + * So, if PG_COMPATIBILITY_CHECK macro is not present pg_variables are + * always compatible. + */ +# ifdef PG_COMPATIBILITY_CHECK + { + if (!pg_compatibility_check_no_error()) + freeStatsLists(); + + PG_COMPATIBILITY_CHECK("pg_variables"); + } +# endif /* PG_COMPATIBILITY_CHECK */ +# endif /* PG_VERSION_NUM */ + +# undef ATX_CHECK +# undef CONNPOOL_CHECK + +#endif /* PGPRO_EE */ } /* @@ -2168,6 +2543,9 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } + + remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); + remove_packages_level(&packages_stats, GetCurrentTransactionNestLevel()); } /* @@ -2197,6 +2575,9 @@ pgvTransCallback(XactEvent event, void *arg) break; } } + + if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) + freeStatsLists(); } /* @@ -2209,6 +2590,33 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); + + freeStatsLists(); +} + +/* + * Free hash_seq_search scans + */ +static void +freeStatsLists(void) +{ + ListCell *cell; + + foreach(cell, variables_stats) + { + VariableStatEntry *entry = (VariableStatEntry *) lfirst(cell); + hash_seq_term(entry->status); + } + + variables_stats = NIL; + + foreach(cell, packages_stats) + { + PackageStatEntry *entry = (PackageStatEntry *) lfirst(cell); + hash_seq_term(entry->status); + } + + packages_stats = NIL; } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index c4b8c90..8cb1d2f 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -614,3 +614,522 @@ SELECT * FROM pgv_list() order by package, name; SELECT pgv_select('test', 'z'); SELECT pgv_free(); + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_remove('test', 'x'); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_remove('test'); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +BEGIN; +SELECT pgv_free(); +ROLLBACK; + +SELECT pgv_select('test', 'x'); + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_remove('test', 'y'); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_remove('test'); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); +SELECT pgv_select('test', 'y'); + +BEGIN; +SELECT pgv_free(); +ROLLBACK; + +SELECT pgv_select('test', 'y'); +-- clear all +SELECT pgv_free(); + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +SELECT pgv_free(); +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test'); + +SELECT pgv_free(); +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test'); + +SELECT pgv_free(); +--- +--- Cursor test #4 +--- + +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'y'); + +SELECT pgv_free(); +--- +--- Cursor test #5 +--- + +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +SELECT pgv_remove('test', 'x'); +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +SELECT pgv_remove('test'); +ROLLBACK; +SELECT pgv_select('test', 'x'); + +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +SELECT pgv_free(); +ROLLBACK; +SELECT pgv_select('test', 'x'); + +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'x'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +SELECT pgv_free(); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'x'); + +--- +--- Cursor test #6 +--- +--SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +--FETCH 1 in r1_cur; +--CLOSE r1_cur; +--SELECT pgv_remove('test', 'x'); +--COMMIT; +SELECT pgv_free(); +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; +SELECT pgv_select('test', 'x') LIMIT 2; +SELECT pgv_select('test', 'x') LIMIT 3; +COMMIT; + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; +SELECT pgv_select('test', 'y') LIMIT 2; +SELECT pgv_select('test', 'y') LIMIT 3; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +FETCH 1 in r3_cur; +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; +FETCH 2 in r2_cur; +FETCH 2 in r3_cur; +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; +FETCH 3 in r2_cur; +FETCH 3 in r3_cur; +ROLLBACK; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +FETCH 1 in r3_cur; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; +FETCH 2 in r2_cur; +FETCH 2 in r3_cur; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; +FETCH 3 in r2_cur; +FETCH 3 in r3_cur; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 2 in r2_cur; +FETCH 3 in r3_cur; +ROLLBACK; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; +FETCH 2 in r2_cur; +FETCH 3 in r3_cur; +COMMIT; + +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; +SELECT pgv_remove('test', 'z1'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z1'); +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +SELECT pgv_remove('test', 'z2'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z2'); +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; +CLOSE r1_cur; +SELECT pgv_remove('test', 'z3'); +FETCH 1 in r1_cur; +ROLLBACK; +SELECT pgv_select('test', 'z3'); + +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +--FETCH 1 in r1_cur; +--SELECT pgv_remove('test', 'z3'); +--COMMIT; +--SELECT pgv_select('test', 'z3'); + +SELECT pgv_free(); +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; +ROLLBACK TO SAVEPOINT sp1; +COMMIT; + +--- +--- Test cases for pgv_stats +--- +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +ROLLBACK; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +ROLLBACK; + +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); + +SELECT pgv_free(); + +--- +--- Some special cases +--- +-- 1 +BEGIN; +SAVEPOINT comm2; +SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +-- 2 +BEGIN; +SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); +SAVEPOINT comm2; +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +-- 3 +BEGIN; +SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +SAVEPOINT comm2; +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +COMMIT; + +-- 4 +BEGIN; +SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +SAVEPOINT comm2; +FETCH 1 in r2_cur; +COMMIT; + +-- 5 +BEGIN; +SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); +DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +FETCH 1 in r1_cur; +FETCH 1 in r2_cur; +SAVEPOINT comm2; +COMMIT; + +SELECT pgv_free(); From 33a29a046bab830c0117745505629fe4c8a437df Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 16 Nov 2020 11:31:12 +0300 Subject: [PATCH 110/147] Revert "Merge branch 'master' into stable" This reverts commit 19e5a77db4fc4fe308e3dc0fef559679e809a47e, reversing changes made to 5af86290a8afae3cf1fbbb7a86ac8d4a5bc3b9e9. --- .travis.yml | 13 +- Dockerfile.tmpl | 2 +- expected/pg_variables_trans.out | 1286 ---------- expected/pg_variables_trans_1.out | 3655 ----------------------------- pg_variables.c | 262 +-- sql/pg_variables_trans.sql | 461 ---- 6 files changed, 29 insertions(+), 5650 deletions(-) delete mode 100644 expected/pg_variables_trans_1.out diff --git a/.travis.yml b/.travis.yml index 5882736..7edda6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +sudo: required + language: c services: @@ -16,22 +18,13 @@ notifications: on_failure: always env: - - PG_VERSION=13 LEVEL=nightmare - - PG_VERSION=13 LEVEL=hardcore - - PG_VERSION=13 - # - PG_VERSION=12 LEVEL=nightmare - - PG_VERSION=12 LEVEL=hardcore - - PG_VERSION=12 - # - PG_VERSION=11 LEVEL=nightmare + - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 -# XXX: consider fixing nightmare mode matrix: allow_failures: - env: PG_VERSION=11 LEVEL=nightmare - - env: PG_VERSION=12 LEVEL=nightmare - - env: PG_VERSION=13 LEVEL=nightmare diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 0bcd176..1760377 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -5,7 +5,7 @@ RUN apk add --no-cache \ openssl curl \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ - zlib-dev libedit-dev linux-headers \ + zlib-dev libedit-dev \ clang clang-analyzer; # Install fresh valgrind diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 2ce8138..d73ef69 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2367,1289 +2367,3 @@ SELECT pgv_free(); (1 row) --- Variables should be rollbackable if transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - ---- ---- Variables should not be rollbackable if not transactional ---- --- case 1 (remove var) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 2 (remove pack) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 3 (free) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- clear all -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #1 (remove var) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "y" -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #2 (remove pack) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #3 (free) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #4 ---- --- non transactional, remove var -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, remove pac -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, free -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional, remove var -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, remove pack -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, free -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #5 ---- --- non transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - ---- ---- Cursor test #6 ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -COMMIT; ---- ---- Tests for "leaked hash_seq_search scan for hash table" ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; ---- ---- Some special cases ---- --- take #1 -SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z1'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z1" -ROLLBACK; -SELECT pgv_select('test', 'z1'); - pgv_select ------------- - (2,2) -(1 row) - --- take #2 -SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z2'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z2" -ROLLBACK; -SELECT pgv_select('test', 'z2'); -ERROR: unrecognized variable "z2" -SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - --- take #3 -SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z3" -ROLLBACK; -SELECT pgv_select('test', 'z3'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -COMMIT; -SELECT pgv_select('test', 'z3'); -ERROR: unrecognized variable "z3" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- take #4 -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -SAVEPOINT sp1; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; ---- ---- Test cases for pgv_stats ---- -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); -ERROR: there is a record in the variable "y" with same key -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); -ERROR: variable "x" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out deleted file mode 100644 index bc55b84..0000000 --- a/expected/pg_variables_trans_1.out +++ /dev/null @@ -1,3655 +0,0 @@ -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; -CREATE TABLE tab (id int, t varchar); -INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); -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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -RELEASE comm; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK to comm2; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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 --- 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); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'text value'::text); - pgv_set ---------- - -(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_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 COMMITTING SUBTRANSACTION -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - after savepoint sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------------- - before savepoint sp1 -(1 row) - -COMMIT; -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK TO sp1; -COMMIT; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -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) - -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) - --- 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) - --- 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_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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - --- 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) - --- 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) - --- 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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('vars', '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_select('vars', 'r1'); -ERROR: unrecognized variable "r1" -SELECT pgv_select('vars', 'r2'); - pgv_select ------------- - (6,str44) -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION -SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - 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; -SAVEPOINT sp1; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp1; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- 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) - -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) - -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) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp5; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -RELEASE sp4; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp3; -SELECT pgv_select('vars3', 'r1'); - pgv_select --------------------------------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ----------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -SELECT pgv_set('vars', 'any1', 'outer'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'begin'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'sp2'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp3; -SAVEPOINT sp4; -SELECT pgv_set('vars', 'any1', 'sp4'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -RELEASE sp4; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp3; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - begin -(1 row) - -ROLLBACK; -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; --- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK -SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -ROLLBACK; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - t -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -COMMIT; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -COMMIT; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_free(); -- Check sequential package removal in one subtransaction - pgv_free ----------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans2 | t -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SAVEPOINT sp3; -SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp4; -SAVEPOINT sp5; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp5; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp4; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -BEGIN; -SELECT pgv_set('vars', 'trans1', 'package created'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -COMMIT; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable -SELECT pgv_insert('package', 'errs',row(n), true) -FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; -ERROR: division by zero -SELECT pgv_insert('package', 'errs',row(1), true); - pgv_insert ------------- - -(1 row) - --- Variable should not exists in case when error occurs during creation -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -ERROR: could not identify a hash function for type unknown -SELECT pgv_select('vars4', 'r1', 0); -ERROR: unrecognized package "vars4" --- If variable created and removed in same transaction level, --- it should be totally removed and should not be present --- in changes list and cache. -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -RELEASE comm; -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -COMMIT; --- Tests for PGPRO-2440 -SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -BEGIN; -SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -COMMIT; -SELECT pgv_delete('vars3', 'r3', 3); - pgv_delete ------------- - t -(1 row) - -BEGIN; -SELECT pgv_set('vars1', 't1', ''::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars2', 't2', ''::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ERROR; -ERROR: syntax error at or near "ERROR" -LINE 1: ERROR; - ^ -COMMIT; -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SAVEPOINT sp_to_rollback; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -ROLLBACK TO sp_to_rollback; -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - --- Package should exist after rollback if it contains regular variable -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - --- Package should not exist if it becomes empty in rolled back transaction -BEGIN; -SAVEPOINT comm2; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK TO comm2; -SELECT pgv_exists('vars'); - pgv_exists ------------- - f -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be insertable after pgv_remove (variable) -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_remove (package) -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_free -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | z | t -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be rollbackable if transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - ---- ---- Variables should not be rollbackable if not transactional ---- --- case 1 (remove var) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 2 (remove pack) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 3 (free) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- clear all -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #1 (remove var) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "y" -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #2 (remove pack) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #3 (free) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #4 ---- --- non transactional, remove var -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, remove pac -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, free -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional, remove var -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, remove pack -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, free -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #5 ---- --- non transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - ---- ---- Cursor test #6 ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -COMMIT; ---- ---- Tests for "leaked hash_seq_search scan for hash table" ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; ---- ---- Some special cases ---- --- take #1 -SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z1'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z1" -ROLLBACK; -SELECT pgv_select('test', 'z1'); - pgv_select ------------- - (2,2) -(1 row) - --- take #2 -SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z2'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z2" -ROLLBACK; -SELECT pgv_select('test', 'z2'); -ERROR: unrecognized variable "z2" -SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - --- take #3 -SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z3" -ROLLBACK; -SELECT pgv_select('test', 'z3'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -COMMIT; -SELECT pgv_select('test', 'z3'); -ERROR: unrecognized variable "z3" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- take #4 -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -SAVEPOINT sp1; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; ---- ---- Test cases for pgv_stats ---- -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); -ERROR: there is a record in the variable "y" with same key -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); -ERROR: variable "x" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/pg_variables.c b/pg_variables.c index 9e83352..ee1fa3d 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -129,165 +129,6 @@ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; -/* - * List to store all the running hash_seq_search, variable and package scan for - * hash table. - * - * NOTE: In function variable_select we use hash_seq_search to find next tuple. - * So, in case user do not get all the data from set at once (use cursors or - * LIMIT) we have to call hash_seq_term to not to leak hash_seq_search scans. - * - * For doing this, we alloc all of the rstats in the TopTransactionContext and - * save pointers to the rstats into list. Once transaction ended (commited or - * aborted) we clear all the "active" hash_seq_search by calling hash_seq_term. - * - * TopTransactionContext is handy here, because it would not be reset by the - * time pgvTransCallback is called. - */ -static List *variables_stats = NIL; -static List *packages_stats = NIL; - -typedef struct tagHtabToStat { - HTAB *hash; - HASH_SEQ_STATUS *status; - Variable *variable; - Package *package; - int level; -} HtabToStat; - -/* - * A bunch of comp functions for HtabToStat members here. - */ -static bool -HtabToStat_status_eq(HtabToStat *entry, void *value) -{ - return entry->status == (HASH_SEQ_STATUS *) value; -} - -static bool -HtabToStat_variable_eq(HtabToStat *entry, void *value) -{ - return entry->variable == (Variable *) value; -} - -static bool -HtabToStat_package_eq(HtabToStat *entry, void *value) -{ - return entry->package == (Package *) value; -} - -static bool -HtabToStat_eq_all(HtabToStat *entry, void *value) -{ - return true; -} - -static bool -HtabToStat_level_eq(HtabToStat *entry, void *value) -{ - return entry->level == *(int *) value; -} - -/* - * Generic remove_if algorithm for HtabToStat. - * - * 'eq' - is a function pointer used to compare list entries to the 'value'. - * 'match_first' - if true return on first match. - */ -static void -HtabToStat_remove_if(List **l, void *value, - bool (*eq)(HtabToStat *, void *), - bool match_first) -{ -#if (PG_VERSION_NUM < 130000) - ListCell *cell, *next, *prev = NULL; - HtabToStat *entry = NULL; - - for (cell = list_head(*l); cell; cell = next) - { - entry = (HtabToStat *) lfirst(cell); - next = lnext(cell); - - if (eq(entry, value)) - { - *l = list_delete_cell(*l, cell, prev); - pfree(entry->status); - pfree(entry); - - if (match_first) - return; - } - else - { - prev = cell; - } - } -#else - /* - * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 - * - * Version > 13 have different lists interface. - */ - ListCell *cell; - HtabToStat *entry = NULL; - - foreach(cell, *l) - { - entry = (HtabToStat *) lfirst(cell); - - if (eq(entry, value)) - *l = foreach_delete_current(*l, cell); - } -#endif -} - -/* - * Remove first entry for status. - */ -static void -remove_variables_status(List **l, HASH_SEQ_STATUS *status) -{ - HtabToStat_remove_if(l, status, HtabToStat_status_eq, true); -} - -/* - * Remove first entry for variable. - */ -static void -remove_variables_variable(List **l, Variable *variable) -{ - HtabToStat_remove_if(l, variable, HtabToStat_variable_eq, true); -} - -/* - * Remove all the entries for package. - */ -static void -remove_variables_package(List **l, Package *package) -{ - HtabToStat_remove_if(l, package, HtabToStat_package_eq, false); -} - -/* - * Remove all the entries for level. - */ -static void -remove_variables_level(List **l, int level) -{ - HtabToStat_remove_if(l, &level, HtabToStat_level_eq, false); -} - -/* - * Remove all. - */ -static void -remove_variables_all(List **l) -{ - HtabToStat_remove_if(l, NULL, HtabToStat_eq_all, false); - *l = NIL; -} - -static void freeStatsLists(void); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -754,31 +595,30 @@ variable_select(PG_FUNCTION_ARGS) FuncCallContext *funcctx; HASH_SEQ_STATUS *rstat; HashRecordEntry *item; - text *package_name; - text *var_name; - Package *package; - Variable *variable; - CHECK_ARGS_FOR_NULL(); + if (SRF_IS_FIRSTCALL()) + { + text *package_name; + text *var_name; + Package *package; + Variable *variable; + MemoryContext oldcontext; + RecordVar *record; - /* Get arguments */ - package_name = PG_GETARG_TEXT_PP(0); - var_name = PG_GETARG_TEXT_PP(1); + CHECK_ARGS_FOR_NULL(); - package = getPackage(package_name, true); - variable = getVariableInternal(package, var_name, RECORDOID, true, - true); + /* Get arguments */ + package_name = PG_GETARG_TEXT_PP(0); + var_name = PG_GETARG_TEXT_PP(1); - if (SRF_IS_FIRSTCALL()) - { - MemoryContext oldcontext; - RecordVar *record; - HtabToStat *htab_to_stat; + package = getPackage(package_name, true); + variable = getVariableInternal(package, var_name, RECORDOID, true, + true); record = &(GetActualValue(variable).record); - funcctx = SRF_FIRSTCALL_INIT(); - oldcontext = MemoryContextSwitchTo(TopTransactionContext); + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = record->tupdesc; @@ -786,14 +626,6 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; - htab_to_stat = palloc0(sizeof(HtabToStat)); - htab_to_stat->hash = record->rhash; - htab_to_stat->status = rstat; - htab_to_stat->variable = variable; - htab_to_stat->package = package; - htab_to_stat->level = GetCurrentTransactionNestLevel(); - variables_stats = lcons((void *)htab_to_stat, variables_stats); - MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -813,7 +645,7 @@ variable_select(PG_FUNCTION_ARGS) } else { - remove_variables_status(&variables_stats, rstat); + pfree(rstat); SRF_RETURN_DONE(funcctx); } } @@ -1115,8 +947,6 @@ remove_package(PG_FUNCTION_ARGS) resetVariablesCache(); - remove_variables_package(&variables_stats, package); - PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); } @@ -1212,7 +1042,6 @@ remove_packages(PG_FUNCTION_ARGS) } resetVariablesCache(); - remove_variables_all(&variables_stats); PG_RETURN_VOID(); } @@ -1384,7 +1213,7 @@ get_packages_stats(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; MemoryContext oldcontext; - HASH_SEQ_STATUS *rstat; + HASH_SEQ_STATUS *pstat; Package *package; if (SRF_IS_FIRSTCALL()) @@ -1409,16 +1238,11 @@ get_packages_stats(PG_FUNCTION_ARGS) */ if (packagesHash) { - MemoryContext ctx; - - ctx = MemoryContextSwitchTo(TopTransactionContext); - rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); + pstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); /* Get packages list */ - hash_seq_init(rstat, packagesHash); + hash_seq_init(pstat, packagesHash); - funcctx->user_fctx = rstat; - packages_stats = lcons((void *)rstat, packages_stats); - MemoryContextSwitchTo(ctx); + funcctx->user_fctx = pstat; } else funcctx->user_fctx = NULL; @@ -1431,9 +1255,9 @@ get_packages_stats(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); /* Get packages list */ - rstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; + pstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; - package = (Package *) hash_seq_search(rstat); + package = (Package *) hash_seq_search(pstat); if (package != NULL) { Datum values[2]; @@ -1465,8 +1289,7 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - packages_stats = list_delete(packages_stats, rstat); - pfree(rstat); + pfree(pstat); SRF_RETURN_DONE(funcctx); } } @@ -1926,7 +1749,6 @@ removeObject(TransObject *object, TransObjectType type) /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); - remove_variables_variable(&variables_stats, (Variable *) object); /* Remove package if it became empty */ if (type == TRANS_VARIABLE && isPackageEmpty(package)) @@ -2346,8 +2168,6 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } - - remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); } /* @@ -2377,9 +2197,6 @@ pgvTransCallback(XactEvent event, void *arg) break; } } - - if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) - freeStatsLists(); } /* @@ -2392,35 +2209,6 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); - - freeStatsLists(); -} - -/* - * Free hash_seq_search scans - */ -static void -freeStatsLists(void) -{ - ListCell *cell; - HASH_SEQ_STATUS *status; - HtabToStat *htab_to_stat; - - foreach(cell, variables_stats) - { - htab_to_stat = (HtabToStat *) lfirst(cell); - hash_seq_term(htab_to_stat->status); - } - - variables_stats = NIL; - - foreach(cell, packages_stats) - { - status = (HASH_SEQ_STATUS *) lfirst(cell); - hash_seq_term(status); - } - - packages_stats = NIL; } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 2b83ab5..c4b8c90 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -614,464 +614,3 @@ SELECT * FROM pgv_list() order by package, name; SELECT pgv_select('test', 'z'); SELECT pgv_free(); - --- Variables should be rollbackable if transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); -SELECT pgv_select('test', 'x'); - -BEGIN; -SELECT pgv_remove('test', 'x'); -ROLLBACK; - -SELECT pgv_select('test', 'x'); - -BEGIN; -SELECT pgv_remove('test'); -ROLLBACK; - -SELECT pgv_select('test', 'x'); - -BEGIN; -SELECT pgv_free(); -ROLLBACK; - -SELECT pgv_select('test', 'x'); - ---- ---- Variables should not be rollbackable if not transactional ---- --- case 1 (remove var) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); -SELECT pgv_select('test', 'y'); - -BEGIN; -SELECT pgv_remove('test', 'y'); -ROLLBACK; - -SELECT pgv_select('test', 'y'); --- case 2 (remove pack) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); -SELECT pgv_select('test', 'y'); - -BEGIN; -SELECT pgv_remove('test'); -ROLLBACK; - -SELECT pgv_select('test', 'y'); --- case 3 (free) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); -SELECT pgv_select('test', 'y'); - -BEGIN; -SELECT pgv_free(); -ROLLBACK; - -SELECT pgv_select('test', 'y'); --- clear all -SELECT pgv_free(); - ---- ---- Cursors test #1 (remove var) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'y'); - -SELECT pgv_free(); ---- ---- Cursors test #2 (remove pack) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test'); - -SELECT pgv_free(); ---- ---- Cursors test #3 (free) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test'); - -SELECT pgv_free(); ---- ---- Cursor test #4 ---- - --- non transactional, remove var -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - --- non transactional, remove pac -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - --- non transactional, free -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - --- transactional, remove var -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'y'); - --- transactional, remove pack -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'y'); - --- transactional, free -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'y'); - -SELECT pgv_free(); ---- ---- Cursor test #5 ---- - --- non transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'x'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - --- transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'x'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_free(); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'x'); - ---- ---- Cursor test #6 ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'x'); -COMMIT; - ---- ---- Tests for "leaked hash_seq_search scan for hash table" ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); -SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); -SELECT pgv_select('test', 'x') LIMIT 1; -SELECT pgv_select('test', 'x') LIMIT 2; -SELECT pgv_select('test', 'x') LIMIT 3; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; -SELECT pgv_select('test', 'x') LIMIT 2; -SELECT pgv_select('test', 'x') LIMIT 3; -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; -SELECT pgv_select('test', 'x') LIMIT 2; -SELECT pgv_select('test', 'x') LIMIT 3; -COMMIT; - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); -SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); -SELECT pgv_select('test', 'y') LIMIT 1; -SELECT pgv_select('test', 'y') LIMIT 2; -SELECT pgv_select('test', 'y') LIMIT 3; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; -SELECT pgv_select('test', 'y') LIMIT 2; -SELECT pgv_select('test', 'y') LIMIT 3; -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; -SELECT pgv_select('test', 'y') LIMIT 2; -SELECT pgv_select('test', 'y') LIMIT 3; -COMMIT; - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -FETCH 1 in r3_cur; -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; -FETCH 2 in r2_cur; -FETCH 2 in r3_cur; -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; -FETCH 3 in r2_cur; -FETCH 3 in r3_cur; -ROLLBACK; - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -FETCH 1 in r3_cur; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; -FETCH 2 in r2_cur; -FETCH 2 in r3_cur; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; -FETCH 3 in r2_cur; -FETCH 3 in r3_cur; -COMMIT; - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -FETCH 2 in r2_cur; -FETCH 3 in r3_cur; -ROLLBACK; - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; -FETCH 2 in r2_cur; -FETCH 3 in r3_cur; -COMMIT; - ---- ---- Some special cases ---- --- take #1 -SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'z1'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'z1'); --- take #2 -SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'z2'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'z2'); -SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); --- take #3 -SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'z3'); -FETCH 1 in r1_cur; -ROLLBACK; -SELECT pgv_select('test', 'z3'); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; -SELECT pgv_remove('test', 'z3'); -COMMIT; -SELECT pgv_select('test', 'z3'); - -SELECT pgv_free(); --- take #4 -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 1 in r1_cur; -ROLLBACK TO SAVEPOINT sp1; -COMMIT; - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 2 in r1_cur; -ROLLBACK TO SAVEPOINT sp1; -COMMIT; - -BEGIN; -SAVEPOINT sp1; -SELECT pgv_select('test', 'x') LIMIT 1; -ROLLBACK TO SAVEPOINT sp1; -COMMIT; - ---- ---- Test cases for pgv_stats ---- -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -COMMIT; - -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -ROLLBACK; - -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); - -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -COMMIT; - -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; -FETCH 1 in r2_cur; -ROLLBACK; - -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); - -SELECT pgv_free(); From 2c47f47054fc367806c89dce49ecce11ebf574d5 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 16 Nov 2020 15:58:28 +0300 Subject: [PATCH 111/147] Revert "Revert "Merge branch 'master' into stable"" This reverts commit 33a29a046bab830c0117745505629fe4c8a437df. --- .travis.yml | 13 ++++++++++--- Dockerfile.tmpl | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7edda6c..5882736 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -sudo: required - language: c services: @@ -18,13 +16,22 @@ notifications: on_failure: always env: - - PG_VERSION=11 LEVEL=nightmare + - PG_VERSION=13 LEVEL=nightmare + - PG_VERSION=13 LEVEL=hardcore + - PG_VERSION=13 + # - PG_VERSION=12 LEVEL=nightmare + - PG_VERSION=12 LEVEL=hardcore + - PG_VERSION=12 + # - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - PG_VERSION=10 - PG_VERSION=9.6 - PG_VERSION=9.5 +# XXX: consider fixing nightmare mode matrix: allow_failures: - env: PG_VERSION=11 LEVEL=nightmare + - env: PG_VERSION=12 LEVEL=nightmare + - env: PG_VERSION=13 LEVEL=nightmare diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 1760377..0bcd176 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -5,7 +5,7 @@ RUN apk add --no-cache \ openssl curl \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ - zlib-dev libedit-dev \ + zlib-dev libedit-dev linux-headers \ clang clang-analyzer; # Install fresh valgrind From 8ee0641cfc40deafbb1484fa682718c5b4fb9099 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 7 Dec 2020 15:46:42 +0300 Subject: [PATCH 112/147] Issue #33: fix pg_variables_trans test fail on sparc. Add sort order for pgv_stats call. Do not use exact allocated memory size as checked value. Remove multiple output from pg_variables_trans test. --- expected/pg_variables_trans.out | 155 +- expected/pg_variables_trans_1.out | 3764 ----------------------------- expected/pg_variables_trans_2.out | 3764 ----------------------------- sql/pg_variables_trans.sql | 48 +- 4 files changed, 113 insertions(+), 7618 deletions(-) delete mode 100644 expected/pg_variables_trans_1.out delete mode 100644 expected/pg_variables_trans_2.out diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 8113fc8..2389efa 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3539,6 +3539,16 @@ COMMIT; --- --- Test cases for pgv_stats --- +--- Amount of allocated memory may vary from version to version, as well as from +--- platform to platform. Moreover, postgres versions less than 90600 always +--- show zero allocated memory. So, it's much easier to check if allocated +--- memory size is multiple of 8k since we use ALLOCSET_DEFAULT_INITSIZE +--- (see memutils.h), insted of creating multiple outputs files. +--- +CREATE TEMP VIEW pgv_stats_view(pack, mem_mult) AS + SELECT package, allocated_memory % 8192 as allocated_multiple_8192 + FROM pgv_stats() + ORDER BY 1; SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); pgv_insert ------------ @@ -3552,36 +3562,36 @@ SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); (1 row) BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); ERROR: there is a record in the variable "y" with same key BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) ROLLBACK; @@ -3592,36 +3602,36 @@ ERROR: variable "y" already created as TRANSACTIONAL SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); ERROR: variable "x" already created as TRANSACTIONAL BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); ERROR: variable "y" already created as TRANSACTIONAL BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) ROLLBACK; @@ -3645,18 +3655,18 @@ SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); (1 row) -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; @@ -3669,18 +3679,18 @@ SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); (1 row) SAVEPOINT comm2; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,49152) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,49152) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; @@ -3692,19 +3702,19 @@ SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); (1 row) -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; SAVEPOINT comm2; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,65536) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,65536) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; @@ -3716,19 +3726,19 @@ SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); (1 row) -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,81920) + pack | mem_mult +------+---------- + test | 0 (1 row) SAVEPOINT comm2; FETCH 1 in r2_cur; - pgv_stats --------------- - (test,81920) + pack | mem_mult +------+---------- + test | 0 (1 row) COMMIT; @@ -3740,22 +3750,23 @@ SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); (1 row) -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; - pgv_stats --------------- - (test,98304) + pack | mem_mult +------+---------- + test | 0 (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- - (test,98304) + pack | mem_mult +------+---------- + test | 0 (1 row) SAVEPOINT comm2; COMMIT; +DROP VIEW pgv_stats_view; SELECT pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out deleted file mode 100644 index b6f8132..0000000 --- a/expected/pg_variables_trans_1.out +++ /dev/null @@ -1,3764 +0,0 @@ -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; -CREATE TABLE tab (id int, t varchar); -INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); -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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -RELEASE comm; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK to comm2; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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 --- 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); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'text value'::text); - pgv_set ---------- - -(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_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 COMMITTING SUBTRANSACTION -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - after savepoint sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------------- - before savepoint sp1 -(1 row) - -COMMIT; -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK TO sp1; -COMMIT; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -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) - -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) - --- 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) - --- 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_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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - --- 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) - --- 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) - --- 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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('vars', '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_select('vars', 'r1'); -ERROR: unrecognized variable "r1" -SELECT pgv_select('vars', 'r2'); - pgv_select ------------- - (6,str44) -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION -SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - 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; -SAVEPOINT sp1; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp1; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- 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) - -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) - -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) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp5; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -RELEASE sp4; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp3; -SELECT pgv_select('vars3', 'r1'); - pgv_select --------------------------------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ----------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -SELECT pgv_set('vars', 'any1', 'outer'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'begin'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'sp2'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp3; -SAVEPOINT sp4; -SELECT pgv_set('vars', 'any1', 'sp4'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -RELEASE sp4; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp3; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - begin -(1 row) - -ROLLBACK; -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; --- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK -SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -ROLLBACK; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - t -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -COMMIT; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -COMMIT; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_free(); -- Check sequential package removal in one subtransaction - pgv_free ----------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans2 | t -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SAVEPOINT sp3; -SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp4; -SAVEPOINT sp5; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp5; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp4; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -BEGIN; -SELECT pgv_set('vars', 'trans1', 'package created'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -COMMIT; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable -SELECT pgv_insert('package', 'errs',row(n), true) -FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; -ERROR: division by zero -SELECT pgv_insert('package', 'errs',row(1), true); - pgv_insert ------------- - -(1 row) - --- Variable should not exists in case when error occurs during creation -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -ERROR: could not identify a hash function for type unknown -SELECT pgv_select('vars4', 'r1', 0); -ERROR: unrecognized package "vars4" --- If variable created and removed in same transaction level, --- it should be totally removed and should not be present --- in changes list and cache. -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -RELEASE comm; -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -COMMIT; --- Tests for PGPRO-2440 -SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -BEGIN; -SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -COMMIT; -SELECT pgv_delete('vars3', 'r3', 3); - pgv_delete ------------- - t -(1 row) - -BEGIN; -SELECT pgv_set('vars1', 't1', ''::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars2', 't2', ''::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ERROR; -ERROR: syntax error at or near "ERROR" -LINE 1: ERROR; - ^ -COMMIT; -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SAVEPOINT sp_to_rollback; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -ROLLBACK TO sp_to_rollback; -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - --- Package should exist after rollback if it contains regular variable -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - --- Package should not exist if it becomes empty in rolled back transaction -BEGIN; -SAVEPOINT comm2; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK TO comm2; -SELECT pgv_exists('vars'); - pgv_exists ------------- - f -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be insertable after pgv_remove (variable) -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_remove (package) -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_free -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | z | t -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be rollbackable if transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - ---- ---- Variables should not be rollbackable if not transactional ---- --- case 1 (remove var) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 2 (remove pack) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 3 (free) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- clear all -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #1 (remove var) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "y" -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #2 (remove pack) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #3 (free) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #4 ---- --- non transactional, remove var -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, remove pac -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, free -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional, remove var -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, remove pack -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, free -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #5 ---- --- non transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - ---- ---- Cursor test #6 ---- ---SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); ---BEGIN; ---DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); ---FETCH 1 in r1_cur; ---CLOSE r1_cur; ---SELECT pgv_remove('test', 'x'); ---COMMIT; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Tests for "leaked hash_seq_search scan for hash table" ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; ---- ---- Some special cases ---- --- take #1 -SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z1'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z1" -ROLLBACK; -SELECT pgv_select('test', 'z1'); - pgv_select ------------- - (2,2) -(1 row) - --- take #2 -SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'z2'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: cursor "r1_cur" does not exist -ROLLBACK; -SELECT pgv_select('test', 'z2'); -ERROR: unrecognized variable "z2" -SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - --- take #3 -SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: cursor "r1_cur" does not exist -ROLLBACK; -SELECT pgv_select('test', 'z3'); - pgv_select ------------- - (1,2) -(1 row) - ---BEGIN; ---DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); ---FETCH 1 in r1_cur; ---SELECT pgv_remove('test', 'z3'); ---COMMIT; ---SELECT pgv_select('test', 'z3'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- take #4 -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -SAVEPOINT sp1; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; ---- ---- Test cases for pgv_stats ---- -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); -ERROR: there is a record in the variable "y" with same key -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); -ERROR: variable "x" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Some special cases ---- --- 1 -BEGIN; -SAVEPOINT comm2; -SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; --- 2 -BEGIN; -SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SAVEPOINT comm2; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; --- 3 -BEGIN; -SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -SAVEPOINT comm2; -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; --- 4 -BEGIN; -SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -SAVEPOINT comm2; -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -COMMIT; --- 5 -BEGIN; -SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats ------------ - (test,0) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats ------------ - (test,0) -(1 row) - -SAVEPOINT comm2; -COMMIT; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/expected/pg_variables_trans_2.out b/expected/pg_variables_trans_2.out deleted file mode 100644 index a07ad7d..0000000 --- a/expected/pg_variables_trans_2.out +++ /dev/null @@ -1,3764 +0,0 @@ -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; -CREATE TABLE tab (id int, t varchar); -INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); -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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -RELEASE comm; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK to comm2; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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 --- 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); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'any2', 'text value'::text); - pgv_set ---------- - -(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_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 COMMITTING SUBTRANSACTION -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - after savepoint sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ----------------------- - before savepoint sp1 -(1 row) - -COMMIT; -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK TO sp1; -COMMIT; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -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) - -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) - --- 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) - --- 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_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) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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) - --- 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) - --- 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) - --- 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) - (1,str33) - (2,) - (0,str00) -(4 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (0,str00) -(4 rows) - -ROLLBACK; -SELECT pgv_select('vars3', 'r1'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (5,str55) - (0,str00) -(5 rows) - -SELECT pgv_select('vars3', 'r2'); - pgv_select ------------- - (,strNULL) - (1,str33) - (2,) - (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('vars', '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_select('vars', 'r1'); -ERROR: unrecognized variable "r1" -SELECT pgv_select('vars', 'r2'); - pgv_select ------------- - (6,str44) -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION -SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------------------- - 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; -SAVEPOINT sp1; -SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -RELEASE sp1; -SELECT pgv_get('vars2', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -ROLLBACK; -SELECT pgv_get('vars2', 'any1',NULL::text); -ERROR: unrecognized package "vars2" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- 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) - -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) - -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) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp5; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -RELEASE sp4; -SELECT pgv_select('vars3', 'r1'); - pgv_select ---------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"after savepoint sp1") - (0,str00) -(5 rows) - -ROLLBACK TO sp3; -SELECT pgv_select('vars3', 'r1'); - pgv_select --------------------------------------- - (,strNULL) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (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) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -COMMIT; -SELECT pgv_select('vars3', 'r1'); - pgv_select ----------------------------- - (,strNULL) - (1,str33) - (2,) - (5,"before savepoint sp1") - (0,str00) -(5 rows) - -SELECT pgv_set('vars', 'any1', 'outer'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_set('vars', 'any1', 'begin'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SELECT pgv_set('vars', 'any1', 'sp1'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp2; -SELECT pgv_set('vars', 'any1', 'sp2'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp3; -SAVEPOINT sp4; -SELECT pgv_set('vars', 'any1', 'sp4'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp5; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -RELEASE sp4; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp4 -(1 row) - -ROLLBACK TO sp3; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -RELEASE sp2; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - sp2 -(1 row) - -ROLLBACK TO sp1; -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ---------- - begin -(1 row) - -ROLLBACK; -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; --- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK -SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -ROLLBACK; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - t -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); - pgv_get ------------------ - variable exists -(1 row) - -BEGIN; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -COMMIT; -SELECT pgv_exists('vars', 'any1'); - pgv_exists ------------- - f -(1 row) - -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ - vars3 | r1 | t -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -COMMIT; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_free(); -- Check sequential package removal in one subtransaction - pgv_free ----------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans2 | t -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -ROLLBACK; -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -BEGIN; -SAVEPOINT sp1; -SAVEPOINT sp2; -SAVEPOINT sp3; -SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp4; -SAVEPOINT sp5; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp5; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -RELEASE sp4; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars - vars2 -(2 rows) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -BEGIN; -SELECT pgv_set('vars', 'trans1', 'package created'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); - pgv_set ---------- - -(1 row) - -SELECT * FROM pgv_list() ORDER BY package, name; - package | name | is_transactional ----------+--------+------------------ - vars | trans1 | t -(1 row) - -COMMIT; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - --- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable -SELECT pgv_insert('package', 'errs',row(n), true) -FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; -ERROR: division by zero -SELECT pgv_insert('package', 'errs',row(1), true); - pgv_insert ------------- - -(1 row) - --- Variable should not exists in case when error occurs during creation -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -ERROR: could not identify a hash function for type unknown -SELECT pgv_select('vars4', 'r1', 0); -ERROR: unrecognized package "vars4" --- If variable created and removed in same transaction level, --- it should be totally removed and should not be present --- in changes list and cache. -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_remove('vars', 'any1'); - pgv_remove ------------- - -(1 row) - -RELEASE comm; -SELECT pgv_get('vars', 'any1',NULL::text); -ERROR: unrecognized package "vars" -COMMIT; --- Tests for PGPRO-2440 -SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -BEGIN; -SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -SAVEPOINT comm; -SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); - pgv_insert ------------- - -(1 row) - -COMMIT; -SELECT pgv_delete('vars3', 'r3', 3); - pgv_delete ------------- - t -(1 row) - -BEGIN; -SELECT pgv_set('vars1', 't1', ''::text); - pgv_set ---------- - -(1 row) - -SELECT pgv_set('vars2', 't2', ''::text, true); - pgv_set ---------- - -(1 row) - -SAVEPOINT sp1; -SAVEPOINT sp2; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ERROR; -ERROR: syntax error at or near "ERROR" -LINE 1: ERROR; - ^ -COMMIT; -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SAVEPOINT sp_to_rollback; -SELECT pgv_set('vars', 'any1', 'some value'::text, true); - pgv_set ---------- - -(1 row) - -ROLLBACK TO sp_to_rollback; -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - --- Package should exist after rollback if it contains regular variable -BEGIN; -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - --- Package should not exist if it becomes empty in rolled back transaction -BEGIN; -SAVEPOINT comm2; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK TO comm2; -SELECT pgv_exists('vars'); - pgv_exists ------------- - f -(1 row) - -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- - vars -(1 row) - -COMMIT; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_set('vars', 'any1', 'some value'::text); - pgv_set ---------- - -(1 row) - -BEGIN; -SELECT pgv_remove('vars'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT package FROM pgv_stats() ORDER BY package; - package ---------- -(0 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be insertable after pgv_remove (variable) -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_remove (package) -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | x | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - --- Variables should be insertable after pgv_free -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -ROLLBACK; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | y | t -(1 row) - -BEGIN; -SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) -(1 row) - -SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -COMMIT; -SELECT * FROM pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - test | z | t -(1 row) - -SELECT pgv_select('test', 'z'); - pgv_select ------------- - (1,3) - (2,4) -(2 rows) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- Variables should be rollbackable if transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - ---- ---- Variables should not be rollbackable if not transactional ---- --- case 1 (remove var) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 2 (remove pack) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- case 3 (free) -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -BEGIN; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" --- clear all -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #1 (remove var) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "y" -ROLLBACK; -SELECT pgv_select('test', 'y'); -ERROR: unrecognized variable "y" -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #2 (remove pack) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursors test #3 (free) ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test'); -ERROR: function pgv_select(unknown) does not exist -LINE 1: SELECT pgv_select('test'); - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #4 ---- --- non transactional, remove var -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, remove pac -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- non transactional, free -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional, remove var -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test', 'y'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, remove pack -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - --- transactional, free -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'y'); - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Cursor test #5 ---- --- non transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -ROLLBACK; -SELECT pgv_select('test', 'x'); -ERROR: unrecognized package "test" --- transactional -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test', 'x'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_remove('test'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized package "test" -ROLLBACK; -SELECT pgv_select('test', 'x'); - pgv_select ------------- - (1,2) -(1 row) - ---- ---- Cursor test #6 ---- ---SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); ---BEGIN; ---DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); ---FETCH 1 in r1_cur; ---CLOSE r1_cur; ---SELECT pgv_remove('test', 'x'); ---COMMIT; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Tests for "leaked hash_seq_search scan for hash table" ---- -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'x') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'x') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -SELECT pgv_select('test', 'y') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -SELECT pgv_select('test', 'y') LIMIT 2; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -SELECT pgv_select('test', 'y') LIMIT 3; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r2_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 1 in r3_cur; - pgv_select ------------- - (1,2) -(1 row) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 2 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 3 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -ROLLBACK; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); -DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -FETCH 2 in r2_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -FETCH 3 in r3_cur; - pgv_select ------------- - (1,2) - (2,3) - (3,4) -(3 rows) - -COMMIT; ---- ---- Some special cases ---- --- take #1 -SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -SELECT pgv_remove('test', 'z1'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: unrecognized variable "z1" -ROLLBACK; -SELECT pgv_select('test', 'z1'); - pgv_select ------------- - (2,2) -(1 row) - --- take #2 -SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); -FETCH 1 in r1_cur; - pgv_select ------------- - (2,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'z2'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: cursor "r1_cur" does not exist -ROLLBACK; -SELECT pgv_select('test', 'z2'); -ERROR: unrecognized variable "z2" -SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); - pgv_insert ------------- - -(1 row) - --- take #3 -SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -CLOSE r1_cur; -SELECT pgv_remove('test', 'z3'); - pgv_remove ------------- - -(1 row) - -FETCH 1 in r1_cur; -ERROR: cursor "r1_cur" does not exist -ROLLBACK; -SELECT pgv_select('test', 'z3'); - pgv_select ------------- - (1,2) -(1 row) - ---BEGIN; ---DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); ---FETCH 1 in r1_cur; ---SELECT pgv_remove('test', 'z3'); ---COMMIT; ---SELECT pgv_select('test', 'z3'); -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - --- take #4 -SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 1 in r1_cur; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); -SAVEPOINT sp1; -FETCH 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; -BEGIN; -SAVEPOINT sp1; -SELECT pgv_select('test', 'x') LIMIT 1; - pgv_select ------------- - (1,2) -(1 row) - -ROLLBACK TO SAVEPOINT sp1; -COMMIT; ---- ---- Test cases for pgv_stats ---- -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); -ERROR: there is a record in the variable "y" with same key -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); -ERROR: variable "x" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -COMMIT; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,32768) -(1 row) - -ROLLBACK; -SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); -ERROR: variable "y" already created as TRANSACTIONAL -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - ---- ---- Some special cases ---- --- 1 -BEGIN; -SAVEPOINT comm2; -SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,24576) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,24576) -(1 row) - -COMMIT; --- 2 -BEGIN; -SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -SAVEPOINT comm2; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,40960) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,40960) -(1 row) - -COMMIT; --- 3 -BEGIN; -SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -SAVEPOINT comm2; -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,57344) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,57344) -(1 row) - -COMMIT; --- 4 -BEGIN; -SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,73728) -(1 row) - -SAVEPOINT comm2; -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,73728) -(1 row) - -COMMIT; --- 5 -BEGIN; -SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); - pgv_insert ------------- - -(1 row) - -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); -FETCH 1 in r1_cur; - pgv_stats --------------- - (test,90112) -(1 row) - -FETCH 1 in r2_cur; - pgv_stats --------------- - (test,90112) -(1 row) - -SAVEPOINT comm2; -COMMIT; -SELECT pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 8cb1d2f..0d67ae1 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1037,11 +1037,22 @@ COMMIT; --- --- Test cases for pgv_stats --- +--- Amount of allocated memory may vary from version to version, as well as from +--- platform to platform. Moreover, postgres versions less than 90600 always +--- show zero allocated memory. So, it's much easier to check if allocated +--- memory size is multiple of 8k since we use ALLOCSET_DEFAULT_INITSIZE +--- (see memutils.h), insted of creating multiple outputs files. +--- +CREATE TEMP VIEW pgv_stats_view(pack, mem_mult) AS + SELECT package, allocated_memory % 8192 as allocated_multiple_8192 + FROM pgv_stats() + ORDER BY 1; + SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; COMMIT; @@ -1049,8 +1060,8 @@ COMMIT; SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; ROLLBACK; @@ -1060,8 +1071,8 @@ SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; COMMIT; @@ -1069,8 +1080,8 @@ COMMIT; SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); BEGIN; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; ROLLBACK; @@ -1086,8 +1097,8 @@ SELECT pgv_free(); BEGIN; SAVEPOINT comm2; SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; COMMIT; @@ -1096,8 +1107,8 @@ COMMIT; BEGIN; SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); SAVEPOINT comm2; -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; COMMIT; @@ -1105,8 +1116,8 @@ COMMIT; -- 3 BEGIN; SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; SAVEPOINT comm2; FETCH 1 in r1_cur; FETCH 1 in r2_cur; @@ -1115,8 +1126,8 @@ COMMIT; -- 4 BEGIN; SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; SAVEPOINT comm2; FETCH 1 in r2_cur; @@ -1125,11 +1136,12 @@ COMMIT; -- 5 BEGIN; SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); -DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); -DECLARE r2_cur CURSOR FOR SELECT pgv_stats(); +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; FETCH 1 in r1_cur; FETCH 1 in r2_cur; SAVEPOINT comm2; COMMIT; +DROP VIEW pgv_stats_view; SELECT pgv_free(); From 6a62ec605b5b5cee72cf1697304032641a44f862 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 27 Nov 2020 13:39:54 +0300 Subject: [PATCH 113/147] Use more detailed message in record insert error. In case of inserting a erroneous record in variable show clear messages, if it is amount of fields or fields types are wrong. --- expected/pg_variables.out | 6 +++--- pg_variables_record.c | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index ba0b64a..2e67ab4 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -563,11 +563,11 @@ ERROR: variable "j1" requires "jsonb" value SELECT pgv_insert('vars3', 'r1', tab) FROM tab; ERROR: there is a record in the variable "r1" with same key SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record structure have 3 attributes, but variable "r1" structure have 2. SELECT pgv_insert('vars3', 'r1', row(1, 1)); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record attribute type for attribute number 2 differs from variable "r1" structure. You may need explicit type casts. SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record attribute type for attribute number 1 differs from variable "r1" structure. You may need explicit type casts. SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail ERROR: searching for elements in multidimensional arrays is not supported -- Test variables caching diff --git a/pg_variables_record.c b/pg_variables_record.c index b97ffca..d34d12c 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -188,8 +188,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc) if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("new record structure differs from variable \"%s\" " - "structure", GetName(variable)))); + errmsg("new record structure have %d attributes, but variable " + "\"%s\" structure have %d.", + tupdesc->natts, GetName(variable), record->tupdesc->natts))); /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) @@ -202,8 +203,10 @@ check_attributes(Variable *variable, TupleDesc tupdesc) || (attr1->atttypmod != attr2->atttypmod)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("new record structure differs from variable \"%s\" " - "structure", GetName(variable)))); + errmsg("new record attribute type for attribute number %d " + "differs from variable \"%s\" structure. You may " + "need explicit type casts.", + i + 1, GetName(variable)))); } } From 4408d5adc7304666a1db051379cf71e9ff7677c2 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 27 Nov 2020 14:46:52 +0300 Subject: [PATCH 114/147] Minor refactoring for insert deleted variable. --- pg_variables.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 4bd6bdd..0350a85 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -696,13 +696,15 @@ variable_insert(PG_FUNCTION_ARGS) tupTypmod = HeapTupleHeaderGetTypMod(rec); record = &(GetActualValue(variable).record); - if (!record->tupdesc) + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + + if (!record->tupdesc || variable->is_deleted) { /* * This is the first record for the var_name. Initialize record. */ - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); init_record(record, tupdesc, variable); + variable->is_deleted = false; } else if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || LastTypeId != tupType) @@ -711,16 +713,7 @@ variable_insert(PG_FUNCTION_ARGS) * We need to check attributes of the new row if this is a transient * record type or if last record has different id. */ - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - if (variable->is_deleted) - { - init_record(record, tupdesc, variable); - variable->is_deleted = false; - } - else - { - check_attributes(variable, tupdesc); - } + check_attributes(variable, tupdesc); } LastTypeId = tupType; From 1b37d6629e89d0b3105eb56f1e86ccfd0c794e62 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 2 Dec 2020 16:22:11 +0300 Subject: [PATCH 115/147] Fix error on insert from table #32 --- expected/pg_variables.out | 56 +++++++++++++++++++++++++++++++++ expected/pg_variables_trans.out | 56 +++++++++++++++++++++++++++++++++ pg_variables.c | 23 +++----------- sql/pg_variables.sql | 16 ++++++++++ sql/pg_variables_trans.sql | 18 +++++++++++ 5 files changed, 151 insertions(+), 18 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 2e67ab4..e21ec67 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -929,3 +929,59 @@ SELECT * FROM pgv_list() order by package, name; ---------+------+------------------ (0 rows) +-- Check insert of record with various amount of fields +CREATE TEMP TABLE foo(id int, t text); +INSERT INTO foo VALUES (0, 'str00'); +SELECT pgv_insert('vars', 'r1', row(1, 'str1'::text, 'str2'::text)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r1', foo) FROM foo; +ERROR: new record structure have 2 attributes, but variable "r1" structure have 3. +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', foo) FROM foo; +ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure. You may need explicit type casts. +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (1,str1) +(1 row) + +SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r3', foo) FROM foo; + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r3'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 2389efa..2f46e04 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3773,3 +3773,59 @@ SELECT pgv_free(); (1 row) +--- +--- Test case for issue #32 [PGPRO-4456] +--- +CREATE TEMP TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'); +SELECT pgv_insert('vars', 'r1', row(1, 'str1', 'str2')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'a', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r1', tab) FROM tab; +ERROR: new record structure have 2 attributes, but variable "r1" structure have 3. +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r2', row(1, 'str1'::varchar)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'b', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 0350a85..b75701a 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -121,8 +121,6 @@ static MemoryContext ModuleContext = NULL; static Package *LastPackage = NULL; /* Recent variable */ static Variable *LastVariable = NULL; -/* Recent row type id */ -static Oid LastTypeId = InvalidOid; /* Saved hook values for recall */ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; @@ -706,8 +704,7 @@ variable_insert(PG_FUNCTION_ARGS) init_record(record, tupdesc, variable); variable->is_deleted = false; } - else if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || - LastTypeId != tupType) + else { /* * We need to check attributes of the new row if this is a transient @@ -716,8 +713,6 @@ variable_insert(PG_FUNCTION_ARGS) check_attributes(variable, tupdesc); } - LastTypeId = tupType; - insert_record(variable, rec); /* Release resources */ @@ -742,6 +737,7 @@ variable_update(PG_FUNCTION_ARGS) bool res; Oid tupType; int32 tupTypmod; + TupleDesc tupdesc = NULL; /* Checks */ CHECK_ARGS_FOR_NULL(); @@ -794,17 +790,9 @@ variable_update(PG_FUNCTION_ARGS) tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); - if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || - LastTypeId != tupType) - { - TupleDesc tupdesc = NULL; - - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - check_attributes(variable, tupdesc); - ReleaseTupleDesc(tupdesc); - } - - LastTypeId = tupType; + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + check_attributes(variable, tupdesc); + ReleaseTupleDesc(tupdesc); res = update_record(variable, rec); @@ -1330,7 +1318,6 @@ resetVariablesCache(void) /* Remove package and variable from cache */ LastPackage = NULL; LastVariable = NULL; - LastTypeId = InvalidOid; } /* diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index a9fdbbd..bad7976 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -259,3 +259,19 @@ SELECT pgv_free(); SELECT pgv_exists('vars'); SELECT * FROM pgv_list() order by package, name; +-- Check insert of record with various amount of fields +CREATE TEMP TABLE foo(id int, t text); +INSERT INTO foo VALUES (0, 'str00'); + +SELECT pgv_insert('vars', 'r1', row(1, 'str1'::text, 'str2'::text)); +SELECT pgv_select('vars', 'r1'); +SELECT pgv_insert('vars', 'r1', foo) FROM foo; +SELECT pgv_select('vars', 'r1'); + +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); +SELECT pgv_insert('vars', 'r2', foo) FROM foo; +SELECT pgv_select('vars', 'r2'); + +SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); +SELECT pgv_insert('vars', 'r3', foo) FROM foo; +SELECT pgv_select('vars', 'r3'); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 0d67ae1..72d9559 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1145,3 +1145,21 @@ COMMIT; DROP VIEW pgv_stats_view; SELECT pgv_free(); + +--- +--- Test case for issue #32 [PGPRO-4456] +--- +CREATE TEMP TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'); + +SELECT pgv_insert('vars', 'r1', row(1, 'str1', 'str2')); +SELECT pgv_insert('vars', 'a', tab) FROM tab; +SELECT pgv_insert('vars', 'r1', tab) FROM tab; +SELECT pgv_select('vars', 'r1'); + +SELECT pgv_insert('vars', 'r2', row(1, 'str1'::varchar)); +SELECT pgv_insert('vars', 'b', tab) FROM tab; +SELECT pgv_insert('vars', 'r2', tab) FROM tab; +SELECT pgv_select('vars', 'r2'); + +SELECT pgv_free(); From 2e3f14f194c9ff2f420cb6fb94628f6671b0361f Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 9 Dec 2020 14:09:16 +0300 Subject: [PATCH 116/147] Use hint instead of long message #32 --- expected/pg_variables.out | 9 ++++++--- pg_variables_record.c | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index e21ec67..180ebd1 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -565,9 +565,11 @@ ERROR: there is a record in the variable "r1" with same key SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); ERROR: new record structure have 3 attributes, but variable "r1" structure have 2. SELECT pgv_insert('vars3', 'r1', row(1, 1)); -ERROR: new record attribute type for attribute number 2 differs from variable "r1" structure. You may need explicit type casts. +ERROR: new record attribute type for attribute number 2 differs from variable "r1" structure. +HINT: You may need explicit type casts. SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); -ERROR: new record attribute type for attribute number 1 differs from variable "r1" structure. You may need explicit type casts. +ERROR: new record attribute type for attribute number 1 differs from variable "r1" structure. +HINT: You may need explicit type casts. SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail ERROR: searching for elements in multidimensional arrays is not supported -- Test variables caching @@ -959,7 +961,8 @@ SELECT pgv_insert('vars', 'r2', row(1, 'str1')); (1 row) SELECT pgv_insert('vars', 'r2', foo) FROM foo; -ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure. You may need explicit type casts. +ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure. +HINT: You may need explicit type casts. SELECT pgv_select('vars', 'r2'); pgv_select ------------ diff --git a/pg_variables_record.c b/pg_variables_record.c index d34d12c..3d7ca18 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -204,9 +204,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new record attribute type for attribute number %d " - "differs from variable \"%s\" structure. You may " - "need explicit type casts.", - i + 1, GetName(variable)))); + "differs from variable \"%s\" structure.", + i + 1, GetName(variable)), + errhint("You may need explicit type casts."))); } } From c8178943f1f8021ab1705abb5ee306ccfba0c9e3 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 4 Feb 2021 15:48:51 +0300 Subject: [PATCH 117/147] Issue #38: fix backand crash on nonhashable row insert. --- expected/pg_variables_trans.out | 5 +++++ pg_variables.c | 8 ++++++++ sql/pg_variables_trans.sql | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 2f46e04..6cede44 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3829,3 +3829,8 @@ SELECT pgv_free(); (1 row) +-- +-- Test case for issue #38 [PGPRO-4676] +-- +SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); +ERROR: could not identify a hash function for type record diff --git a/pg_variables.c b/pg_variables.c index b75701a..5e7156e 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2116,6 +2116,14 @@ rollbackSavepoint(TransObject *object, TransObjectType type) { TransState *state; + /* Nothing to do here if trans object was removed already. */ + if (dlist_is_empty(&object->states)) + { + /* Probably redundant. */ + removeObject(object, type); + return; + } + state = GetActualState(object); removeState(object, type, state); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 72d9559..7deef59 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1163,3 +1163,9 @@ SELECT pgv_insert('vars', 'r2', tab) FROM tab; SELECT pgv_select('vars', 'r2'); SELECT pgv_free(); + + +-- +-- Test case for issue #38 [PGPRO-4676] +-- +SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); From 2a38b715170c1016b14783bebcd5eec497c9732f Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 8 Feb 2021 12:39:54 +0300 Subject: [PATCH 118/147] Issue #38: remove useless comment. --- pg_variables.c | 1 - 1 file changed, 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index 5e7156e..152b5d6 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2119,7 +2119,6 @@ rollbackSavepoint(TransObject *object, TransObjectType type) /* Nothing to do here if trans object was removed already. */ if (dlist_is_empty(&object->states)) { - /* Probably redundant. */ removeObject(object, type); return; } From 8521406d5e81401ac934d495cd2eed48d2fc9e23 Mon Sep 17 00:00:00 2001 From: Roman Zharkov Date: Tue, 16 Mar 2021 15:32:53 +0700 Subject: [PATCH 119/147] Remove extra double quotes in the extension name. --- pg_variables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index 152b5d6..d76e01f 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2494,7 +2494,7 @@ compatibility_check(void) if (!pg_compatibility_check_no_error()) freeStatsLists(); - PG_COMPATIBILITY_CHECK("pg_variables"); + PG_COMPATIBILITY_CHECK(pg_variables); } # endif /* PG_COMPATIBILITY_CHECK */ # endif /* PG_VERSION_NUM */ From 2806cf758eb9b20aa94e79df650503d0cfb37d2a Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 23 Jun 2021 17:28:33 +0300 Subject: [PATCH 120/147] Add support for PostgreSQL 14. --- expected/pg_variables_trans_0.out | 3840 +++++++++++++++++++++++++++++ pg_variables.c | 16 +- 2 files changed, 3853 insertions(+), 3 deletions(-) create mode 100644 expected/pg_variables_trans_0.out diff --git a/expected/pg_variables_trans_0.out b/expected/pg_variables_trans_0.out new file mode 100644 index 0000000..7c29138 --- /dev/null +++ b/expected/pg_variables_trans_0.out @@ -0,0 +1,3840 @@ +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; +CREATE TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL'); +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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +RELEASE comm; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK to comm2; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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 +-- 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); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'any2', 'text value'::text); + pgv_set +--------- + +(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_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 COMMITTING SUBTRANSACTION +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + after savepoint sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +---------------------- + before savepoint sp1 +(1 row) + +COMMIT; +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK TO sp1; +COMMIT; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +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) + +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) + +-- 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) + +-- 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_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) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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) + +-- 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) + +-- 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) + +-- 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) + (1,str33) + (2,) + (0,str00) +(4 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (0,str00) +(4 rows) + +ROLLBACK; +SELECT pgv_select('vars3', 'r1'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (5,str55) + (0,str00) +(5 rows) + +SELECT pgv_select('vars3', 'r2'); + pgv_select +------------ + (,strNULL) + (1,str33) + (2,) + (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_insert('vars', 'r1', row(6 :: integer, 'str44' :: varchar), true); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', '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_select('vars', 'r1'); +ERROR: unrecognized variable "r1" +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (6,str44) +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- CHECK ROLLBACK AFTER COMMITTING SUBTRANSACTION +SELECT pgv_set('vars', 'any1', 'before transaction block'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'before savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'after savepoint sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'after savepoint sp2'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------------------- + 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; +SAVEPOINT sp1; +SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +RELEASE sp1; +SELECT pgv_get('vars2', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +ROLLBACK; +SELECT pgv_get('vars2', 'any1',NULL::text); +ERROR: unrecognized package "vars2" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- 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) + +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) + +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) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp5; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +RELEASE sp4; +SELECT pgv_select('vars3', 'r1'); + pgv_select +--------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"after savepoint sp1") + (0,str00) +(5 rows) + +ROLLBACK TO sp3; +SELECT pgv_select('vars3', 'r1'); + pgv_select +-------------------------------------- + (,strNULL) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (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) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +COMMIT; +SELECT pgv_select('vars3', 'r1'); + pgv_select +---------------------------- + (,strNULL) + (1,str33) + (2,) + (5,"before savepoint sp1") + (0,str00) +(5 rows) + +SELECT pgv_set('vars', 'any1', 'outer'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_set('vars', 'any1', 'begin'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SELECT pgv_set('vars', 'any1', 'sp1'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp2; +SELECT pgv_set('vars', 'any1', 'sp2'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp3; +SAVEPOINT sp4; +SELECT pgv_set('vars', 'any1', 'sp4'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp5; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +RELEASE sp4; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp4 +(1 row) + +ROLLBACK TO sp3; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +RELEASE sp2; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + sp2 +(1 row) + +ROLLBACK TO sp1; +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +--------- + begin +(1 row) + +ROLLBACK; +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; +-- THE REMOVAL OF THE VARIABLE MUST BE CANCELED ON ROLLBACK +SELECT pgv_set('vars', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +ROLLBACK; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + t +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); + pgv_get +----------------- + variable exists +(1 row) + +BEGIN; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +COMMIT; +SELECT pgv_exists('vars', 'any1'); + pgv_exists +------------ + f +(1 row) + +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ + vars3 | r1 | t +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'regular', 'regular variable exists'::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars', 'trans1', 'trans1 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_free(); -- Check sequential package removal in one subtransaction + pgv_free +---------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans2 | t +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +ROLLBACK; +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +BEGIN; +SAVEPOINT sp1; +SAVEPOINT sp2; +SAVEPOINT sp3; +SELECT pgv_set('vars2', 'trans2', 'trans2 variable exists'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp4; +SAVEPOINT sp5; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp5; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +RELEASE sp4; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars + vars2 +(2 rows) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +BEGIN; +SELECT pgv_set('vars', 'trans1', 'package created'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +SELECT pgv_set('vars', 'trans1', 'package restored'::text, true); + pgv_set +--------- + +(1 row) + +SELECT * FROM pgv_list() ORDER BY package, name; + package | name | is_transactional +---------+--------+------------------ + vars | trans1 | t +(1 row) + +COMMIT; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +-- REMOVED TRANSACTIONAL VARIABLE SHOULD BE NOT ACCESSIBLE THROUGH LastVariable +SELECT pgv_insert('package', 'errs',row(n), true) +FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; +ERROR: division by zero +SELECT pgv_insert('package', 'errs',row(1), true); + pgv_insert +------------ + +(1 row) + +-- Variable should not exists in case when error occurs during creation +SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +ERROR: could not identify a hash function for type unknown +SELECT pgv_select('vars4', 'r1', 0); +ERROR: unrecognized package "vars4" +-- If variable created and removed in same transaction level, +-- it should be totally removed and should not be present +-- in changes list and cache. +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT comm; +SELECT pgv_remove('vars', 'any1'); + pgv_remove +------------ + +(1 row) + +RELEASE comm; +SELECT pgv_get('vars', 'any1',NULL::text); +ERROR: unrecognized package "vars" +COMMIT; +-- Tests for PGPRO-2440 +SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +BEGIN; +SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm; +SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT pgv_delete('vars3', 'r3', 3); + pgv_delete +------------ + t +(1 row) + +BEGIN; +SELECT pgv_set('vars1', 't1', ''::text); + pgv_set +--------- + +(1 row) + +SELECT pgv_set('vars2', 't2', ''::text, true); + pgv_set +--------- + +(1 row) + +SAVEPOINT sp1; +SAVEPOINT sp2; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ERROR; +ERROR: syntax error at or near "ERROR" +LINE 1: ERROR; + ^ +COMMIT; +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SAVEPOINT sp_to_rollback; +SELECT pgv_set('vars', 'any1', 'some value'::text, true); + pgv_set +--------- + +(1 row) + +ROLLBACK TO sp_to_rollback; +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +-- Package should exist after rollback if it contains regular variable +BEGIN; +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +-- Package should not exist if it becomes empty in rolled back transaction +BEGIN; +SAVEPOINT comm2; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK TO comm2; +SELECT pgv_exists('vars'); + pgv_exists +------------ + f +(1 row) + +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- + vars +(1 row) + +COMMIT; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_set('vars', 'any1', 'some value'::text); + pgv_set +--------- + +(1 row) + +BEGIN; +SELECT pgv_remove('vars'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT package FROM pgv_stats() ORDER BY package; + package +--------- +(0 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be insertable after pgv_remove (variable) +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_remove (package) +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +BEGIN; +SELECT pgv_insert('test', 'z', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'z', ROW (1::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) +(1 row) + +SELECT pgv_insert('test', 'z', ROW (2::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | z | t +(1 row) + +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- Variables should be rollbackable if transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +--- +--- Variables should not be rollbackable if not transactional +--- +-- case 1 (remove var) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 2 (remove pack) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- case 3 (free) +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +BEGIN; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +-- clear all +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #1 (remove var) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "y" +ROLLBACK; +SELECT pgv_select('test', 'y'); +ERROR: unrecognized variable "y" +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #2 (remove pack) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursors test #3 (free) +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test'); +ERROR: function pgv_select(unknown) does not exist +LINE 1: SELECT pgv_select('test'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #4 +--- +-- non transactional, remove var +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, remove pac +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- non transactional, free +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional, remove var +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test', 'y'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, remove pack +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +-- transactional, free +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'y'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'y'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Cursor test #5 +--- +-- non transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +ROLLBACK; +SELECT pgv_select('test', 'x'); +ERROR: unrecognized package "test" +-- transactional +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_remove('test'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +SELECT pgv_select('test', 'x'); + pgv_select +------------ + (1,2) +(1 row) + +--- +--- Cursor test #6 +--- +--SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +--FETCH 1 in r1_cur; +--CLOSE r1_cur; +--SELECT pgv_remove('test', 'x'); +--COMMIT; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Tests for "leaked hash_seq_search scan for hash table" +--- +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), FALSE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'x') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'x') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +SELECT pgv_select('test', 'y') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_select('test', 'y') LIMIT 2; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +SELECT pgv_select('test', 'y') LIMIT 3; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r2_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 1 in r3_cur; + pgv_select +------------ + (1,2) +(1 row) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 2 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 3 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +ROLLBACK; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +DECLARE r2_cur CURSOR FOR SELECT pgv_select('test', 'y'); +DECLARE r3_cur CURSOR FOR SELECT pgv_select('test', 'x'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +FETCH 2 in r2_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +FETCH 3 in r3_cur; + pgv_select +------------ + (1,2) + (2,3) + (3,4) +(3 rows) + +COMMIT; +--- +--- Some special cases +--- +-- take #1 +SELECT pgv_insert('test', 'z1', ROW (2::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z1'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +SELECT pgv_remove('test', 'z1'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: unrecognized variable "z1" +ROLLBACK; +SELECT pgv_select('test', 'z1'); + pgv_select +------------ + (2,2) +(1 row) + +-- take #2 +SELECT pgv_insert('test', 'z2', ROW (2::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z2'); +FETCH 1 in r1_cur; + pgv_select +------------ + (2,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z2'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z2'); +ERROR: unrecognized variable "z2" +SELECT pgv_insert('test', 'z2', ROW (1::int, 2::int), FALSE); + pgv_insert +------------ + +(1 row) + +-- take #3 +SELECT pgv_insert('test', 'z3', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +CLOSE r1_cur; +SELECT pgv_remove('test', 'z3'); + pgv_remove +------------ + +(1 row) + +FETCH 1 in r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +SELECT pgv_select('test', 'z3'); + pgv_select +------------ + (1,2) +(1 row) + +--BEGIN; +--DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'z3'); +--FETCH 1 in r1_cur; +--SELECT pgv_remove('test', 'z3'); +--COMMIT; +--SELECT pgv_select('test', 'z3'); +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- take #4 +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (2::int, 3::int), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +SAVEPOINT sp1; +FETCH 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +BEGIN; +SAVEPOINT sp1; +SELECT pgv_select('test', 'x') LIMIT 1; + pgv_select +------------ + (1,2) +(1 row) + +ROLLBACK TO SAVEPOINT sp1; +COMMIT; +--- +--- Test cases for pgv_stats +--- +--- Amount of allocated memory may vary from version to version, as well as from +--- platform to platform. Moreover, postgres versions less than 90600 always +--- show zero allocated memory. So, it's much easier to check if allocated +--- memory size is multiple of 8k since we use ALLOCSET_DEFAULT_INITSIZE +--- (see memutils.h), insted of creating multiple outputs files. +--- +CREATE TEMP VIEW pgv_stats_view(pack, mem_mult) AS + SELECT package, allocated_memory % 8192 as allocated_multiple_8192 + FROM pgv_stats() + ORDER BY 1; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), TRUE); +ERROR: there is a record in the variable "y" with same key +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_insert('test1', 'x', ROW (2::float, 1::float), FALSE); +ERROR: variable "x" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +BEGIN; +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +ROLLBACK; +SELECT pgv_insert('test1', 'y', ROW (2::float, 1::float), FALSE); +ERROR: variable "y" already created as TRANSACTIONAL +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Some special cases +--- +-- 1 +BEGIN; +SAVEPOINT comm2; +SELECT pgv_insert('test', 'x1', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +-- 2 +BEGIN; +SELECT pgv_insert('test', 'x2', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +SAVEPOINT comm2; +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +-- 3 +BEGIN; +SELECT pgv_insert('test', 'x3', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +SAVEPOINT comm2; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +-- 4 +BEGIN; +SELECT pgv_insert('test', 'x4', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +SAVEPOINT comm2; +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +COMMIT; +-- 5 +BEGIN; +SELECT pgv_insert('test', 'x5', ROW (2::float, 1::float), TRUE); + pgv_insert +------------ + +(1 row) + +DECLARE r1_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +DECLARE r2_cur CURSOR FOR SELECT pack, mem_mult FROM pgv_stats_view; +FETCH 1 in r1_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +FETCH 1 in r2_cur; + pack | mem_mult +------+---------- + test | 0 +(1 row) + +SAVEPOINT comm2; +COMMIT; +DROP VIEW pgv_stats_view; +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +--- +--- Test case for issue #32 [PGPRO-4456] +--- +CREATE TEMP TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'); +SELECT pgv_insert('vars', 'r1', row(1, 'str1', 'str2')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'a', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r1', tab) FROM tab; +ERROR: new record structure have 2 attributes, but variable "r1" structure have 3. +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r2', row(1, 'str1'::varchar)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'b', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- +-- Test case for issue #38 [PGPRO-4676] +-- +SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); + pgv_insert +------------ + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index d76e01f..bcd0e21 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -1489,7 +1489,9 @@ getMemoryTotalSpace(MemoryContext context, int level, Size *totalspace) /* Examine the context itself */ memset(&totals, 0, sizeof(totals)); -#if PG_VERSION_NUM >= 110000 +#if PG_VERSION_NUM >= 140000 + (*context->methods->stats) (context, NULL, NULL, &totals, true); +#elif PG_VERSION_NUM >= 110000 (*context->methods->stats) (context, NULL, NULL, &totals); #else (*context->methods->stats) (context, level, false, &totals); @@ -1641,7 +1643,11 @@ ensurePackagesHashExists(void) packagesHash = hash_create("Packages hash", NUMPACKAGES, &ctl, - HASH_ELEM | HASH_CONTEXT); + HASH_ELEM | +# if PG_VERSION_NUM >= 140000 + HASH_STRINGS | +# endif + HASH_CONTEXT); } /* @@ -1668,7 +1674,11 @@ makePackHTAB(Package *package, bool is_trans) ctl.hcxt = *context; *htab = hash_create(hash_name, NUMVARIABLES, &ctl, - HASH_ELEM | HASH_CONTEXT); + HASH_ELEM | +# if PG_VERSION_NUM >= 140000 + HASH_STRINGS | +# endif + HASH_CONTEXT); } static void From a92036be3732d6acd37f062b3b5b41ca04569c2a Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 2 Jul 2021 16:51:25 +0300 Subject: [PATCH 121/147] Changes for Travis-CI build --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5882736..6f7474d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ +os: linux + +dist: focal + language: c services: From dbc3c83cfc6e40cb1c7d2e81d075fd98427eadaa Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 2 Jul 2021 17:58:51 +0300 Subject: [PATCH 122/147] travis-ci.org -> travis-ci.com --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01be495..294ed37 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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) +[![Build Status](https://travis-ci.com/postgrespro/pg_variables.svg?branch=master)](https://travis-ci.com/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) From 8652451e1ec81f6978da705af4e3a4cb6d691d67 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Thu, 4 Nov 2021 17:08:19 +0300 Subject: [PATCH 123/147] Changes for pg_variables and ATX compatibility --- Makefile | 2 +- expected/pg_variables_atx.out | 571 ++++++++++++++++++++++++++++++++ expected/pg_variables_atx_0.out | 465 ++++++++++++++++++++++++++ expected/pg_variables_atx_1.out | 465 ++++++++++++++++++++++++++ pg_variables.c | 385 +++++++++++++++++---- pg_variables.h | 25 +- sql/pg_variables_atx.sql | 196 +++++++++++ 7 files changed, 2047 insertions(+), 62 deletions(-) create mode 100644 expected/pg_variables_atx.out create mode 100644 expected/pg_variables_atx_0.out create mode 100644 expected/pg_variables_atx_1.out create mode 100644 sql/pg_variables_atx.sql diff --git a/Makefile b/Makefile index 7e262ff..f95c39f 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ DATA_built = $(EXTENSION)--$(EXTVERSION).sql PGFILEDESC = "pg_variables - sessional variables" -REGRESS = pg_variables pg_variables_any pg_variables_trans +REGRESS = pg_variables pg_variables_any pg_variables_trans pg_variables_atx ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/expected/pg_variables_atx.out b/expected/pg_variables_atx.out new file mode 100644 index 0000000..3322c37 --- /dev/null +++ b/expected/pg_variables_atx.out @@ -0,0 +1,571 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103); + pgv_set +--------- + +(1 row) + +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int1', 1001); + pgv_set +--------- + +(1 row) + + begin autonomous; +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int2', 1002); + pgv_set +--------- + +(1 row) + + commit; + commit; +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 103 +(1 row) + + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103, true); + pgv_set +--------- + +(1 row) + +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + begin autonomous; + select pgv_set('vars', 'int2', 1002, true); + pgv_set +--------- + +(1 row) + +-- 1002: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 1002 +(1 row) + + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + commit; + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 102 +(1 row) + +rollback; +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 101 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + select pgv_insert('test', 'z', row (11::int, 22::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (22::int, 33::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (33::int, 44::int), false); + pgv_insert +------------ + +(1 row) + + declare r11_cur cursor for select pgv_select('test', 'x'); +-- (1,2),(2,3): + fetch 2 in r11_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + + declare r2_cur cursor for select pgv_select('test', 'y'); +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: unrecognized variable "y" + rollback; + rollback; + rollback; + rollback; + rollback; + declare r2_cur cursor for select pgv_select('test', 'y'); + declare r3_cur cursor for select pgv_select('test', 'z'); +-- (1,2),(2,3): + fetch 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +-- (10,20),(20,30): + fetch 2 in r2_cur; + pgv_select +------------ + (10,20) + (20,30) +(2 rows) + +-- (11,22),(22,33): + fetch 2 in r3_cur; + pgv_select +------------ + (11,22) + (22,33) +(2 rows) + +rollback; +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; + select pgv_set('vars', 'trans_int', 103, true); + pgv_set +--------- + +(1 row) + +-- 103: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 103 +(1 row) + + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + rollback to sp1; +commit; +-- 101: +select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; + begin autonomous; + select pgv_set('vars1', 'int1', 2); + pgv_set +--------- + +(1 row) + + select pgv_set('vars1', 'trans_int1', 3, true); + pgv_set +--------- + +(1 row) + + savepoint sp2; + select pgv_set('vars1', 'trans_int1', 4, true); + pgv_set +--------- + +(1 row) + +-- 2 + select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 2 +(1 row) + +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 4 +(1 row) + + rollback to sp2; +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 3 +(1 row) + +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + + select pgv_set('vars1', 'trans_int2', 4, true); + pgv_set +--------- + +(1 row) + + select pgv_set('vars1', 'trans_int3', 5, true); + pgv_set +--------- + +(1 row) + + select pgv_set('vars1', 'int2', 3); + pgv_set +--------- + +(1 row) + + rollback; + commit; + rollback to sp1; +-- 1 + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 1 +(1 row) + +-- 2 + select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 2 +(1 row) + +-- 3 + select pgv_get('vars1', 'int2', null::int); + pgv_get +--------- + 3 +(1 row) + +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+-----------+------------------ + vars | trans_int | t + vars1 | int1 | f + vars1 | int2 | f +(3 rows) + +commit; +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; + select pgv_set('vars1', 'int1', 1); + pgv_set +--------- + +(1 row) + + select pgv_set('vars1', 'trans_int1', 2, true); + pgv_set +--------- + +(1 row) + + savepoint sp2; + select pgv_set('vars1', 'trans_int1', 3, true); + pgv_set +--------- + +(1 row) + + rollback to sp2; +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 2 +(1 row) + + commit; +rollback; +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_atx_0.out b/expected/pg_variables_atx_0.out new file mode 100644 index 0000000..3ffdc86 --- /dev/null +++ b/expected/pg_variables_atx_0.out @@ -0,0 +1,465 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int3', 103); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars', 'int1', 1001); +ERROR: current transaction is aborted, commands ignored until end of transaction block + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars', 'int2', 1002); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; + commit; +WARNING: there is no transaction in progress +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: unrecognized variable "int3" + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +WARNING: there is no transaction in progress +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int3', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int2', 1002, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 1002: + select pgv_get('vars', 'int2', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); +ERROR: unrecognized variable "int3" + commit; +WARNING: there is no transaction in progress + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); +ERROR: unrecognized variable "int2" +rollback; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_insert('test', 'z', row (11::int, 22::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_insert('test', 'z', row (22::int, 33::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_insert('test', 'z', row (33::int, 44::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + declare r11_cur cursor for select pgv_select('test', 'x'); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- (1,2),(2,3): + fetch 2 in r11_cur; +ERROR: current transaction is aborted, commands ignored until end of transaction block + declare r2_cur cursor for select pgv_select('test', 'y'); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + declare r2_cur cursor for select pgv_select('test', 'y'); +ERROR: DECLARE CURSOR can only be used in transaction blocks + declare r3_cur cursor for select pgv_select('test', 'z'); +ERROR: DECLARE CURSOR can only be used in transaction blocks +-- (1,2),(2,3): + fetch 2 in r1_cur; +ERROR: cursor "r1_cur" does not exist +-- (10,20),(20,30): + fetch 2 in r2_cur; +ERROR: cursor "r2_cur" does not exist +-- (11,22),(22,33): + fetch 2 in r3_cur; +ERROR: cursor "r3_cur" does not exist +rollback; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'trans_int', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'trans_int', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +commit; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars1', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int2', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int3', 5, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'int2', 3); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + commit; +WARNING: there is no transaction in progress + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +-- 1 + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: unrecognized package "vars1" +-- 3 + select pgv_get('vars1', 'int2', null::int); +ERROR: unrecognized package "vars1" +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +commit; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars1', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 2, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +rollback; +WARNING: there is no transaction in progress +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 0 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_atx_1.out b/expected/pg_variables_atx_1.out new file mode 100644 index 0000000..b5d8a07 --- /dev/null +++ b/expected/pg_variables_atx_1.out @@ -0,0 +1,465 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int3', 103); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars', 'int1', 1001); +ERROR: current transaction is aborted, commands ignored until end of transaction block + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars', 'int2', 1002); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; + commit; +WARNING: there is no transaction in progress +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +ERROR: unrecognized variable "int3" + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +WARNING: there is no transaction in progress +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int3', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'int3', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'int2', 1002, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 1002: + select pgv_get('vars', 'int2', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); +ERROR: unrecognized variable "int3" + commit; +WARNING: there is no transaction in progress + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); +ERROR: unrecognized variable "int2" +rollback; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_insert('test', 'z', row (11::int, 22::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_insert('test', 'z', row (22::int, 33::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_insert('test', 'z', row (33::int, 44::int), false); +ERROR: current transaction is aborted, commands ignored until end of transaction block + declare r11_cur cursor for select pgv_select('test', 'x'); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- (1,2),(2,3): + fetch 2 in r11_cur; +ERROR: current transaction is aborted, commands ignored until end of transaction block + declare r2_cur cursor for select pgv_select('test', 'y'); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + rollback; +WARNING: there is no transaction in progress + declare r2_cur cursor for select pgv_select('test', 'y'); +ERROR: DECLARE CURSOR can only be used in transaction blocks + declare r3_cur cursor for select pgv_select('test', 'z'); +ERROR: DECLARE CURSOR can only be used in transaction blocks +-- (1,2),(2,3): + fetch 2 in r1_cur; +ERROR: cursor "r1_cur" does not exist +-- (10,20),(20,30): + fetch 2 in r2_cur; +ERROR: cursor "r2_cur" does not exist +-- (11,22),(22,33): + fetch 2 in r3_cur; +ERROR: cursor "r3_cur" does not exist +rollback; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars', 'trans_int', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'trans_int', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +commit; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars1', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: no such savepoint +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int2', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int3', 5, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'int2', 3); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + commit; +WARNING: there is no transaction in progress + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +-- 1 + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: unrecognized package "vars1" +-- 3 + select pgv_get('vars1', 'int2', null::int); +ERROR: unrecognized package "vars1" +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +commit; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; +ERROR: syntax error at or near "autonomous" +LINE 1: begin autonomous; + ^ + select pgv_set('vars1', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 2, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: no such savepoint +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +rollback; +WARNING: there is no transaction in progress +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 0 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index bcd0e21..9730101 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -3,7 +3,7 @@ * pg_variables.c * Functions, which get or set variables values * - * Copyright (c) 2015-2016, Postgres Professional + * Copyright (c) 2015-2021, Postgres Professional * *------------------------------------------------------------------------- */ @@ -79,6 +79,11 @@ static void pushChangesStack(void); static int numOfRegVars(Package *package); +#ifdef PGPRO_EE +static void pgvSaveContext(void); +static void pgvRestoreContext(void); +#endif + /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); static inline ChangedObject *makeChangedObject(TransObject *object, @@ -153,15 +158,31 @@ typedef struct tagVariableStatEntry HASH_SEQ_STATUS *status; Variable *variable; Package *package; - int level; + Levels levels; } VariableStatEntry; typedef struct tagPackageStatEntry { HASH_SEQ_STATUS *status; - int level; + Levels levels; } PackageStatEntry; +#ifdef PGPRO_EE +/* + * Context for storing/restoring parameters when switching autonomous + * transactions + */ +typedef struct PgvContextStruct +{ + dlist_head *changesStack; + MemoryContext changesStackContext; + struct PgvContextStruct *next; +} PgvContextStruct; + +static PgvContextStruct *pgv_context = NULL; + +#endif /* PGPRO_EE */ + /* * Compare functions for VariableStatEntry and PackageStatEntry members. */ @@ -192,7 +213,12 @@ VariableStatEntry_eq_all(void *entry, void *value) static bool VariableStatEntry_level_eq(void *entry, void *value) { - return ((VariableStatEntry *) entry)->level == *(int *) value; + return +#ifdef PGPRO_EE + /* Compare ATX level */ + ((VariableStatEntry *) entry)->levels.atxlevel == ((Levels *) value)->atxlevel && +#endif + ((VariableStatEntry *) entry)->levels.level == ((Levels *) value)->level; } static bool @@ -204,9 +230,22 @@ PackageStatEntry_status_eq(void *entry, void *value) static bool PackageStatEntry_level_eq(void *entry, void *value) { - return ((PackageStatEntry *) entry)->level == *(int *) value; + return +#ifdef PGPRO_EE + /* Compare ATX level */ + ((PackageStatEntry *) entry)->levels.atxlevel == ((Levels *) value)->atxlevel && +#endif + ((PackageStatEntry *) entry)->levels.level == ((Levels *) value)->level; } +#ifdef PGPRO_EE +static bool +VariableStatEntry_is_transactional(void *entry, void *value) +{ + return ((VariableStatEntry *) entry)->variable->is_transactional; +} +#endif + /* * VariableStatEntry and PackageStatEntry status member getters. */ @@ -367,12 +406,12 @@ remove_variables_package(List **list, Package *package) * Remove all the entries for level. */ static void -remove_variables_level(List **list, int level) +remove_variables_level(List **list, Levels *levels) { RemoveIfContext ctx = { .list = list, - .value = &level, + .value = levels, .eq = VariableStatEntry_level_eq, .getter = VariableStatEntry_status_ptr, .match_first = false, @@ -421,12 +460,12 @@ remove_packages_status(List **list, HASH_SEQ_STATUS *status) * Remove all the entries with level for packages list. */ static void -remove_packages_level(List **list, int level) +remove_packages_level(List **list, Levels *levels) { RemoveIfContext ctx = { .list = list, - .value = &level, + .value = levels, .eq = PackageStatEntry_level_eq, .getter = PackageStatEntry_status_ptr, .match_first = false, @@ -435,6 +474,26 @@ remove_packages_level(List **list, int level) list_remove_if(ctx); } +#ifdef PGPRO_EE +/* + * Remove all transactional entries. + */ +static void +remove_variables_transactional(List **list) +{ + RemoveIfContext ctx = + { + .list = list, + .value = NULL, + .eq = VariableStatEntry_is_transactional, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = true + }; + list_remove_if(ctx); +} +#endif + static void freeStatsLists(void); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ @@ -922,7 +981,10 @@ variable_select(PG_FUNCTION_ARGS) entry->status = rstat; entry->variable = variable; entry->package = package; - entry->level = GetCurrentTransactionNestLevel(); + entry->levels.level = GetCurrentTransactionNestLevel(); +#ifdef PGPRO_EE + entry->levels.atxlevel = getNestLevelATX(); +#endif variables_stats = lcons((void *)entry, variables_stats); MemoryContextSwitchTo(oldcontext); @@ -1552,7 +1614,10 @@ get_packages_stats(PG_FUNCTION_ARGS) funcctx->user_fctx = rstat; entry = palloc0(sizeof(PackageStatEntry)); entry->status = rstat; - entry->level = GetCurrentTransactionNestLevel(); + entry->levels.level = GetCurrentTransactionNestLevel(); +#ifdef PGPRO_EE + entry->levels.atxlevel = getNestLevelATX(); +#endif packages_stats = lcons((void *)entry, packages_stats); MemoryContextSwitchTo(ctx); } @@ -1643,7 +1708,7 @@ ensurePackagesHashExists(void) packagesHash = hash_create("Packages hash", NUMPACKAGES, &ctl, - HASH_ELEM | + HASH_ELEM | # if PG_VERSION_NUM >= 140000 HASH_STRINGS | # endif @@ -1794,6 +1859,9 @@ createPackage(text *name, bool is_trans) package->varHashTransact = NULL; package->hctxRegular = NULL; package->hctxTransact = NULL; +#ifdef PGPRO_EE + package->context = NULL; +#endif initObjectHistory(&package->transObject, TRANS_PACKAGE); } @@ -2045,6 +2113,10 @@ removeObject(TransObject *object, TransObjectType type) if (type == TRANS_PACKAGE) { +#ifdef PGPRO_EE + PackageContext *context, *next; +#endif + package = (Package *) object; /* Regular variables had already removed */ @@ -2052,6 +2124,19 @@ removeObject(TransObject *object, TransObjectType type) MemoryContextDelete(package->hctxRegular); if (package->hctxTransact) MemoryContextDelete(package->hctxTransact); +#ifdef PGPRO_EE + /* + * Remove contexts with transactional part (stored when switching to + * ATX transaction) + */ + context = package->context; + while (context) + { + next = context->next; + pfree(context); + context = next; + } +#endif hash = packagesHash; } else @@ -2146,7 +2231,10 @@ rollbackSavepoint(TransObject *object, TransObjectType type) { /* ...create a new state to make package valid. */ initObjectHistory(object, type); - GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; +#ifdef PGPRO_EE + GetActualState(object)->levels.atxlevel = getNestLevelATX(); +#endif + GetActualState(object)->levels.level = GetCurrentTransactionNestLevel() - 1; if (!dlist_is_empty(changesStack)) addToChangesStackUpperLevel(object, type); } @@ -2170,7 +2258,10 @@ rollbackSavepoint(TransObject *object, TransObjectType type) { createSavepoint(object, type); addToChangesStackUpperLevel(object, type); - GetActualState(object)->level = GetCurrentTransactionNestLevel() - 1; +#ifdef PGPRO_EE + GetActualState(object)->levels.atxlevel = getNestLevelATX(); +#endif + GetActualState(object)->levels.level = GetCurrentTransactionNestLevel() - 1; } GetActualState(object)->is_valid = false; } @@ -2190,7 +2281,7 @@ static void releaseSavepoint(TransObject *object, TransObjectType type) { dlist_head *states = &object->states; - Assert(GetActualState(object)->level == GetCurrentTransactionNestLevel()); + Assert(GetActualState(object)->levels.level == GetCurrentTransactionNestLevel()); /* * If the object is not valid and does not exist at a higher level @@ -2221,7 +2312,7 @@ releaseSavepoint(TransObject *object, TransObjectType type) addToChangesStackUpperLevel(object, type); /* Change subxact level due to release */ - GetActualState(object)->level--; + GetActualState(object)->levels.level--; } static void @@ -2252,7 +2343,15 @@ isObjectChangedInCurrentTrans(TransObject *object) return false; state = GetActualState(object); - return state->level == GetCurrentTransactionNestLevel(); + return +#ifdef PGPRO_EE + /* + * We should separate states with equal subxacts but with + * different ATX level + */ + state->levels.atxlevel == getNestLevelATX() && +#endif + state->levels.level == GetCurrentTransactionNestLevel(); } /* @@ -2266,13 +2365,32 @@ isObjectChangedInUpperTrans(TransObject *object) cur_state = GetActualState(object); if (dlist_has_next(&object->states, &cur_state->node) && - cur_state->level == GetCurrentTransactionNestLevel()) +#ifdef PGPRO_EE + cur_state->levels.atxlevel == getNestLevelATX() && +#endif + cur_state->levels.level == GetCurrentTransactionNestLevel()) { prev_state = dlist_container(TransState, node, cur_state->node.next); - return prev_state->level == GetCurrentTransactionNestLevel() - 1; + return +#ifdef PGPRO_EE + /* + * We should separate states with equal subxacts but with + * different ATX level + */ + prev_state->levels.atxlevel == getNestLevelATX() && +#endif + prev_state->levels.level == GetCurrentTransactionNestLevel() - 1; } else - return cur_state->level == GetCurrentTransactionNestLevel() - 1; + return +#ifdef PGPRO_EE + /* + * We should separate states with equal subxacts but with + * different ATX level + */ + cur_state->levels.atxlevel == getNestLevelATX() && +#endif + cur_state->levels.level == GetCurrentTransactionNestLevel() - 1; } /* @@ -2367,7 +2485,10 @@ addToChangesStack(TransObject *object, TransObjectType type) csn->changedVarsList, &co->node); /* Give this object current subxact level */ - GetActualState(object)->level = GetCurrentTransactionNestLevel(); + GetActualState(object)->levels.level = GetCurrentTransactionNestLevel(); +#ifdef PGPRO_EE + GetActualState(object)->levels.atxlevel = getNestLevelATX(); +#endif } } @@ -2466,54 +2587,176 @@ static void compatibility_check(void) { /* - * | Edition | ConnPool | ATX | COMPAT_CHECK | - * ------------------------------------------- - * | std 9.6 | no | no | no | - * | std 10 | no | no | yes | - * | std 11 | no | no | yes | - * | std 12 | no | no | yes | - * | std 13 | no | no | yes | - * | ee 9.6 | no | yes | no | - * | ee 10 | no | yes | yes | - * | ee 11 | yes | yes | yes | - * | ee 12 | yes | yes | yes | - * | ee 13 | yes | yes | yes | + * | Edition | ConnPool | + * ---------------------- + * | std 9.6 | no | + * | std 10 | no | + * | std 11 | no | + * | std 12 | no | + * | std 13 | no | + * | ee 9.6 | no | + * | ee 10 | no | + * | ee 11 | yes | + * | ee 12 | yes | + * | ee 13 | yes | */ +#if defined(PGPRO_EE) && PG_VERSION_NUM >= 110000 + if (!IsDedicatedBackend) + { + freeStatsLists(); + elog(ERROR, "pg_variables extension is incompatible with connection pooling"); + } +#endif /* PGPRO_EE */ +} + #ifdef PGPRO_EE +/* + * At the beginning of ATX store the pg_variables's env into + * pgv_context. + */ +static void +pgvSaveContext(void) +{ + Package *package; + HASH_SEQ_STATUS pstat; + PgvContextStruct *sus = MemoryContextAlloc(CurTransactionContext, + sizeof(PgvContextStruct)); -# if (PG_VERSION_NUM < 100000) - /* - * This versions does not have dedicated macro to check compatibility. - * So, use simple check here for ATX. - */ - if (getNestLevelATX() != 0) + /* Save transactional variables for all packages (in packages structs) */ + if (packagesHash != NULL) + { + /* Get packages list */ + hash_seq_init(&pstat, packagesHash); + while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { - freeStatsLists(); - elog(ERROR, "pg_variables extension is not compatible with " - "autonomous transactions"); + PackageContext *context = MemoryContextAlloc(ModuleContext, + sizeof(PackageContext)); + context->next = package->context; + package->context = context; + + /* Save transactional variables in context */ + context->hctxTransact = package->hctxTransact; + context->varHashTransact = package->varHashTransact; + /* + * Package structure has a transactional part 'transObject'. + * This part is used in asserts like + * Assert(GetActualState(object)->levels.level == + * GetCurrentTransactionNestLevel()) + * But this comparison is not valid for ATX transactions because + * 'CurrentTransactionState->nestingLevel' for each of new ATX + * level is starts with 1. We should save package state at start + * of ATX transaction and restore it at finish. + * No need do this for transactional variables (we clean them at + * end of ATX transaction) and regular variables (we modify them + * directly). + */ + context->state = GetActualState(&package->transObject); + + package->hctxTransact = NULL; + package->varHashTransact = NULL; } -# else - /* - * Since ee10 there is PG_COMPATIBILITY_CHECK macro to check compatibility. - * But for some reasons it may not be present at the moment. - * So, if PG_COMPATIBILITY_CHECK macro is not present pg_variables are - * always compatible. - */ -# ifdef PG_COMPATIBILITY_CHECK + } + + /* Remove stats for all transactional variables */ + remove_variables_transactional(&variables_stats); + resetVariablesCache(); + + sus->changesStack = changesStack; + changesStack = NULL; + sus->changesStackContext = changesStackContext; + changesStackContext = NULL; + + sus->next = pgv_context; + pgv_context = sus; +} + +/* + * Restore pg_variables's env pointer from pgv_context. + */ +static void +pgvRestoreContext() +{ + Package *package; + HASH_SEQ_STATUS pstat; + PgvContextStruct *sus = pgv_context; + + resetVariablesCache(); + /* Delete changes stack for all transactional variables */ + if (changesStackContext) + { + MemoryContextDelete(changesStackContext); + changesStack = NULL; + changesStackContext = NULL; + } + /* We just finished ATX => need to free all hash_seq_search scans */ + freeStatsLists(); + + /* Restore transactional variables for all packages */ + if (packagesHash != NULL) + { + /* Get packages list */ + hash_seq_init(&pstat, packagesHash); + while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { - if (!pg_compatibility_check_no_error()) - freeStatsLists(); + /* + * Delete context with transactional variables (they are + * no need outside ATX transaction) + */ + if (package->hctxTransact) + MemoryContextDelete(package->hctxTransact); + + /* We have stored context for this package? */ + if (package->context) + { + PackageContext *context = package->context; + PackageContext *next = context->next; + TransObject *object = &package->transObject; + TransState *state; + + /* Restore transactional variables from context */ + package->hctxTransact = context->hctxTransact; + package->varHashTransact = context->varHashTransact; - PG_COMPATIBILITY_CHECK(pg_variables); + /* Remove all package states, generated in ATX transaction */ + while ((state = GetActualState(object)) != context->state) + { + if (dlist_is_empty(&object->states)) + elog(ERROR, "pg_variables extension can not find " + "transaction state for package"); + removeState(object, TRANS_PACKAGE, state); + } + + pfree(context); + package->context = next; + } + else + { + /* Package was created in this autonomous transaction */ + package->hctxTransact = NULL; + package->varHashTransact = NULL; + /* + * No need to remove package states: for just created package + * we have one state with level = 0 + */ + } } -# endif /* PG_COMPATIBILITY_CHECK */ -# endif /* PG_VERSION_NUM */ + } -# undef ATX_CHECK -# undef CONNPOOL_CHECK + /* + * 'sus' can be NULL in case pg_variables was not initialized + * at start of transaction + */ + if (sus) + { + /* Restore changes stack for previous level: */ + changesStack = sus->changesStack; + changesStackContext = sus->changesStackContext; -#endif /* PGPRO_EE */ + pgv_context = sus->next; + pfree(sus); + } } +#endif /* PGPRO_EE */ /* * Intercept execution during subtransaction processing @@ -2522,6 +2765,8 @@ static void pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { + Levels levels; + if (changesStack) { switch (event) @@ -2541,8 +2786,12 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, } } - remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); - remove_packages_level(&packages_stats, GetCurrentTransactionNestLevel()); + levels.level = GetCurrentTransactionNestLevel(); +#ifdef PGPRO_EE + levels.atxlevel = getNestLevelATX(); +#endif + remove_variables_level(&variables_stats, &levels); + remove_packages_level(&packages_stats, &levels); } /* @@ -2575,6 +2824,22 @@ pgvTransCallback(XactEvent event, void *arg) if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) freeStatsLists(); + +#ifdef PGPRO_EE + if (getNestLevelATX() > 0) + { + if (event == XACT_EVENT_START) + { /* on each ATX transaction start */ + pgvSaveContext(); + } + else if (event == XACT_EVENT_ABORT || event == XACT_EVENT_PARALLEL_ABORT || + event == XACT_EVENT_COMMIT || event == XACT_EVENT_PARALLEL_COMMIT || + event == XACT_EVENT_PREPARE) + { /* on each ATX transaction finish */ + pgvRestoreContext(); + } + } +#endif } /* diff --git a/pg_variables.h b/pg_variables.h index b08faa6..864a5e2 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -52,12 +52,21 @@ typedef struct ScalarVar int16 typlen; } ScalarVar; +/* Object levels (subxact + atx) */ +typedef struct Levels +{ + int level; +#ifdef PGPRO_EE + int atxlevel; +#endif +} Levels; + /* State of TransObject instance */ typedef struct TransState { dlist_node node; bool is_valid; - int level; + Levels levels; } TransState; /* List node that stores one of the package's states */ @@ -85,6 +94,17 @@ typedef struct TransObject dlist_head states; } TransObject; +#ifdef PGPRO_EE +/* Package context for save transactional part of package */ +typedef struct PackageContext +{ + HTAB *varHashTransact; + MemoryContext hctxTransact; + TransState *state; + struct PackageContext *next; +} PackageContext; +#endif + /* Transactional package */ typedef struct Package { @@ -94,6 +114,9 @@ typedef struct Package /* Memory context for package variables for easy memory release */ MemoryContext hctxRegular, hctxTransact; +#ifdef PGPRO_EE + PackageContext *context; +#endif } Package; /* Transactional variable */ diff --git a/sql/pg_variables_atx.sql b/sql/pg_variables_atx.sql new file mode 100644 index 0000000..396ea18 --- /dev/null +++ b/sql/pg_variables_atx.sql @@ -0,0 +1,196 @@ +select pgv_free(); + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); +begin; + select pgv_set('vars', 'int2', 102); + begin autonomous; + select pgv_set('vars', 'int3', 103); +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + select pgv_set('vars', 'int1', 1001); + begin autonomous; +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + select pgv_set('vars', 'int2', 1002); + commit; + commit; +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + select pgv_set('vars', 'int3', 1003); +rollback; + +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + +select pgv_free(); + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); +begin; + select pgv_set('vars', 'int2', 102, true); + begin autonomous; + select pgv_set('vars', 'int3', 103, true); +-- 103: + select pgv_get('vars', 'int3', null::int); + begin autonomous; + select pgv_set('vars', 'int2', 1002, true); +-- 1002: + select pgv_get('vars', 'int2', null::int); + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); + commit; + select pgv_set('vars', 'int1', 1001, true); +-- 1001: + select pgv_get('vars', 'int1', null::int); +-- 102: + select pgv_get('vars', 'int2', null::int); +rollback; +-- 101: +select pgv_get('vars', 'int1', null::int); +-- vars:int1: +select * from pgv_list() order by package, name; + +select pgv_free(); + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); +select pgv_insert('test', 'x', row (2::int, 3::int), false); +select pgv_insert('test', 'x', row (3::int, 4::int), false); + +select pgv_insert('test', 'y', row (10::int, 20::int), true); +select pgv_insert('test', 'y', row (20::int, 30::int), true); +select pgv_insert('test', 'y', row (30::int, 40::int), true); + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + select pgv_insert('test', 'z', row (11::int, 22::int), false); + select pgv_insert('test', 'z', row (22::int, 33::int), false); + select pgv_insert('test', 'z', row (33::int, 44::int), false); + + declare r11_cur cursor for select pgv_select('test', 'x'); +-- (1,2),(2,3): + fetch 2 in r11_cur; + declare r2_cur cursor for select pgv_select('test', 'y'); +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; + rollback; + rollback; + rollback; + rollback; + rollback; + declare r2_cur cursor for select pgv_select('test', 'y'); + declare r3_cur cursor for select pgv_select('test', 'z'); +-- (1,2),(2,3): + fetch 2 in r1_cur; +-- (10,20),(20,30): + fetch 2 in r2_cur; +-- (11,22),(22,33): + fetch 2 in r3_cur; +rollback; + +select pgv_free(); + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); +-- 101: + select pgv_get('vars', 'trans_int', null::int); + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); +-- 102: + select pgv_get('vars', 'trans_int', null::int); + begin autonomous; + select pgv_set('vars', 'trans_int', 103, true); +-- 103: + select pgv_get('vars', 'trans_int', null::int); + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); + rollback to sp1; +commit; +-- 101: +select pgv_get('vars', 'trans_int', null::int); + +select pgv_free(); + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + begin autonomous; + begin autonomous; + select pgv_set('vars1', 'int1', 2); + select pgv_set('vars1', 'trans_int1', 3, true); + savepoint sp2; + select pgv_set('vars1', 'trans_int1', 4, true); +-- 2 + select pgv_get('vars1', 'int1', null::int); +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); + rollback to sp2; +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; + select pgv_set('vars1', 'trans_int2', 4, true); + select pgv_set('vars1', 'trans_int3', 5, true); + select pgv_set('vars1', 'int2', 3); + rollback; + commit; + rollback to sp1; +-- 1 + select pgv_get('vars', 'trans_int', null::int); +-- 2 + select pgv_get('vars1', 'int1', null::int); +-- 3 + select pgv_get('vars1', 'int2', null::int); +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; +commit; + +select pgv_free(); + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); +select pgv_set('vars1', 'trans_int1', 0, true); +begin; + begin autonomous; + select pgv_set('vars1', 'int1', 1); + select pgv_set('vars1', 'trans_int1', 2, true); + savepoint sp2; + select pgv_set('vars1', 'trans_int1', 3, true); + rollback to sp2; +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); + commit; +rollback; +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; +-- 1 +select pgv_get('vars1', 'int1', null::int); +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + +select pgv_free(); From 76019de1360ebdf3c41da3b8210f4fe5bae45199 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 15 Nov 2021 17:33:14 +0300 Subject: [PATCH 124/147] Run pgindent --- pg_variables.c | 465 ++++++++++++++++++++++-------------------- pg_variables.h | 23 ++- pg_variables_record.c | 18 +- 3 files changed, 270 insertions(+), 236 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 9730101..46c7f24 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -77,7 +77,7 @@ static void addToChangesStackUpperLevel(TransObject *object, TransObjectType type); static void pushChangesStack(void); -static int numOfRegVars(Package *package); +static int numOfRegVars(Package *package); #ifdef PGPRO_EE static void pgvSaveContext(void); @@ -86,8 +86,8 @@ static void pgvRestoreContext(void); /* Constructors */ static void makePackHTAB(Package *package, bool is_trans); -static inline ChangedObject *makeChangedObject(TransObject *object, - MemoryContext ctx); +static inline ChangedObject * makeChangedObject(TransObject *object, + MemoryContext ctx); static void initObjectHistory(TransObject *object, TransObjectType type); /* Hook functions */ @@ -105,7 +105,7 @@ do { \ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("variable name can not be NULL"))); \ } while(0) -#else /* PG_VERSION_NUM < 120000 */ +#else /* PG_VERSION_NUM < 120000 */ #define CHECK_ARGS_FOR_NULL() \ do { \ if (fcinfo->argnull[0]) \ @@ -117,13 +117,14 @@ do { \ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ errmsg("variable name can not be NULL"))); \ } while(0) -#endif /* PG_VERSION_NUM */ +#endif /* PG_VERSION_NUM */ static HTAB *packagesHash = NULL; static MemoryContext ModuleContext = NULL; /* Recent package */ static Package *LastPackage = NULL; + /* Recent variable */ static Variable *LastVariable = NULL; @@ -154,18 +155,18 @@ static List *packages_stats = NIL; typedef struct tagVariableStatEntry { - HTAB *hash; - HASH_SEQ_STATUS *status; - Variable *variable; - Package *package; - Levels levels; -} VariableStatEntry; + HTAB *hash; + HASH_SEQ_STATUS *status; + Variable *variable; + Package *package; + Levels levels; +} VariableStatEntry; typedef struct tagPackageStatEntry { - HASH_SEQ_STATUS *status; - Levels levels; -} PackageStatEntry; + HASH_SEQ_STATUS *status; + Levels levels; +} PackageStatEntry; #ifdef PGPRO_EE /* @@ -174,14 +175,14 @@ typedef struct tagPackageStatEntry */ typedef struct PgvContextStruct { - dlist_head *changesStack; - MemoryContext changesStackContext; + dlist_head *changesStack; + MemoryContext changesStackContext; struct PgvContextStruct *next; } PgvContextStruct; static PgvContextStruct *pgv_context = NULL; -#endif /* PGPRO_EE */ +#endif /* PGPRO_EE */ /* * Compare functions for VariableStatEntry and PackageStatEntry members. @@ -215,7 +216,7 @@ VariableStatEntry_level_eq(void *entry, void *value) { return #ifdef PGPRO_EE - /* Compare ATX level */ + /* Compare ATX level */ ((VariableStatEntry *) entry)->levels.atxlevel == ((Levels *) value)->atxlevel && #endif ((VariableStatEntry *) entry)->levels.level == ((Levels *) value)->level; @@ -232,7 +233,7 @@ PackageStatEntry_level_eq(void *entry, void *value) { return #ifdef PGPRO_EE - /* Compare ATX level */ + /* Compare ATX level */ ((PackageStatEntry *) entry)->levels.atxlevel == ((Levels *) value)->atxlevel && #endif ((PackageStatEntry *) entry)->levels.level == ((Levels *) value)->level; @@ -276,20 +277,22 @@ PackageStatEntry_status_ptr(void *entry) */ typedef struct tagRemoveIfContext { - List **list; /* target list */ - void *value; /* value to compare with */ - bool (*eq)(void *, void *); /* list item eq to value func */ - HASH_SEQ_STATUS * (*getter)(void *); /* status getter */ - bool match_first; /* return on first match */ - bool term; /* hash_seq_term on match */ -} RemoveIfContext; + List **list; /* target list */ + void *value; /* value to compare with */ + bool (*eq) (void *, void *); /* list item eq to value func */ + HASH_SEQ_STATUS *(*getter) (void *); /* status getter */ + bool match_first; /* return on first match */ + bool term; /* hash_seq_term on match */ +} RemoveIfContext; static void list_remove_if(RemoveIfContext ctx) { #if (PG_VERSION_NUM < 130000) - ListCell *cell, *next, *prev = NULL; - void *entry = NULL; + ListCell *cell, + *next, + *prev = NULL; + void *entry = NULL; for (cell = list_head(*ctx.list); cell; cell = next) { @@ -316,12 +319,13 @@ list_remove_if(RemoveIfContext ctx) } #else /* - * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 + * See + * https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 * * Version >= 13 have different lists interface. */ - ListCell *cell; - void *entry = NULL; + ListCell *cell; + void *entry = NULL; foreach(cell, *ctx.list) { @@ -352,13 +356,14 @@ remove_variables_status(List **list, HASH_SEQ_STATUS *status) { RemoveIfContext ctx = { - .list = list, - .value = status, - .eq = VariableStatEntry_status_eq, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = status, + .eq = VariableStatEntry_status_eq, + .getter = VariableStatEntry_status_ptr, .match_first = true, - .term = false + .term = false }; + list_remove_if(ctx); } @@ -374,13 +379,14 @@ remove_variables_variable(List **list, Variable *variable) */ RemoveIfContext ctx = { - .list = list, - .value = variable, - .eq = VariableStatEntry_variable_eq, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = variable, + .eq = VariableStatEntry_variable_eq, + .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true }; + list_remove_if(ctx); } @@ -392,13 +398,14 @@ remove_variables_package(List **list, Package *package) { RemoveIfContext ctx = { - .list = list, - .value = package, - .eq = VariableStatEntry_package_eq, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = package, + .eq = VariableStatEntry_package_eq, + .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true }; + list_remove_if(ctx); } @@ -410,13 +417,14 @@ remove_variables_level(List **list, Levels *levels) { RemoveIfContext ctx = { - .list = list, - .value = levels, - .eq = VariableStatEntry_level_eq, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = levels, + .eq = VariableStatEntry_level_eq, + .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = false + .term = false }; + list_remove_if(ctx); } @@ -428,13 +436,14 @@ remove_variables_all(List **list) { RemoveIfContext ctx = { - .list = list, - .value = NULL, - .eq = VariableStatEntry_eq_all, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = NULL, + .eq = VariableStatEntry_eq_all, + .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true }; + list_remove_if(ctx); } @@ -446,13 +455,14 @@ remove_packages_status(List **list, HASH_SEQ_STATUS *status) { RemoveIfContext ctx = { - .list = list, - .value = status, - .eq = PackageStatEntry_status_eq, - .getter = PackageStatEntry_status_ptr, + .list = list, + .value = status, + .eq = PackageStatEntry_status_eq, + .getter = PackageStatEntry_status_ptr, .match_first = true, - .term = false + .term = false }; + list_remove_if(ctx); } @@ -464,13 +474,14 @@ remove_packages_level(List **list, Levels *levels) { RemoveIfContext ctx = { - .list = list, - .value = levels, - .eq = PackageStatEntry_level_eq, - .getter = PackageStatEntry_status_ptr, + .list = list, + .value = levels, + .eq = PackageStatEntry_level_eq, + .getter = PackageStatEntry_status_ptr, .match_first = false, - .term = true + .term = true }; + list_remove_if(ctx); } @@ -483,18 +494,20 @@ remove_variables_transactional(List **list) { RemoveIfContext ctx = { - .list = list, - .value = NULL, - .eq = VariableStatEntry_is_transactional, - .getter = VariableStatEntry_status_ptr, + .list = list, + .value = NULL, + .eq = VariableStatEntry_is_transactional, + .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true }; + list_remove_if(ctx); } #endif static void freeStatsLists(void); + /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -530,7 +543,7 @@ static void variable_set(text *package_name, text *var_name, Oid typid, Datum value, bool is_null, bool is_transactional) { - Package *package; + Package *package; Variable *variable; ScalarVar *scalar; @@ -561,7 +574,7 @@ static Datum variable_get(text *package_name, text *var_name, Oid typid, bool *is_null, bool strict) { - Package *package; + Package *package; Variable *variable; ScalarVar *scalar; @@ -675,7 +688,7 @@ variable_insert(PG_FUNCTION_ARGS) text *package_name; text *var_name; HeapTupleHeader rec; - Package *package; + Package *package; Variable *variable; bool is_transactional; @@ -790,7 +803,7 @@ variable_update(PG_FUNCTION_ARGS) text *package_name; text *var_name; HeapTupleHeader rec; - Package *package; + Package *package; Variable *variable; TransObject *transObject; bool res; @@ -870,7 +883,7 @@ variable_delete(PG_FUNCTION_ARGS) Oid value_type; Datum value; bool value_is_null = PG_ARGISNULL(2); - Package *package; + Package *package; Variable *variable; TransObject *transObject; bool res; @@ -944,10 +957,10 @@ variable_select(PG_FUNCTION_ARGS) FuncCallContext *funcctx; HASH_SEQ_STATUS *rstat; HashRecordEntry *item; - text *package_name; - text *var_name; - Package *package; - Variable *variable; + text *package_name; + text *var_name; + Package *package; + Variable *variable; CHECK_ARGS_FOR_NULL(); @@ -961,9 +974,9 @@ variable_select(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; - RecordVar *record; - VariableStatEntry *entry; + MemoryContext oldcontext; + RecordVar *record; + VariableStatEntry *entry; record = &(GetActualValue(variable).record); funcctx = SRF_FIRSTCALL_INIT(); @@ -985,7 +998,7 @@ variable_select(PG_FUNCTION_ARGS) #ifdef PGPRO_EE entry->levels.atxlevel = getNestLevelATX(); #endif - variables_stats = lcons((void *)entry, variables_stats); + variables_stats = lcons((void *) entry, variables_stats); MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); @@ -1000,7 +1013,7 @@ variable_select(PG_FUNCTION_ARGS) if (item != NULL) { Assert(!HeapTupleHeaderHasExternal( - (HeapTupleHeader) DatumGetPointer(item->tuple))); + (HeapTupleHeader) DatumGetPointer(item->tuple))); SRF_RETURN_NEXT(funcctx, item->tuple); } @@ -1019,7 +1032,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) Oid value_type; Datum value; bool value_is_null = PG_ARGISNULL(2); - Package *package; + Package *package; Variable *variable; HashRecordEntry *item; @@ -1067,7 +1080,7 @@ variable_select_by_value(PG_FUNCTION_ARGS) if (found) { Assert(!HeapTupleHeaderHasExternal( - (HeapTupleHeader) DatumGetPointer(item->tuple))); + (HeapTupleHeader) DatumGetPointer(item->tuple))); PG_RETURN_DATUM(item->tuple); } @@ -1096,7 +1109,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) text *package_name; text *var_name; ArrayType *values; - Package *package; + Package *package; Variable *variable; MemoryContext oldcontext; @@ -1161,7 +1174,7 @@ variable_select_by_values(PG_FUNCTION_ARGS) if (found) { Assert(!HeapTupleHeaderHasExternal( - (HeapTupleHeader) DatumGetPointer(item->tuple))); + (HeapTupleHeader) DatumGetPointer(item->tuple))); SRF_RETURN_NEXT(funcctx, item->tuple); } } @@ -1179,7 +1192,7 @@ variable_exists(PG_FUNCTION_ARGS) { text *package_name; text *var_name; - Package *package; + Package *package; Variable *variable = NULL; char key[NAMEDATALEN]; bool found = false; @@ -1241,11 +1254,11 @@ package_exists(PG_FUNCTION_ARGS) Datum remove_variable(PG_FUNCTION_ARGS) { - text *package_name; - text *var_name; - Package *package; - Variable *variable; - TransObject *transObject; + text *package_name; + text *var_name; + Package *package; + Variable *variable; + TransObject *transObject; CHECK_ARGS_FOR_NULL(); @@ -1293,7 +1306,7 @@ remove_variable(PG_FUNCTION_ARGS) Datum remove_package(PG_FUNCTION_ARGS) { - Package *package; + Package *package; text *package_name; if (PG_ARGISNULL(0)) @@ -1317,11 +1330,11 @@ remove_package(PG_FUNCTION_ARGS) static void removePackageInternal(Package *package) { - TransObject *transObject; - Variable *variable; - HTAB *htab; - HASH_SEQ_STATUS vstat; - int i; + TransObject *transObject; + Variable *variable; + HTAB *htab; + HASH_SEQ_STATUS vstat; + int i; /* Mark all the valid variables from package as deleted */ for (i = 0; i < 2; i++) @@ -1362,7 +1375,7 @@ removePackageInternal(Package *package) static bool isPackageEmpty(Package *package) { - int var_num = GetPackState(package)->trans_var_num; + int var_num = GetPackState(package)->trans_var_num; if (package->varHashRegular) var_num += hash_get_num_entries(package->varHashRegular); @@ -1389,7 +1402,7 @@ resetVariablesCache(void) Datum remove_packages(PG_FUNCTION_ARGS) { - Package *package; + Package *package; HASH_SEQ_STATUS pstat; /* There is no any packages and variables */ @@ -1451,7 +1464,7 @@ get_packages_and_variables(PG_FUNCTION_ARGS) */ if (packagesHash) { - Package *package; + Package *package; HASH_SEQ_STATUS pstat; int mRecs = NUMVARIABLES, nRecs = 0; @@ -1473,7 +1486,8 @@ get_packages_and_variables(PG_FUNCTION_ARGS) /* Get variables list for package */ for (i = 0; i < 2; i++) { - HTAB *htab = pack_htab(package, i); + HTAB *htab = pack_htab(package, i); + if (!htab) continue; hash_seq_init(&vstat, htab); @@ -1579,7 +1593,7 @@ get_packages_stats(PG_FUNCTION_ARGS) FuncCallContext *funcctx; MemoryContext oldcontext; HASH_SEQ_STATUS *rstat; - Package *package; + Package *package; if (SRF_IS_FIRSTCALL()) { @@ -1603,7 +1617,7 @@ get_packages_stats(PG_FUNCTION_ARGS) */ if (packagesHash) { - MemoryContext ctx; + MemoryContext ctx; PackageStatEntry *entry; ctx = MemoryContextSwitchTo(TopTransactionContext); @@ -1618,7 +1632,7 @@ get_packages_stats(PG_FUNCTION_ARGS) #ifdef PGPRO_EE entry->levels.atxlevel = getNestLevelATX(); #endif - packages_stats = lcons((void *)entry, packages_stats); + packages_stats = lcons((void *) entry, packages_stats); MemoryContextSwitchTo(ctx); } else @@ -1709,9 +1723,9 @@ ensurePackagesHashExists(void) packagesHash = hash_create("Packages hash", NUMPACKAGES, &ctl, HASH_ELEM | -# if PG_VERSION_NUM >= 140000 +#if PG_VERSION_NUM >= 140000 HASH_STRINGS | -# endif +#endif HASH_CONTEXT); } @@ -1721,10 +1735,10 @@ ensurePackagesHashExists(void) static void makePackHTAB(Package *package, bool is_trans) { - HASHCTL ctl; - char hash_name[BUFSIZ]; - HTAB **htab; - MemoryContext *context; + HASHCTL ctl; + char hash_name[BUFSIZ]; + HTAB **htab; + MemoryContext *context; htab = is_trans ? &package->varHashTransact : &package->varHashRegular; context = is_trans ? &package->hctxTransact : &package->hctxRegular; @@ -1740,9 +1754,9 @@ makePackHTAB(Package *package, bool is_trans) *htab = hash_create(hash_name, NUMVARIABLES, &ctl, HASH_ELEM | -# if PG_VERSION_NUM >= 140000 +#if PG_VERSION_NUM >= 140000 HASH_STRINGS | -# endif +#endif HASH_CONTEXT); } @@ -1761,13 +1775,14 @@ initObjectHistory(TransObject *object, TransObjectType type) /* Initialize state */ state->is_valid = true; if (type == TRANS_PACKAGE) - ((PackState *)state)->trans_var_num = 0; + ((PackState *) state)->trans_var_num = 0; else { - Variable *variable = (Variable *) object; + Variable *variable = (Variable *) object; + if (!variable->is_record) { - VarState * varState = (VarState *) state; + VarState *varState = (VarState *) state; ScalarVar *scalar = &(varState->value.scalar); get_typlenbyval(variable->typid, &scalar->typlen, @@ -1780,7 +1795,7 @@ initObjectHistory(TransObject *object, TransObjectType type) static Package * getPackage(text *name, bool strict) { - Package *package; + Package *package; char key[NAMEDATALEN]; bool found; @@ -1793,8 +1808,8 @@ getPackage(text *name, bool strict) if (found && GetActualState(package)->is_valid) { - Assert (GetPackState(package)->trans_var_num + - numOfRegVars(package) > 0); + Assert(GetPackState(package)->trans_var_num + + numOfRegVars(package) > 0); return package; } } @@ -1802,7 +1817,7 @@ getPackage(text *name, bool strict) if (strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized package \"%s\"", key))); + errmsg("unrecognized package \"%s\"", key))); return NULL; } @@ -1810,7 +1825,7 @@ getPackage(text *name, bool strict) static Package * createPackage(text *name, bool is_trans) { - Package *package; + Package *package; char key[NAMEDATALEN]; bool found; @@ -1905,19 +1920,19 @@ getVariableInternal(Package *package, text *name, Oid typid, bool is_record, if (variable->typid != typid) { char *var_type = DatumGetCString( - DirectFunctionCall1(regtypeout, - ObjectIdGetDatum(variable->typid))); + DirectFunctionCall1(regtypeout, + ObjectIdGetDatum(variable->typid))); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("variable \"%s\" requires \"%s\" value", + errmsg("variable \"%s\" requires \"%s\" value", key, var_type))); } if (variable->is_record != is_record) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"%s\" isn't a %s variable", + errmsg("\"%s\" isn't a %s variable", key, is_record ? "record" : "scalar"))); } if (!GetActualState(variable)->is_valid && strict) @@ -1964,7 +1979,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, if (found) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("variable \"%s\" already created as %sTRANSACTIONAL", + errmsg("variable \"%s\" already created as %sTRANSACTIONAL", key, is_transactional ? "NOT " : ""))); } @@ -1991,7 +2006,7 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" isn't a %s variable", - key, is_record ? "record" : "scalar"))); + key, is_record ? "record" : "scalar"))); /* * Savepoint must be created when variable changed in current @@ -2023,8 +2038,8 @@ createVariableInternal(Package *package, text *name, Oid typid, bool is_record, } /* - * If the variable has been created or has just become valid, - * increment the counter of valid transactional variables. + * If the variable has been created or has just become valid, increment + * the counter of valid transactional variables. */ if (is_transactional && (!found || !GetActualState(variable)->is_valid)) @@ -2109,12 +2124,13 @@ removeObject(TransObject *object, TransObjectType type) { bool found; HTAB *hash; - Package *package = NULL; + Package *package = NULL; if (type == TRANS_PACKAGE) { #ifdef PGPRO_EE - PackageContext *context, *next; + PackageContext *context, + *next; #endif package = (Package *) object; @@ -2125,6 +2141,7 @@ removeObject(TransObject *object, TransObjectType type) if (package->hctxTransact) MemoryContextDelete(package->hctxTransact); #ifdef PGPRO_EE + /* * Remove contexts with transactional part (stored when switching to * ATX transaction) @@ -2141,11 +2158,12 @@ removeObject(TransObject *object, TransObjectType type) } else { - Variable *var = (Variable *) object; + Variable *var = (Variable *) object; + package = var->package; hash = var->is_transactional ? - var->package->varHashTransact : - var->package->varHashRegular; + var->package->varHashTransact : + var->package->varHashRegular; } /* Remove all object's states */ @@ -2180,7 +2198,7 @@ createSavepoint(TransObject *object, TransObjectType type) { newState = (TransState *) MemoryContextAllocZero(ModuleContext, sizeof(PackState)); - ((PackState *)newState)->trans_var_num = ((PackState *)prevState)->trans_var_num; + ((PackState *) newState)->trans_var_num = ((PackState *) prevState)->trans_var_num; } else { @@ -2227,7 +2245,7 @@ rollbackSavepoint(TransObject *object, TransObjectType type) if (dlist_is_empty(&object->states)) { /* ...but object is a package and has some regular variables... */ - if (numOfRegVars((Package *)object) > 0) + if (numOfRegVars((Package *) object) > 0) { /* ...create a new state to make package valid. */ initObjectHistory(object, type); @@ -2242,11 +2260,12 @@ rollbackSavepoint(TransObject *object, TransObjectType type) /* ...or remove an object if it is no longer needed. */ removeObject(object, type); } + /* - * But if a package has more states, but hasn't valid variables, - * mark it as not valid or remove at top level transaction. - */ - else if (isPackageEmpty((Package *)object)) + * But if a package has more states, but hasn't valid variables, mark + * it as not valid or remove at top level transaction. + */ + else if (isPackageEmpty((Package *) object)) { if (dlist_is_empty(changesStack)) { @@ -2281,33 +2300,40 @@ static void releaseSavepoint(TransObject *object, TransObjectType type) { dlist_head *states = &object->states; + Assert(GetActualState(object)->levels.level == GetCurrentTransactionNestLevel()); /* - * If the object is not valid and does not exist at a higher level - * (or if we complete the transaction) - remove object. + * If the object is not valid and does not exist at a higher level (or if + * we complete the transaction) - remove object. */ if (!GetActualState(object)->is_valid && - (!dlist_has_next(states, dlist_head_node(states)) || - dlist_is_empty(changesStack)) + (!dlist_has_next(states, dlist_head_node(states)) || + dlist_is_empty(changesStack)) ) { removeObject(object, type); return; } - /* If object has been changed in upper level - - * replace state of that level with the current one. */ + /* + * If object has been changed in upper level - replace state of that level + * with the current one. + */ if (isObjectChangedInUpperTrans(object)) { TransState *stateToDelete; dlist_node *nodeToDelete; + nodeToDelete = dlist_next_node(states, dlist_head_node(states)); stateToDelete = dlist_container(TransState, node, nodeToDelete); removeState(object, type, stateToDelete); } - /* If the object does not yet have a record in previous level changesStack, - * create it. */ + + /* + * If the object does not yet have a record in previous level + * changesStack, create it. + */ else if (!dlist_is_empty(changesStack)) addToChangesStackUpperLevel(object, type); @@ -2320,15 +2346,16 @@ addToChangesStackUpperLevel(TransObject *object, TransObjectType type) { ChangedObject *co_new; ChangesStackNode *csn; + /* - * Impossible to push in upper list existing node - * because it was created in another context + * Impossible to push in upper list existing node because it was created + * in another context */ csn = dlist_head_element(ChangesStackNode, node, changesStack); co_new = makeChangedObject(object, csn->ctx); dlist_push_head(type == TRANS_PACKAGE ? csn->changedPacksList : - csn->changedVarsList, - &co_new->node); + csn->changedVarsList, + &co_new->node); } /* @@ -2345,10 +2372,11 @@ isObjectChangedInCurrentTrans(TransObject *object) state = GetActualState(object); return #ifdef PGPRO_EE - /* - * We should separate states with equal subxacts but with - * different ATX level - */ + + /* + * We should separate states with equal subxacts but with different ATX + * level + */ state->levels.atxlevel == getNestLevelATX() && #endif state->levels.level == GetCurrentTransactionNestLevel(); @@ -2373,10 +2401,11 @@ isObjectChangedInUpperTrans(TransObject *object) prev_state = dlist_container(TransState, node, cur_state->node.next); return #ifdef PGPRO_EE - /* - * We should separate states with equal subxacts but with - * different ATX level - */ + + /* + * We should separate states with equal subxacts but with different + * ATX level + */ prev_state->levels.atxlevel == getNestLevelATX() && #endif prev_state->levels.level == GetCurrentTransactionNestLevel() - 1; @@ -2384,10 +2413,11 @@ isObjectChangedInUpperTrans(TransObject *object) else return #ifdef PGPRO_EE - /* - * We should separate states with equal subxacts but with - * different ATX level - */ + + /* + * We should separate states with equal subxacts but with different + * ATX level + */ cur_state->levels.atxlevel == getNestLevelATX() && #endif cur_state->levels.level == GetCurrentTransactionNestLevel() - 1; @@ -2518,9 +2548,7 @@ processChanges(Action action) dlist_pop_head_node(changesStack)); /* - * i: - * 1 - manage variables - * 0 - manage packages + * i: 1 - manage variables 0 - manage packages */ for (i = 1; i > -1; i--) { @@ -2548,7 +2576,7 @@ processChanges(Action action) if (i) { Variable *variable = (Variable *) object; - Package *package = variable->package; + Package *package = variable->package; if (!GetActualState(package)->is_valid) GetActualState(variable)->is_valid = false; @@ -2586,19 +2614,20 @@ processChanges(Action action) static void compatibility_check(void) { - /* - * | Edition | ConnPool | - * ---------------------- - * | std 9.6 | no | - * | std 10 | no | - * | std 11 | no | - * | std 12 | no | - * | std 13 | no | - * | ee 9.6 | no | - * | ee 10 | no | - * | ee 11 | yes | - * | ee 12 | yes | - * | ee 13 | yes | + /* ---------------------- + * | Edition | ConnPool | + * ---------------------- + * | std 9.6 | no | + * | std 10 | no | + * | std 11 | no | + * | std 12 | no | + * | std 13 | no | + * | ee 9.6 | no | + * | ee 10 | no | + * | ee 11 | yes | + * | ee 12 | yes | + * | ee 13 | yes | + * ---------------------- */ #if defined(PGPRO_EE) && PG_VERSION_NUM >= 110000 if (!IsDedicatedBackend) @@ -2606,7 +2635,7 @@ compatibility_check(void) freeStatsLists(); elog(ERROR, "pg_variables extension is incompatible with connection pooling"); } -#endif /* PGPRO_EE */ +#endif /* PGPRO_EE */ } #ifdef PGPRO_EE @@ -2617,10 +2646,10 @@ compatibility_check(void) static void pgvSaveContext(void) { - Package *package; - HASH_SEQ_STATUS pstat; - PgvContextStruct *sus = MemoryContextAlloc(CurTransactionContext, - sizeof(PgvContextStruct)); + Package *package; + HASH_SEQ_STATUS pstat; + PgvContextStruct *sus = MemoryContextAlloc(CurTransactionContext, + sizeof(PgvContextStruct)); /* Save transactional variables for all packages (in packages structs) */ if (packagesHash != NULL) @@ -2630,25 +2659,26 @@ pgvSaveContext(void) while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { PackageContext *context = MemoryContextAlloc(ModuleContext, - sizeof(PackageContext)); + sizeof(PackageContext)); + context->next = package->context; package->context = context; /* Save transactional variables in context */ context->hctxTransact = package->hctxTransact; context->varHashTransact = package->varHashTransact; + /* - * Package structure has a transactional part 'transObject'. - * This part is used in asserts like + * Package structure has a transactional part 'transObject'. This + * part is used in asserts like * Assert(GetActualState(object)->levels.level == - * GetCurrentTransactionNestLevel()) - * But this comparison is not valid for ATX transactions because + * GetCurrentTransactionNestLevel()) But this comparison is not + * valid for ATX transactions because * 'CurrentTransactionState->nestingLevel' for each of new ATX * level is starts with 1. We should save package state at start - * of ATX transaction and restore it at finish. - * No need do this for transactional variables (we clean them at - * end of ATX transaction) and regular variables (we modify them - * directly). + * of ATX transaction and restore it at finish. No need do this + * for transactional variables (we clean them at end of ATX + * transaction) and regular variables (we modify them directly). */ context->state = GetActualState(&package->transObject); @@ -2676,9 +2706,9 @@ pgvSaveContext(void) static void pgvRestoreContext() { - Package *package; - HASH_SEQ_STATUS pstat; - PgvContextStruct *sus = pgv_context; + Package *package; + HASH_SEQ_STATUS pstat; + PgvContextStruct *sus = pgv_context; resetVariablesCache(); /* Delete changes stack for all transactional variables */ @@ -2699,8 +2729,8 @@ pgvRestoreContext() while ((package = (Package *) hash_seq_search(&pstat)) != NULL) { /* - * Delete context with transactional variables (they are - * no need outside ATX transaction) + * Delete context with transactional variables (they are no need + * outside ATX transaction) */ if (package->hctxTransact) MemoryContextDelete(package->hctxTransact); @@ -2708,10 +2738,10 @@ pgvRestoreContext() /* We have stored context for this package? */ if (package->context) { - PackageContext *context = package->context; - PackageContext *next = context->next; - TransObject *object = &package->transObject; - TransState *state; + PackageContext *context = package->context; + PackageContext *next = context->next; + TransObject *object = &package->transObject; + TransState *state; /* Restore transactional variables from context */ package->hctxTransact = context->hctxTransact; @@ -2722,7 +2752,7 @@ pgvRestoreContext() { if (dlist_is_empty(&object->states)) elog(ERROR, "pg_variables extension can not find " - "transaction state for package"); + "transaction state for package"); removeState(object, TRANS_PACKAGE, state); } @@ -2734,6 +2764,7 @@ pgvRestoreContext() /* Package was created in this autonomous transaction */ package->hctxTransact = NULL; package->varHashTransact = NULL; + /* * No need to remove package states: for just created package * we have one state with level = 0 @@ -2743,8 +2774,8 @@ pgvRestoreContext() } /* - * 'sus' can be NULL in case pg_variables was not initialized - * at start of transaction + * 'sus' can be NULL in case pg_variables was not initialized at start of + * transaction */ if (sus) { @@ -2756,7 +2787,7 @@ pgvRestoreContext() pfree(sus); } } -#endif /* PGPRO_EE */ +#endif /* PGPRO_EE */ /* * Intercept execution during subtransaction processing @@ -2765,7 +2796,7 @@ static void pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { - Levels levels; + Levels levels; if (changesStack) { @@ -2829,13 +2860,13 @@ pgvTransCallback(XactEvent event, void *arg) if (getNestLevelATX() > 0) { if (event == XACT_EVENT_START) - { /* on each ATX transaction start */ + { /* on each ATX transaction start */ pgvSaveContext(); } else if (event == XACT_EVENT_ABORT || event == XACT_EVENT_PARALLEL_ABORT || event == XACT_EVENT_COMMIT || event == XACT_EVENT_PARALLEL_COMMIT || event == XACT_EVENT_PREPARE) - { /* on each ATX transaction finish */ + { /* on each ATX transaction finish */ pgvRestoreContext(); } } @@ -2857,16 +2888,17 @@ variable_ExecutorEnd(QueryDesc *queryDesc) } /* - * Free hash_seq_search scans + * Free hash_seq_search scans */ static void freeStatsLists(void) { - ListCell *cell; + ListCell *cell; foreach(cell, variables_stats) { VariableStatEntry *entry = (VariableStatEntry *) lfirst(cell); + hash_seq_term(entry->status); } @@ -2875,6 +2907,7 @@ freeStatsLists(void) foreach(cell, packages_stats) { PackageStatEntry *entry = (PackageStatEntry *) lfirst(cell); + hash_seq_term(entry->status); } diff --git a/pg_variables.h b/pg_variables.h index 864a5e2..70a9399 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -42,7 +42,7 @@ typedef struct RecordVar FmgrInfo hash_proc; /* Match function info */ FmgrInfo cmp_proc; -} RecordVar; +} RecordVar; typedef struct ScalarVar { @@ -55,9 +55,9 @@ typedef struct ScalarVar /* Object levels (subxact + atx) */ typedef struct Levels { - int level; + int level; #ifdef PGPRO_EE - int atxlevel; + int atxlevel; #endif } Levels; @@ -72,8 +72,8 @@ typedef struct TransState /* List node that stores one of the package's states */ typedef struct PackState { - TransState state; - unsigned long trans_var_num; /* Number of valid transactional variables */ + TransState state; + unsigned long trans_var_num; /* Number of valid transactional variables */ } PackState; /* List node that stores one of the variable's states */ @@ -85,22 +85,22 @@ typedef struct VarState ScalarVar scalar; RecordVar record; } value; -} VarState; +} VarState; /* Transactional object */ typedef struct TransObject { char name[NAMEDATALEN]; dlist_head states; -} TransObject; +} TransObject; #ifdef PGPRO_EE /* Package context for save transactional part of package */ typedef struct PackageContext { - HTAB *varHashTransact; + HTAB *varHashTransact; MemoryContext hctxTransact; - TransState *state; + TransState *state; struct PackageContext *next; } PackageContext; #endif @@ -117,7 +117,7 @@ typedef struct Package #ifdef PGPRO_EE PackageContext *context; #endif -} Package; +} Package; /* Transactional variable */ typedef struct Variable @@ -125,6 +125,7 @@ typedef struct Variable TransObject transObject; Package *package; Oid typid; + /* * We need an additional flag to determine variable's type since we can * store record type DATUM within scalar variable @@ -137,7 +138,7 @@ typedef struct Variable */ bool is_transactional; bool is_deleted; -} Variable; +} Variable; typedef struct HashRecordKey { diff --git a/pg_variables_record.c b/pg_variables_record.c index 3d7ca18..99a5142 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -17,10 +17,10 @@ * Split tuptoaster.c into three separate files. */ #if PG_VERSION_NUM >= 130000 -# include "access/detoast.h" -# include "access/heaptoast.h" +#include "access/detoast.h" +#include "access/heaptoast.h" #else -# include "access/tuptoaster.h" +#include "access/tuptoaster.h" #endif #include "catalog/pg_collation.h" @@ -96,8 +96,8 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) /* * In case something went wrong, you need to roll back the changes before - * completing the transaction, because the variable may be regular - * and not present in list of changed vars. + * completing the transaction, because the variable may be regular and not + * present in list of changed vars. */ if (!OidIsValid(typentry->hash_proc_finfo.fn_oid)) { @@ -243,7 +243,7 @@ copy_record_tuple(RecordVar *record, HeapTupleHeader tupleHeader) */ if (HeapTupleHeaderHasExternal(tupleHeader)) return toast_flatten_tuple_to_datum(tupleHeader, - HeapTupleHeaderGetDatumLength(tupleHeader), + HeapTupleHeaderGetDatumLength(tupleHeader), tupdesc); /* @@ -267,9 +267,9 @@ get_record_key(Datum tuple, TupleDesc tupdesc, bool *isnull) { HeapTupleHeader th = (HeapTupleHeader) DatumGetPointer(tuple); bool hasnulls = th->t_infomask & HEAP_HASNULL; - bits8 *bp = th->t_bits; /* ptr to null bitmap in tuple */ - char *tp; /* ptr to tuple data */ - long off; /* offset in tuple data */ + bits8 *bp = th->t_bits; /* ptr to null bitmap in tuple */ + char *tp; /* ptr to tuple data */ + long off; /* offset in tuple data */ int keyatt = 0; Form_pg_attribute attr = GetTupleDescAttr(tupdesc, keyatt); From d37328be1de4ba5bba1e0302a5aee6c0bc12ddb1 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 15 Nov 2021 18:02:54 +0300 Subject: [PATCH 125/147] Refactoring processChanges --- pg_variables.c | 83 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 46c7f24..fa4682c 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2532,6 +2532,46 @@ typedef enum Action ROLLBACK_TO_SAVEPOINT } Action; +/* + * Apply savepoint actions on list of variables or packages. + */ +static void +applyAction(Action action, TransObjectType type, dlist_head *list) +{ + dlist_iter iter; + + dlist_foreach(iter, list) + { + ChangedObject *co = dlist_container(ChangedObject, node, iter.cur); + TransObject *object = co->object; + + switch (action) + { + case ROLLBACK_TO_SAVEPOINT: + rollbackSavepoint(object, type); + break; + case RELEASE_SAVEPOINT: + + /* + * If package was removed in current transaction level mark + * var as removed. We do not check pack_state->level, because + * var cannot get in list of changes until pack is removed. + */ + if (type == TRANS_VARIABLE) + { + Variable *variable = (Variable *) object; + Package *package = variable->package; + + if (!GetActualState(package)->is_valid) + GetActualState(variable)->is_valid = false; + } + + releaseSavepoint(object, type); + break; + } + } +} + /* * Iterate variables and packages from list of changes and * apply corresponding action on them @@ -2540,53 +2580,14 @@ static void processChanges(Action action) { ChangesStackNode *bottom_list; - int i; Assert(changesStack && changesStackContext); /* List removed from stack but we still can use it */ bottom_list = dlist_container(ChangesStackNode, node, dlist_pop_head_node(changesStack)); - /* - * i: 1 - manage variables 0 - manage packages - */ - for (i = 1; i > -1; i--) - { - dlist_iter iter; - - dlist_foreach(iter, i ? bottom_list->changedVarsList : - bottom_list->changedPacksList) - { - ChangedObject *co = dlist_container(ChangedObject, node, iter.cur); - TransObject *object = co->object; - - switch (action) - { - case ROLLBACK_TO_SAVEPOINT: - rollbackSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); - break; - case RELEASE_SAVEPOINT: - - /* - * If package was removed in current transaction level - * mark var as removed. We do not check pack_state->level, - * because var cannot get in list of changes until pack is - * removed. - */ - if (i) - { - Variable *variable = (Variable *) object; - Package *package = variable->package; - - if (!GetActualState(package)->is_valid) - GetActualState(variable)->is_valid = false; - } - - releaseSavepoint(object, i ? TRANS_VARIABLE : TRANS_PACKAGE); - break; - } - } - } + applyAction(action, TRANS_VARIABLE, bottom_list->changedVarsList); + applyAction(action, TRANS_PACKAGE, bottom_list->changedPacksList); /* Remove changes list of current level */ MemoryContextDelete(bottom_list->ctx); From e2a7231a286513453970c1d5e142c05a56234e83 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 7 Dec 2021 01:49:12 +0300 Subject: [PATCH 126/147] Added expected out-file for case (atx + in-memory extension) --- expected/pg_variables_atx_2.out | 497 ++++++++++++++++++++++++++++++++ 1 file changed, 497 insertions(+) create mode 100644 expected/pg_variables_atx_2.out diff --git a/expected/pg_variables_atx_2.out b/expected/pg_variables_atx_2.out new file mode 100644 index 0000000..c6f44a7 --- /dev/null +++ b/expected/pg_variables_atx_2.out @@ -0,0 +1,497 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103); + pgv_set +--------- + +(1 row) + +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int1', 1001); + pgv_set +--------- + +(1 row) + + begin autonomous; +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int2', 1002); + pgv_set +--------- + +(1 row) + + commit; + commit; +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 103 +(1 row) + + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103, true); + pgv_set +--------- + +(1 row) + +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + begin autonomous; + select pgv_set('vars', 'int2', 1002, true); + pgv_set +--------- + +(1 row) + +-- 1002: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 1002 +(1 row) + + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + commit; + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 102 +(1 row) + +rollback; +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 101 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + select pgv_insert('test', 'z', row (11::int, 22::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (22::int, 33::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (33::int, 44::int), false); + pgv_insert +------------ + +(1 row) + + declare r11_cur cursor for select pgv_select('test', 'x'); +-- (1,2),(2,3): + fetch 2 in r11_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + + declare r2_cur cursor for select pgv_select('test', 'y'); +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: unrecognized variable "y" + rollback; + rollback; + rollback; + rollback; + rollback; + declare r2_cur cursor for select pgv_select('test', 'y'); + declare r3_cur cursor for select pgv_select('test', 'z'); +-- (1,2),(2,3): + fetch 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +-- (10,20),(20,30): + fetch 2 in r2_cur; + pgv_select +------------ + (10,20) + (20,30) +(2 rows) + +-- (11,22),(22,33): + fetch 2 in r3_cur; + pgv_select +------------ + (11,22) + (22,33) +(2 rows) + +rollback; +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; +ERROR: in_memory extension is incompatible with autonomous transactions + select pgv_set('vars', 'trans_int', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'trans_int', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +commit; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: in_memory extension is incompatible with autonomous transactions + begin autonomous; +ERROR: in_memory extension is incompatible with autonomous transactions + select pgv_set('vars1', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int2', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int3', 5, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'int2', 3); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + commit; +WARNING: there is no transaction in progress + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +-- 1 + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: unrecognized package "vars1" +-- 3 + select pgv_get('vars1', 'int2', null::int); +ERROR: unrecognized package "vars1" +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +commit; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; +ERROR: in_memory extension is incompatible with autonomous transactions + select pgv_set('vars1', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 2, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +rollback; +WARNING: there is no transaction in progress +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 0 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + From 417ade15264b09221f0eb527a6ff2ac6ebddadcc Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 7 Dec 2021 11:45:22 +0300 Subject: [PATCH 127/147] Added expected out-file for case (atx + in-memory extension) for v10,v11 --- expected/pg_variables_atx_3.out | 497 ++++++++++++++++++++++++++++++++ expected/pg_variables_atx_4.out | 497 ++++++++++++++++++++++++++++++++ 2 files changed, 994 insertions(+) create mode 100644 expected/pg_variables_atx_3.out create mode 100644 expected/pg_variables_atx_4.out diff --git a/expected/pg_variables_atx_3.out b/expected/pg_variables_atx_3.out new file mode 100644 index 0000000..ad1ae89 --- /dev/null +++ b/expected/pg_variables_atx_3.out @@ -0,0 +1,497 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103); + pgv_set +--------- + +(1 row) + +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int1', 1001); + pgv_set +--------- + +(1 row) + + begin autonomous; +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int2', 1002); + pgv_set +--------- + +(1 row) + + commit; + commit; +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 103 +(1 row) + + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103, true); + pgv_set +--------- + +(1 row) + +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + begin autonomous; + select pgv_set('vars', 'int2', 1002, true); + pgv_set +--------- + +(1 row) + +-- 1002: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 1002 +(1 row) + + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + commit; + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 102 +(1 row) + +rollback; +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 101 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + select pgv_insert('test', 'z', row (11::int, 22::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (22::int, 33::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (33::int, 44::int), false); + pgv_insert +------------ + +(1 row) + + declare r11_cur cursor for select pgv_select('test', 'x'); +-- (1,2),(2,3): + fetch 2 in r11_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + + declare r2_cur cursor for select pgv_select('test', 'y'); +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: unrecognized variable "y" + rollback; + rollback; + rollback; + rollback; + rollback; + declare r2_cur cursor for select pgv_select('test', 'y'); + declare r3_cur cursor for select pgv_select('test', 'z'); +-- (1,2),(2,3): + fetch 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +-- (10,20),(20,30): + fetch 2 in r2_cur; + pgv_select +------------ + (10,20) + (20,30) +(2 rows) + +-- (11,22),(22,33): + fetch 2 in r3_cur; + pgv_select +------------ + (11,22) + (22,33) +(2 rows) + +rollback; +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars', 'trans_int', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'trans_int', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +commit; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars1', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: no such savepoint +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int2', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int3', 5, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'int2', 3); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + commit; +WARNING: there is no transaction in progress + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +-- 1 + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: unrecognized package "vars1" +-- 3 + select pgv_get('vars1', 'int2', null::int); +ERROR: unrecognized package "vars1" +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +commit; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars1', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 2, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: no such savepoint +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +rollback; +WARNING: there is no transaction in progress +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 0 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_atx_4.out b/expected/pg_variables_atx_4.out new file mode 100644 index 0000000..914725a --- /dev/null +++ b/expected/pg_variables_atx_4.out @@ -0,0 +1,497 @@ +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------ +-- Non-transactional variables +------------------------------ +select pgv_set('vars', 'int1', 101); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103); + pgv_set +--------- + +(1 row) + +-- 101, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 101 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int1', 1001); + pgv_set +--------- + +(1 row) + + begin autonomous; +-- 1001, 102, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 102 | 103 +(1 row) + + select pgv_set('vars', 'int2', 1002); + pgv_set +--------- + +(1 row) + + commit; + commit; +-- 1001, 1002, 103: + select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 103 +(1 row) + + select pgv_set('vars', 'int3', 1003); + pgv_set +--------- + +(1 row) + +rollback; +-- 1001, 1002, 1003: +select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); + pgv_get | pgv_get | pgv_get +---------+---------+--------- + 1001 | 1002 | 1003 +(1 row) + +-- vars:int1, vars:int2, vars:int3: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | f + vars | int2 | f + vars | int3 | f +(3 rows) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +-------------------------- +-- Transactional variables +-------------------------- +select pgv_set('vars', 'int1', 101, true); + pgv_set +--------- + +(1 row) + +begin; + select pgv_set('vars', 'int2', 102, true); + pgv_set +--------- + +(1 row) + + begin autonomous; + select pgv_set('vars', 'int3', 103, true); + pgv_set +--------- + +(1 row) + +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + begin autonomous; + select pgv_set('vars', 'int2', 1002, true); + pgv_set +--------- + +(1 row) + +-- 1002: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 1002 +(1 row) + + commit; +-- 103: + select pgv_get('vars', 'int3', null::int); + pgv_get +--------- + 103 +(1 row) + + commit; + select pgv_set('vars', 'int1', 1001, true); + pgv_set +--------- + +(1 row) + +-- 1001: + select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1001 +(1 row) + +-- 102: + select pgv_get('vars', 'int2', null::int); + pgv_get +--------- + 102 +(1 row) + +rollback; +-- 101: +select pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 101 +(1 row) + +-- vars:int1: +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + vars | int1 | t +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + +---------- +-- Cursors +---------- +select pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (2::int, 3::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (10::int, 20::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (20::int, 30::int), true); + pgv_insert +------------ + +(1 row) + +select pgv_insert('test', 'y', row (30::int, 40::int), true); + pgv_insert +------------ + +(1 row) + +begin; + declare r1_cur cursor for select pgv_select('test', 'x'); + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + begin autonomous; + select pgv_insert('test', 'z', row (11::int, 22::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (22::int, 33::int), false); + pgv_insert +------------ + +(1 row) + + select pgv_insert('test', 'z', row (33::int, 44::int), false); + pgv_insert +------------ + +(1 row) + + declare r11_cur cursor for select pgv_select('test', 'x'); +-- (1,2),(2,3): + fetch 2 in r11_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + + declare r2_cur cursor for select pgv_select('test', 'y'); +-- correct error: unrecognized variable "y" + fetch 2 in r2_cur; +ERROR: unrecognized variable "y" + rollback; + rollback; + rollback; + rollback; + rollback; + declare r2_cur cursor for select pgv_select('test', 'y'); + declare r3_cur cursor for select pgv_select('test', 'z'); +-- (1,2),(2,3): + fetch 2 in r1_cur; + pgv_select +------------ + (1,2) + (2,3) +(2 rows) + +-- (10,20),(20,30): + fetch 2 in r2_cur; + pgv_select +------------ + (10,20) + (20,30) +(2 rows) + +-- (11,22),(22,33): + fetch 2 in r3_cur; + pgv_select +------------ + (11,22) + (22,33) +(2 rows) + +rollback; +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------ +-- Savepoint: rollback in main transaction +------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 101, true); + pgv_set +--------- + +(1 row) + +-- 101: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 101 +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 102, true); + pgv_set +--------- + +(1 row) + +-- 102: + select pgv_get('vars', 'trans_int', null::int); + pgv_get +--------- + 102 +(1 row) + + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars', 'trans_int', 103, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 103: + select pgv_get('vars', 'trans_int', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +-- 102: + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +commit; +WARNING: there is no transaction in progress +-- 101: +select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------ +-- Savepoint: rollback in autonomous transaction +------------------------------------------------ +begin; + select pgv_set('vars', 'trans_int', 1, true); + pgv_set +--------- + +(1 row) + + savepoint sp1; + select pgv_set('vars', 'trans_int', 100, true); + pgv_set +--------- + +(1 row) + + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars1', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- 4 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 3 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- vars1:int1, vars1:trans_int1: + select * from pgv_list() order by package, name; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int2', 4, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int3', 5, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'int2', 3); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback; + commit; +WARNING: there is no transaction in progress + rollback to sp1; +ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks +-- 1 + select pgv_get('vars', 'trans_int', null::int); +ERROR: unrecognized package "vars" +-- 2 + select pgv_get('vars1', 'int1', null::int); +ERROR: unrecognized package "vars1" +-- 3 + select pgv_get('vars1', 'int2', null::int); +ERROR: unrecognized package "vars1" +-- vars:trans_int, vars1:int1, vars1:int2: + select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +commit; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + +------------------------------------------------------------ +-- Sample with (subxact inside ATX) == (subxact outside ATX) +------------------------------------------------------------ +select pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + +select pgv_set('vars1', 'trans_int1', 0, true); + pgv_set +--------- + +(1 row) + +begin; + begin autonomous; +ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling + select pgv_set('vars1', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 2, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + savepoint sp2; +ERROR: current transaction is aborted, commands ignored until end of transaction block + select pgv_set('vars1', 'trans_int1', 3, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + rollback to sp2; +ERROR: savepoint "sp2" does not exist +-- 2 + select pgv_get('vars1', 'trans_int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + commit; +rollback; +WARNING: there is no transaction in progress +-- vars1:int1, vars1:trans_int1 +select * from pgv_list() order by package, name; + package | name | is_transactional +---------+------------+------------------ + vars1 | int1 | f + vars1 | trans_int1 | t +(2 rows) + +-- 1 +select pgv_get('vars1', 'int1', null::int); + pgv_get +--------- + 0 +(1 row) + +-- 0 +select pgv_get('vars1', 'trans_int1', null::int); + pgv_get +--------- + 0 +(1 row) + +select pgv_free(); + pgv_free +---------- + +(1 row) + From 720adc855ac0f659de15fb36a2133af94478e7fc Mon Sep 17 00:00:00 2001 From: Sofia Kopikova Date: Mon, 27 Dec 2021 00:54:00 +0300 Subject: [PATCH 128/147] replace UNKNOWNOID to TEXTOID --- expected/pg_variables.out | 54 +++++++++++--- expected/pg_variables_trans.out | 4 +- pg_variables.c | 34 ++++++++- pg_variables.h | 7 +- pg_variables_record.c | 122 +++++++++++++++++++++++++++++++- sql/pg_variables.sql | 16 +++-- sql/pg_variables_trans.sql | 2 +- 7 files changed, 220 insertions(+), 19 deletions(-) diff --git a/expected/pg_variables.out b/expected/pg_variables.out index 180ebd1..1e5bee6 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -773,8 +773,8 @@ CLOSE r1_cur; COMMIT; -- warning RESET client_min_messages; -- Clean memory after unsuccessful creation of a variable -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail -ERROR: could not identify a hash function for type unknown +SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); -- fail +ERROR: could not identify a hash function for type record SELECT package FROM pgv_stats() WHERE package = 'vars4'; package --------- @@ -954,20 +954,24 @@ SELECT pgv_select('vars', 'r1'); (1,str1,str2) (1 row) -SELECT pgv_insert('vars', 'r2', row(1, 'str1')); +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', foo) FROM foo; -- ok pgv_insert ------------ (1 row) -SELECT pgv_insert('vars', 'r2', foo) FROM foo; -ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure. -HINT: You may need explicit type casts. SELECT pgv_select('vars', 'r2'); pgv_select ------------ (1,str1) -(1 row) + (0,str00) +(2 rows) SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); pgv_insert @@ -975,7 +979,7 @@ SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); (1 row) -SELECT pgv_insert('vars', 'r3', foo) FROM foo; +SELECT pgv_insert('vars', 'r3', foo) FROM foo; -- ok, no conversions pgv_insert ------------ @@ -988,3 +992,37 @@ SELECT pgv_select('vars', 'r3'); (0,str00) (2 rows) +SELECT pgv_insert('vars', 'r4', row(1, 2::int)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r4', row(0, 'str1')); -- fail, UNKNOWNOID of 'str1' can't be converted to int +ERROR: new record attribute type for attribute number 2 differs from variable "r4" structure. +HINT: You may need explicit type casts. +SELECT pgv_select('vars', 'r4'); + pgv_select +------------ + (1,2) +(1 row) + +SELECT pgv_insert('vars', 'r5', foo) FROM foo; -- types: int, text + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r5', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r5'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 6cede44..09f56c8 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -1858,8 +1858,8 @@ SELECT pgv_insert('package', 'errs',row(1), true); (1 row) -- Variable should not exists in case when error occurs during creation -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -ERROR: could not identify a hash function for type unknown +SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); +ERROR: could not identify a hash function for type record SELECT pgv_select('vars4', 'r1', 0); ERROR: unrecognized package "vars4" -- If variable created and removed in same transaction level, diff --git a/pg_variables.c b/pg_variables.c index bcd0e21..f658541 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -114,6 +114,10 @@ do { \ } while(0) #endif /* PG_VERSION_NUM */ +/* User controlled GUCs */ +bool convert_unknownoid_guc; +bool convert_unknownoid; + static HTAB *packagesHash = NULL; static MemoryContext ModuleContext = NULL; @@ -701,6 +705,14 @@ variable_insert(PG_FUNCTION_ARGS) /* * This is the first record for the var_name. Initialize record. */ + /* Convert UNKNOWNOID to TEXTOID if needed + * tupdesc may be changed + */ + if (convert_unknownoid) + { + coerce_unknown_first_record(&tupdesc, &rec); + } + init_record(record, tupdesc, variable); variable->is_deleted = false; } @@ -709,8 +721,11 @@ variable_insert(PG_FUNCTION_ARGS) /* * We need to check attributes of the new row if this is a transient * record type or if last record has different id. + * Also we convert UNKNOWNOID to TEXTOID if needed. + * tupdesc may be changed */ - check_attributes(variable, tupdesc); + check_attributes(variable, &rec, tupdesc); + } insert_record(variable, rec); @@ -791,7 +806,11 @@ variable_update(PG_FUNCTION_ARGS) tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - check_attributes(variable, tupdesc); + /* + * Convert UNKNOWNOID to TEXTOID if needed + * tupdesc may be changed + */ + check_attributes(variable, &rec, tupdesc); ReleaseTupleDesc(tupdesc); res = update_record(variable, rec); @@ -2622,6 +2641,17 @@ freeStatsLists(void) void _PG_init(void) { + DefineCustomBoolVariable("pg_variables.convert_unknownoid", + "Use \'TEXT\' format for all values of \'UNKNOWNOID\', default is true.", + NULL, + &convert_unknownoid, + true, + PGC_USERSET, + 0, /* FLAGS??? */ + NULL, + NULL, + NULL); + RegisterXactCallback(pgvTransCallback, NULL); RegisterSubXactCallback(pgvSubTransCallback, NULL); diff --git a/pg_variables.h b/pg_variables.h index b08faa6..52d8c84 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -16,6 +16,7 @@ #include "access/tupdesc.h" #include "datatype/timestamp.h" #include "utils/date.h" +#include "utils/guc.h" #include "utils/hsearch.h" #include "utils/numeric.h" #include "utils/jsonb.h" @@ -155,8 +156,12 @@ typedef struct ChangesStackNode MemoryContext ctx; } ChangesStackNode; +/* pg_variables.c */ +extern bool convert_unknownoid; + extern void init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable); -extern void check_attributes(Variable *variable, TupleDesc tupdesc); +extern void check_attributes(Variable *variable, HeapTupleHeader *rec, TupleDesc tupdesc); +extern void coerce_unknown_first_record(TupleDesc *tupdesc, HeapTupleHeader * rec); extern void check_record_key(Variable *variable, Oid typid); extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader); diff --git a/pg_variables_record.c b/pg_variables_record.c index 3d7ca18..b067758 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -25,8 +25,11 @@ #include "catalog/pg_collation.h" #include "catalog/pg_type.h" +#include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" #include "utils/memutils.h" #include "utils/typcache.h" @@ -172,14 +175,118 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable) MemoryContextSwitchTo(oldcxt); } +/* Check if any attributes of type UNKNOWNOID are in given tupdesc */ +static int +is_unknownoid_in_tupdesc(TupleDesc tupdesc) +{ + int i = 0; + for (i = 0; i < tupdesc->natts; i++) + { + Form_pg_attribute attr = GetTupleDescAttr(tupdesc, i); + + if (attr->atttypid == UNKNOWNOID) + return true; + + } + return false; +} + +/* Replace all attributes of type UNKNOWNOID to TEXTOID in given tupdesc */ +static void +coerce_unknown_rewrite_tupdesc(TupleDesc old_tupdesc, TupleDesc *return_tupdesc) +{ + int i; + + (*return_tupdesc) = CreateTupleDescCopy(old_tupdesc); + + for (i = 0; i < old_tupdesc->natts; i++) + { + Form_pg_attribute attr = GetTupleDescAttr(old_tupdesc, i); + + if (attr->atttypid == UNKNOWNOID) + { + FormData_pg_attribute new_attr = *attr; + + new_attr.atttypid = TEXTOID; + new_attr.attlen = -1; + new_attr.atttypmod = -1; + memcpy(TupleDescAttr((*return_tupdesc), i), &new_attr, sizeof(FormData_pg_attribute)); + } + } +} + +/* + * Deform tuple with old_tupdesc, coerce values of type UNKNOWNOID to TEXTOID, form tuple with new_tupdesc. + * new_tupdesc must have the same attributes as old_tupdesc except such of types UNKNOWNOID -- they must be of TEXTOID type + */ +static void +reconstruct_tuple(TupleDesc old_tupdesc, TupleDesc new_tupdesc, HeapTupleHeader *rec) +{ + HeapTupleData tuple; + HeapTuple newtup; + Datum *values = (Datum*)palloc(old_tupdesc->natts * sizeof(Datum)); + bool *isnull = (bool*)palloc(old_tupdesc->natts * sizeof(bool)); + Oid baseTypeId = UNKNOWNOID; + int32 baseTypeMod = -1; + int32 inputTypeMod = -1; + Type baseType = NULL; + int i; + + baseTypeId = getBaseTypeAndTypmod(TEXTOID, &baseTypeMod); + baseType = typeidType(baseTypeId); + /* Build a temporary HeapTuple control structure */ + tuple.t_len = HeapTupleHeaderGetDatumLength(*rec); + tuple.t_data = *rec; + heap_deform_tuple(&tuple, old_tupdesc, values, isnull); + + for (i = 0; i < old_tupdesc->natts; i++) + { + Form_pg_attribute attr = GetTupleDescAttr(old_tupdesc, i); + + if (attr->atttypid == UNKNOWNOID) + { + values[i] = stringTypeDatum(baseType, + DatumGetCString(values[i]), + inputTypeMod); + } + } + + newtup = heap_form_tuple(new_tupdesc, values, isnull); + (*rec) = newtup->t_data; + pfree(isnull); + pfree(values); + ReleaseSysCache(baseType); +} + +/* + * Used in pg_variables.c insert_record for coercing types in first record in variable. + * If there are UNKNOWNOIDs in tupdesc, rewrites it and reconstructs tuple with new tupdesc. + * Replaces given tupdesc with the new one. + */ +void +coerce_unknown_first_record(TupleDesc *tupdesc, HeapTupleHeader *rec) +{ + TupleDesc new_tupdesc = NULL; + + if (!is_unknownoid_in_tupdesc(*tupdesc)) + return; + + coerce_unknown_rewrite_tupdesc(*tupdesc, &new_tupdesc); + reconstruct_tuple(*tupdesc, new_tupdesc, rec); + + ReleaseTupleDesc(*tupdesc); + (*tupdesc) = new_tupdesc; +} + /* * New record structure should be the same as the first record. */ void -check_attributes(Variable *variable, TupleDesc tupdesc) +check_attributes(Variable *variable, HeapTupleHeader *rec, TupleDesc tupdesc) { int i; RecordVar *record; + bool unknowns = false; Assert(variable->typid == RECORDOID); @@ -198,6 +305,16 @@ check_attributes(Variable *variable, TupleDesc tupdesc) Form_pg_attribute attr1 = GetTupleDescAttr(record->tupdesc, i), attr2 = GetTupleDescAttr(tupdesc, i); + /* + * For the sake of convenience, we consider all the unknown types are to be + * a text type. + */ + if (convert_unknownoid && (attr1->atttypid == TEXTOID) && (attr2->atttypid == UNKNOWNOID)) + { + unknowns = true; + continue; + } + if ((attr1->atttypid != attr2->atttypid) || (attr1->attndims != attr2->attndims) || (attr1->atttypmod != attr2->atttypmod)) @@ -208,6 +325,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc) i + 1, GetName(variable)), errhint("You may need explicit type casts."))); } + + if (unknowns) + reconstruct_tuple(tupdesc, record->tupdesc, rec); } /* diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index bad7976..4b9a3d2 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -222,7 +222,7 @@ COMMIT; -- warning RESET client_min_messages; -- Clean memory after unsuccessful creation of a variable -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail +SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); -- fail SELECT package FROM pgv_stats() WHERE package = 'vars4'; -- Remove package if it is empty @@ -268,10 +268,18 @@ SELECT pgv_select('vars', 'r1'); SELECT pgv_insert('vars', 'r1', foo) FROM foo; SELECT pgv_select('vars', 'r1'); -SELECT pgv_insert('vars', 'r2', row(1, 'str1')); -SELECT pgv_insert('vars', 'r2', foo) FROM foo; +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID +SELECT pgv_insert('vars', 'r2', foo) FROM foo; -- ok SELECT pgv_select('vars', 'r2'); SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); -SELECT pgv_insert('vars', 'r3', foo) FROM foo; +SELECT pgv_insert('vars', 'r3', foo) FROM foo; -- ok, no conversions SELECT pgv_select('vars', 'r3'); + +SELECT pgv_insert('vars', 'r4', row(1, 2::int)); +SELECT pgv_insert('vars', 'r4', row(0, 'str1')); -- fail, UNKNOWNOID of 'str1' can't be converted to int +SELECT pgv_select('vars', 'r4'); + +SELECT pgv_insert('vars', 'r5', foo) FROM foo; -- types: int, text +SELECT pgv_insert('vars', 'r5', row(1, 'str1')); -- ok, UNKNOWNOID of 'str1' converts to TEXTOID +SELECT pgv_select('vars', 'r5'); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 7deef59..ae69df7 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -470,7 +470,7 @@ FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; SELECT pgv_insert('package', 'errs',row(1), true); -- Variable should not exists in case when error occurs during creation -SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); +SELECT pgv_insert('vars4', 'r1', row (('str1'::text, 'str1'::text))); SELECT pgv_select('vars4', 'r1', 0); -- If variable created and removed in same transaction level, From b317eb0cc727a6fb834073cd384e4d7d72fdfc96 Mon Sep 17 00:00:00 2001 From: Sofia Kopikova Date: Tue, 22 Mar 2022 12:39:01 +0300 Subject: [PATCH 129/147] update copyrigth notices --- LICENSE | 2 +- pg_variables.c | 2 +- pg_variables.h | 2 +- pg_variables_record.c | 2 +- run_tests.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index f340ae8..9c8fe9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ pg_variables is released under the PostgreSQL License, a liberal Open Source license, similar to the BSD or MIT licenses. -Copyright (c) 2016-2018, Postgres Professional +Copyright (c) 2016-2022, Postgres Professional Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group Portions Copyright (c) 1994, The Regents of the University of California diff --git a/pg_variables.c b/pg_variables.c index 837f35e..b1d1b47 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -3,7 +3,7 @@ * pg_variables.c * Functions, which get or set variables values * - * Copyright (c) 2015-2021, Postgres Professional + * Copyright (c) 2015-2022, Postgres Professional * *------------------------------------------------------------------------- */ diff --git a/pg_variables.h b/pg_variables.h index aca7e93..afa9b22 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -3,7 +3,7 @@ * pg_variables.c * exported definitions for pg_variables.c * - * Copyright (c) 2015-2016, Postgres Professional + * Copyright (c) 2015-2022, Postgres Professional * *------------------------------------------------------------------------- */ diff --git a/pg_variables_record.c b/pg_variables_record.c index 1aeeadd..dadb14f 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -3,7 +3,7 @@ * pg_variables_record.c * Functions to work with record types * - * Copyright (c) 2015-2016, Postgres Professional + * Copyright (c) 2015-2022, Postgres Professional * *------------------------------------------------------------------------- */ diff --git a/run_tests.sh b/run_tests.sh index 2574328..2e63dad 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # -# Copyright (c) 2018, Postgres Professional +# Copyright (c) 2018-2022, Postgres Professional # # supported levels: # * standard From 0806ab15b5f549dfbd8f686dfcbbf6b99601c686 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 31 May 2022 14:20:27 +0300 Subject: [PATCH 130/147] [PGPRO-5833] Changes for tests with nightmare level (travis-ci) --- run_tests.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 2574328..ad0cd67 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -43,7 +43,7 @@ if [ "$LEVEL" = "hardcore" ] || \ # enable additional options ./configure \ CFLAGS='-O0 -ggdb3 -fno-omit-frame-pointer' \ - --enable-cassert \ + --enable-cassert --enable-debug \ --prefix=$CUSTOM_PG_BIN \ --quiet @@ -100,6 +100,7 @@ if [ "$LEVEL" = "nightmare" ]; then --time-stamp=yes \ --track-origins=yes \ --trace-children=yes \ + --trace-children-skip="/bin/*,/usr/bin/*,/lib/*" \ --gen-suppressions=all \ --suppressions=$CUSTOM_PG_SRC/src/tools/valgrind.supp \ --suppressions=$PWD/valgrind.supp \ From d4786bf45122acfeb8da8661a0ff0ce6e1f426dc Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 31 May 2022 17:06:49 +0300 Subject: [PATCH 131/147] [PGPRO-5833] Added tests for v14 + tests with nightmare level for v11, v12 --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f7474d..9b62198 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,18 +20,19 @@ notifications: on_failure: always env: + - PG_VERSION=14 LEVEL=nightmare + - PG_VERSION=14 LEVEL=hardcore + - PG_VERSION=14 - PG_VERSION=13 LEVEL=nightmare - PG_VERSION=13 LEVEL=hardcore - PG_VERSION=13 - # - PG_VERSION=12 LEVEL=nightmare + - PG_VERSION=12 LEVEL=nightmare - PG_VERSION=12 LEVEL=hardcore - PG_VERSION=12 - # - PG_VERSION=11 LEVEL=nightmare + - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - PG_VERSION=10 - - PG_VERSION=9.6 - - PG_VERSION=9.5 # XXX: consider fixing nightmare mode matrix: @@ -39,3 +40,4 @@ matrix: - env: PG_VERSION=11 LEVEL=nightmare - env: PG_VERSION=12 LEVEL=nightmare - env: PG_VERSION=13 LEVEL=nightmare + - env: PG_VERSION=14 LEVEL=nightmare From 9f9466c2b9193133446851b1f4b2b5b192d8f0e0 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 9 Sep 2022 17:22:21 +0300 Subject: [PATCH 132/147] Remove _PG_fini call Due to upstream commit ab02d702, _PG_fini was removed. To be consistent, we remove this particular call too. --- pg_variables.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index b1d1b47..5c52c79 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -47,7 +47,9 @@ PG_FUNCTION_INFO_V1(get_packages_and_variables); PG_FUNCTION_INFO_V1(get_packages_stats); extern void _PG_init(void); +#if PG_VERSION_NUM < 150000 extern void _PG_fini(void); +#endif static void ensurePackagesHashExists(void); static void getKeyFromName(text *name, char *key); @@ -2959,6 +2961,7 @@ _PG_init(void) ExecutorEnd_hook = variable_ExecutorEnd; } +#if PG_VERSION_NUM < 150000 /* * Unregister callback function when module unloads */ @@ -2969,3 +2972,4 @@ _PG_fini(void) UnregisterSubXactCallback(pgvSubTransCallback, NULL); ExecutorEnd_hook = prev_ExecutorEnd; } +#endif From 9c3d02817d85ce676c4bc43c5574d11b297ad11a Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Wed, 14 Dec 2022 11:41:58 +0300 Subject: [PATCH 133/147] Remove AssertArg See the commit b1099eca8f38ff5cfaf0901bb91cb6a22f909bc6 (Remove AssertArg and AssertState) in PostgreSQL 16. --- pg_variables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index 5c52c79..9c571d7 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -1582,7 +1582,7 @@ getMemoryTotalSpace(MemoryContext context, int level, Size *totalspace) MemoryContext child; MemoryContextCounters totals; - AssertArg(MemoryContextIsValid(context)); + Assert(MemoryContextIsValid(context)); /* Examine the context itself */ memset(&totals, 0, sizeof(totals)); From ffdf2427b7914890e041d1ec52c1c85e84e9e23a Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Mon, 19 Dec 2022 11:47:25 +0300 Subject: [PATCH 134/147] Add support for PG15 in travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9b62198..228302a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,9 @@ notifications: on_failure: always env: + - PG_VERSION=15 LEVEL=nightmare + - PG_VERSION=15 LEVEL=hardcore + - PG_VERSION=15 - PG_VERSION=14 LEVEL=nightmare - PG_VERSION=14 LEVEL=hardcore - PG_VERSION=14 @@ -41,3 +44,4 @@ matrix: - env: PG_VERSION=12 LEVEL=nightmare - env: PG_VERSION=13 LEVEL=nightmare - env: PG_VERSION=14 LEVEL=nightmare + - env: PG_VERSION=15 LEVEL=nightmare From 43aac50a9314466ff91857294fd5584b1aa11f7b Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Mon, 26 Dec 2022 19:34:27 +0300 Subject: [PATCH 135/147] [PGPRO-6577] Remove obsolete test results (incl. v10) Tags: pg_variables --- .travis.yml | 1 - expected/pg_variables_atx_1.out | 465 ------------------------------ expected/pg_variables_atx_2.out | 497 -------------------------------- expected/pg_variables_atx_3.out | 497 -------------------------------- expected/pg_variables_atx_4.out | 497 -------------------------------- 5 files changed, 1957 deletions(-) delete mode 100644 expected/pg_variables_atx_1.out delete mode 100644 expected/pg_variables_atx_2.out delete mode 100644 expected/pg_variables_atx_3.out delete mode 100644 expected/pg_variables_atx_4.out diff --git a/.travis.yml b/.travis.yml index 228302a..45189e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,6 @@ env: - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - - PG_VERSION=10 # XXX: consider fixing nightmare mode matrix: diff --git a/expected/pg_variables_atx_1.out b/expected/pg_variables_atx_1.out deleted file mode 100644 index b5d8a07..0000000 --- a/expected/pg_variables_atx_1.out +++ /dev/null @@ -1,465 +0,0 @@ -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------- --- Non-transactional variables ------------------------------- -select pgv_set('vars', 'int1', 101); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars', 'int3', 103); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 101, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars', 'int1', 1001); -ERROR: current transaction is aborted, commands ignored until end of transaction block - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ --- 1001, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars', 'int2', 1002); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; - commit; -WARNING: there is no transaction in progress --- 1001, 1002, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); -ERROR: unrecognized variable "int3" - select pgv_set('vars', 'int3', 1003); - pgv_set ---------- - -(1 row) - -rollback; -WARNING: there is no transaction in progress --- 1001, 1002, 1003: -select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 101 | 102 | 1003 -(1 row) - --- vars:int1, vars:int2, vars:int3: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | f - vars | int2 | f - vars | int3 | f -(3 rows) - -select pgv_free(); - pgv_free ----------- - -(1 row) - --------------------------- --- Transactional variables --------------------------- -select pgv_set('vars', 'int1', 101, true); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102, true); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars', 'int3', 103, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 103: - select pgv_get('vars', 'int3', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars', 'int2', 1002, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 1002: - select pgv_get('vars', 'int2', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; --- 103: - select pgv_get('vars', 'int3', null::int); -ERROR: unrecognized variable "int3" - commit; -WARNING: there is no transaction in progress - select pgv_set('vars', 'int1', 1001, true); - pgv_set ---------- - -(1 row) - --- 1001: - select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 1001 -(1 row) - --- 102: - select pgv_get('vars', 'int2', null::int); -ERROR: unrecognized variable "int2" -rollback; -WARNING: there is no transaction in progress --- 101: -select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 1001 -(1 row) - --- vars:int1: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | t -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - ----------- --- Cursors ----------- -select pgv_insert('test', 'x', row (1::int, 2::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (2::int, 3::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (3::int, 4::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (10::int, 20::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (20::int, 30::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (30::int, 40::int), true); - pgv_insert ------------- - -(1 row) - -begin; - declare r1_cur cursor for select pgv_select('test', 'x'); - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_insert('test', 'z', row (11::int, 22::int), false); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_insert('test', 'z', row (22::int, 33::int), false); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_insert('test', 'z', row (33::int, 44::int), false); -ERROR: current transaction is aborted, commands ignored until end of transaction block - declare r11_cur cursor for select pgv_select('test', 'x'); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- (1,2),(2,3): - fetch 2 in r11_cur; -ERROR: current transaction is aborted, commands ignored until end of transaction block - declare r2_cur cursor for select pgv_select('test', 'y'); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- correct error: unrecognized variable "y" - fetch 2 in r2_cur; -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback; - rollback; -WARNING: there is no transaction in progress - rollback; -WARNING: there is no transaction in progress - rollback; -WARNING: there is no transaction in progress - rollback; -WARNING: there is no transaction in progress - declare r2_cur cursor for select pgv_select('test', 'y'); -ERROR: DECLARE CURSOR can only be used in transaction blocks - declare r3_cur cursor for select pgv_select('test', 'z'); -ERROR: DECLARE CURSOR can only be used in transaction blocks --- (1,2),(2,3): - fetch 2 in r1_cur; -ERROR: cursor "r1_cur" does not exist --- (10,20),(20,30): - fetch 2 in r2_cur; -ERROR: cursor "r2_cur" does not exist --- (11,22),(22,33): - fetch 2 in r3_cur; -ERROR: cursor "r3_cur" does not exist -rollback; -WARNING: there is no transaction in progress -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------- --- Savepoint: rollback in main transaction ------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 101, true); - pgv_set ---------- - -(1 row) - --- 101: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 101 -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 102, true); - pgv_set ---------- - -(1 row) - --- 102: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 102 -(1 row) - - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars', 'trans_int', 103, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 103: - select pgv_get('vars', 'trans_int', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; --- 102: - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -commit; -WARNING: there is no transaction in progress --- 101: -select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------- --- Savepoint: rollback in autonomous transaction ------------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 1, true); - pgv_set ---------- - -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 100, true); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars1', 'int1', 2); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 4 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: no such savepoint --- 3 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- vars1:int1, vars1:trans_int1: - select * from pgv_list() order by package, name; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int2', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int3', 5, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'int2', 3); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback; - commit; -WARNING: there is no transaction in progress - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks --- 1 - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: unrecognized package "vars1" --- 3 - select pgv_get('vars1', 'int2', null::int); -ERROR: unrecognized package "vars1" --- vars:trans_int, vars1:int1, vars1:int2: - select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -commit; -WARNING: there is no transaction in progress -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------------------- --- Sample with (subxact inside ATX) == (subxact outside ATX) ------------------------------------------------------------- -select pgv_set('vars1', 'int1', 0); - pgv_set ---------- - -(1 row) - -select pgv_set('vars1', 'trans_int1', 0, true); - pgv_set ---------- - -(1 row) - -begin; - begin autonomous; -ERROR: syntax error at or near "autonomous" -LINE 1: begin autonomous; - ^ - select pgv_set('vars1', 'int1', 1); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 2, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: no such savepoint --- 2 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; -rollback; -WARNING: there is no transaction in progress --- vars1:int1, vars1:trans_int1 -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------------+------------------ - vars1 | int1 | f - vars1 | trans_int1 | t -(2 rows) - --- 1 -select pgv_get('vars1', 'int1', null::int); - pgv_get ---------- - 0 -(1 row) - --- 0 -select pgv_get('vars1', 'trans_int1', null::int); - pgv_get ---------- - 0 -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/expected/pg_variables_atx_2.out b/expected/pg_variables_atx_2.out deleted file mode 100644 index c6f44a7..0000000 --- a/expected/pg_variables_atx_2.out +++ /dev/null @@ -1,497 +0,0 @@ -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------- --- Non-transactional variables ------------------------------- -select pgv_set('vars', 'int1', 101); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103); - pgv_set ---------- - -(1 row) - --- 101, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 101 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int1', 1001); - pgv_set ---------- - -(1 row) - - begin autonomous; --- 1001, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int2', 1002); - pgv_set ---------- - -(1 row) - - commit; - commit; --- 1001, 1002, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 103 -(1 row) - - select pgv_set('vars', 'int3', 1003); - pgv_set ---------- - -(1 row) - -rollback; --- 1001, 1002, 1003: -select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 1003 -(1 row) - --- vars:int1, vars:int2, vars:int3: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | f - vars | int2 | f - vars | int3 | f -(3 rows) - -select pgv_free(); - pgv_free ----------- - -(1 row) - --------------------------- --- Transactional variables --------------------------- -select pgv_set('vars', 'int1', 101, true); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102, true); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103, true); - pgv_set ---------- - -(1 row) - --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - begin autonomous; - select pgv_set('vars', 'int2', 1002, true); - pgv_set ---------- - -(1 row) - --- 1002: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 1002 -(1 row) - - commit; --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - commit; - select pgv_set('vars', 'int1', 1001, true); - pgv_set ---------- - -(1 row) - --- 1001: - select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 1001 -(1 row) - --- 102: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 102 -(1 row) - -rollback; --- 101: -select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 101 -(1 row) - --- vars:int1: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | t -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - ----------- --- Cursors ----------- -select pgv_insert('test', 'x', row (1::int, 2::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (2::int, 3::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (3::int, 4::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (10::int, 20::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (20::int, 30::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (30::int, 40::int), true); - pgv_insert ------------- - -(1 row) - -begin; - declare r1_cur cursor for select pgv_select('test', 'x'); - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - select pgv_insert('test', 'z', row (11::int, 22::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (22::int, 33::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (33::int, 44::int), false); - pgv_insert ------------- - -(1 row) - - declare r11_cur cursor for select pgv_select('test', 'x'); --- (1,2),(2,3): - fetch 2 in r11_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - - declare r2_cur cursor for select pgv_select('test', 'y'); --- correct error: unrecognized variable "y" - fetch 2 in r2_cur; -ERROR: unrecognized variable "y" - rollback; - rollback; - rollback; - rollback; - rollback; - declare r2_cur cursor for select pgv_select('test', 'y'); - declare r3_cur cursor for select pgv_select('test', 'z'); --- (1,2),(2,3): - fetch 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - --- (10,20),(20,30): - fetch 2 in r2_cur; - pgv_select ------------- - (10,20) - (20,30) -(2 rows) - --- (11,22),(22,33): - fetch 2 in r3_cur; - pgv_select ------------- - (11,22) - (22,33) -(2 rows) - -rollback; -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------- --- Savepoint: rollback in main transaction ------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 101, true); - pgv_set ---------- - -(1 row) - --- 101: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 101 -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 102, true); - pgv_set ---------- - -(1 row) - --- 102: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 102 -(1 row) - - begin autonomous; -ERROR: in_memory extension is incompatible with autonomous transactions - select pgv_set('vars', 'trans_int', 103, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 103: - select pgv_get('vars', 'trans_int', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; --- 102: - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -commit; -WARNING: there is no transaction in progress --- 101: -select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------- --- Savepoint: rollback in autonomous transaction ------------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 1, true); - pgv_set ---------- - -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 100, true); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: in_memory extension is incompatible with autonomous transactions - begin autonomous; -ERROR: in_memory extension is incompatible with autonomous transactions - select pgv_set('vars1', 'int1', 2); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 4 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: savepoint "sp2" does not exist --- 3 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- vars1:int1, vars1:trans_int1: - select * from pgv_list() order by package, name; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int2', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int3', 5, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'int2', 3); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback; - commit; -WARNING: there is no transaction in progress - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks --- 1 - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: unrecognized package "vars1" --- 3 - select pgv_get('vars1', 'int2', null::int); -ERROR: unrecognized package "vars1" --- vars:trans_int, vars1:int1, vars1:int2: - select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -commit; -WARNING: there is no transaction in progress -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------------------- --- Sample with (subxact inside ATX) == (subxact outside ATX) ------------------------------------------------------------- -select pgv_set('vars1', 'int1', 0); - pgv_set ---------- - -(1 row) - -select pgv_set('vars1', 'trans_int1', 0, true); - pgv_set ---------- - -(1 row) - -begin; - begin autonomous; -ERROR: in_memory extension is incompatible with autonomous transactions - select pgv_set('vars1', 'int1', 1); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 2, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: savepoint "sp2" does not exist --- 2 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; -rollback; -WARNING: there is no transaction in progress --- vars1:int1, vars1:trans_int1 -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------------+------------------ - vars1 | int1 | f - vars1 | trans_int1 | t -(2 rows) - --- 1 -select pgv_get('vars1', 'int1', null::int); - pgv_get ---------- - 0 -(1 row) - --- 0 -select pgv_get('vars1', 'trans_int1', null::int); - pgv_get ---------- - 0 -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/expected/pg_variables_atx_3.out b/expected/pg_variables_atx_3.out deleted file mode 100644 index ad1ae89..0000000 --- a/expected/pg_variables_atx_3.out +++ /dev/null @@ -1,497 +0,0 @@ -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------- --- Non-transactional variables ------------------------------- -select pgv_set('vars', 'int1', 101); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103); - pgv_set ---------- - -(1 row) - --- 101, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 101 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int1', 1001); - pgv_set ---------- - -(1 row) - - begin autonomous; --- 1001, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int2', 1002); - pgv_set ---------- - -(1 row) - - commit; - commit; --- 1001, 1002, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 103 -(1 row) - - select pgv_set('vars', 'int3', 1003); - pgv_set ---------- - -(1 row) - -rollback; --- 1001, 1002, 1003: -select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 1003 -(1 row) - --- vars:int1, vars:int2, vars:int3: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | f - vars | int2 | f - vars | int3 | f -(3 rows) - -select pgv_free(); - pgv_free ----------- - -(1 row) - --------------------------- --- Transactional variables --------------------------- -select pgv_set('vars', 'int1', 101, true); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102, true); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103, true); - pgv_set ---------- - -(1 row) - --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - begin autonomous; - select pgv_set('vars', 'int2', 1002, true); - pgv_set ---------- - -(1 row) - --- 1002: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 1002 -(1 row) - - commit; --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - commit; - select pgv_set('vars', 'int1', 1001, true); - pgv_set ---------- - -(1 row) - --- 1001: - select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 1001 -(1 row) - --- 102: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 102 -(1 row) - -rollback; --- 101: -select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 101 -(1 row) - --- vars:int1: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | t -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - ----------- --- Cursors ----------- -select pgv_insert('test', 'x', row (1::int, 2::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (2::int, 3::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (3::int, 4::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (10::int, 20::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (20::int, 30::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (30::int, 40::int), true); - pgv_insert ------------- - -(1 row) - -begin; - declare r1_cur cursor for select pgv_select('test', 'x'); - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - select pgv_insert('test', 'z', row (11::int, 22::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (22::int, 33::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (33::int, 44::int), false); - pgv_insert ------------- - -(1 row) - - declare r11_cur cursor for select pgv_select('test', 'x'); --- (1,2),(2,3): - fetch 2 in r11_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - - declare r2_cur cursor for select pgv_select('test', 'y'); --- correct error: unrecognized variable "y" - fetch 2 in r2_cur; -ERROR: unrecognized variable "y" - rollback; - rollback; - rollback; - rollback; - rollback; - declare r2_cur cursor for select pgv_select('test', 'y'); - declare r3_cur cursor for select pgv_select('test', 'z'); --- (1,2),(2,3): - fetch 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - --- (10,20),(20,30): - fetch 2 in r2_cur; - pgv_select ------------- - (10,20) - (20,30) -(2 rows) - --- (11,22),(22,33): - fetch 2 in r3_cur; - pgv_select ------------- - (11,22) - (22,33) -(2 rows) - -rollback; -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------- --- Savepoint: rollback in main transaction ------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 101, true); - pgv_set ---------- - -(1 row) - --- 101: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 101 -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 102, true); - pgv_set ---------- - -(1 row) - --- 102: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 102 -(1 row) - - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars', 'trans_int', 103, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 103: - select pgv_get('vars', 'trans_int', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; --- 102: - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -commit; -WARNING: there is no transaction in progress --- 101: -select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------- --- Savepoint: rollback in autonomous transaction ------------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 1, true); - pgv_set ---------- - -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 100, true); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars1', 'int1', 2); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 4 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: no such savepoint --- 3 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- vars1:int1, vars1:trans_int1: - select * from pgv_list() order by package, name; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int2', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int3', 5, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'int2', 3); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback; - commit; -WARNING: there is no transaction in progress - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks --- 1 - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: unrecognized package "vars1" --- 3 - select pgv_get('vars1', 'int2', null::int); -ERROR: unrecognized package "vars1" --- vars:trans_int, vars1:int1, vars1:int2: - select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -commit; -WARNING: there is no transaction in progress -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------------------- --- Sample with (subxact inside ATX) == (subxact outside ATX) ------------------------------------------------------------- -select pgv_set('vars1', 'int1', 0); - pgv_set ---------- - -(1 row) - -select pgv_set('vars1', 'trans_int1', 0, true); - pgv_set ---------- - -(1 row) - -begin; - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars1', 'int1', 1); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 2, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: no such savepoint --- 2 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; -rollback; -WARNING: there is no transaction in progress --- vars1:int1, vars1:trans_int1 -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------------+------------------ - vars1 | int1 | f - vars1 | trans_int1 | t -(2 rows) - --- 1 -select pgv_get('vars1', 'int1', null::int); - pgv_get ---------- - 0 -(1 row) - --- 0 -select pgv_get('vars1', 'trans_int1', null::int); - pgv_get ---------- - 0 -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - diff --git a/expected/pg_variables_atx_4.out b/expected/pg_variables_atx_4.out deleted file mode 100644 index 914725a..0000000 --- a/expected/pg_variables_atx_4.out +++ /dev/null @@ -1,497 +0,0 @@ -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------- --- Non-transactional variables ------------------------------- -select pgv_set('vars', 'int1', 101); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103); - pgv_set ---------- - -(1 row) - --- 101, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 101 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int1', 1001); - pgv_set ---------- - -(1 row) - - begin autonomous; --- 1001, 102, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 102 | 103 -(1 row) - - select pgv_set('vars', 'int2', 1002); - pgv_set ---------- - -(1 row) - - commit; - commit; --- 1001, 1002, 103: - select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 103 -(1 row) - - select pgv_set('vars', 'int3', 1003); - pgv_set ---------- - -(1 row) - -rollback; --- 1001, 1002, 1003: -select pgv_get('vars', 'int1', null::int), pgv_get('vars', 'int2', null::int), pgv_get('vars', 'int3', null::int); - pgv_get | pgv_get | pgv_get ----------+---------+--------- - 1001 | 1002 | 1003 -(1 row) - --- vars:int1, vars:int2, vars:int3: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | f - vars | int2 | f - vars | int3 | f -(3 rows) - -select pgv_free(); - pgv_free ----------- - -(1 row) - --------------------------- --- Transactional variables --------------------------- -select pgv_set('vars', 'int1', 101, true); - pgv_set ---------- - -(1 row) - -begin; - select pgv_set('vars', 'int2', 102, true); - pgv_set ---------- - -(1 row) - - begin autonomous; - select pgv_set('vars', 'int3', 103, true); - pgv_set ---------- - -(1 row) - --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - begin autonomous; - select pgv_set('vars', 'int2', 1002, true); - pgv_set ---------- - -(1 row) - --- 1002: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 1002 -(1 row) - - commit; --- 103: - select pgv_get('vars', 'int3', null::int); - pgv_get ---------- - 103 -(1 row) - - commit; - select pgv_set('vars', 'int1', 1001, true); - pgv_set ---------- - -(1 row) - --- 1001: - select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 1001 -(1 row) - --- 102: - select pgv_get('vars', 'int2', null::int); - pgv_get ---------- - 102 -(1 row) - -rollback; --- 101: -select pgv_get('vars', 'int1', null::int); - pgv_get ---------- - 101 -(1 row) - --- vars:int1: -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ - vars | int1 | t -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - ----------- --- Cursors ----------- -select pgv_insert('test', 'x', row (1::int, 2::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (2::int, 3::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'x', row (3::int, 4::int), false); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (10::int, 20::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (20::int, 30::int), true); - pgv_insert ------------- - -(1 row) - -select pgv_insert('test', 'y', row (30::int, 40::int), true); - pgv_insert ------------- - -(1 row) - -begin; - declare r1_cur cursor for select pgv_select('test', 'x'); - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - begin autonomous; - select pgv_insert('test', 'z', row (11::int, 22::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (22::int, 33::int), false); - pgv_insert ------------- - -(1 row) - - select pgv_insert('test', 'z', row (33::int, 44::int), false); - pgv_insert ------------- - -(1 row) - - declare r11_cur cursor for select pgv_select('test', 'x'); --- (1,2),(2,3): - fetch 2 in r11_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - - declare r2_cur cursor for select pgv_select('test', 'y'); --- correct error: unrecognized variable "y" - fetch 2 in r2_cur; -ERROR: unrecognized variable "y" - rollback; - rollback; - rollback; - rollback; - rollback; - declare r2_cur cursor for select pgv_select('test', 'y'); - declare r3_cur cursor for select pgv_select('test', 'z'); --- (1,2),(2,3): - fetch 2 in r1_cur; - pgv_select ------------- - (1,2) - (2,3) -(2 rows) - --- (10,20),(20,30): - fetch 2 in r2_cur; - pgv_select ------------- - (10,20) - (20,30) -(2 rows) - --- (11,22),(22,33): - fetch 2 in r3_cur; - pgv_select ------------- - (11,22) - (22,33) -(2 rows) - -rollback; -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------- --- Savepoint: rollback in main transaction ------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 101, true); - pgv_set ---------- - -(1 row) - --- 101: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 101 -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 102, true); - pgv_set ---------- - -(1 row) - --- 102: - select pgv_get('vars', 'trans_int', null::int); - pgv_get ---------- - 102 -(1 row) - - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars', 'trans_int', 103, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 103: - select pgv_get('vars', 'trans_int', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; --- 102: - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks -commit; -WARNING: there is no transaction in progress --- 101: -select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------- --- Savepoint: rollback in autonomous transaction ------------------------------------------------- -begin; - select pgv_set('vars', 'trans_int', 1, true); - pgv_set ---------- - -(1 row) - - savepoint sp1; - select pgv_set('vars', 'trans_int', 100, true); - pgv_set ---------- - -(1 row) - - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars1', 'int1', 2); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- 4 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: savepoint "sp2" does not exist --- 3 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block --- vars1:int1, vars1:trans_int1: - select * from pgv_list() order by package, name; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int2', 4, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int3', 5, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'int2', 3); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback; - commit; -WARNING: there is no transaction in progress - rollback to sp1; -ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks --- 1 - select pgv_get('vars', 'trans_int', null::int); -ERROR: unrecognized package "vars" --- 2 - select pgv_get('vars1', 'int1', null::int); -ERROR: unrecognized package "vars1" --- 3 - select pgv_get('vars1', 'int2', null::int); -ERROR: unrecognized package "vars1" --- vars:trans_int, vars1:int1, vars1:int2: - select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------+------------------ -(0 rows) - -commit; -WARNING: there is no transaction in progress -select pgv_free(); - pgv_free ----------- - -(1 row) - ------------------------------------------------------------- --- Sample with (subxact inside ATX) == (subxact outside ATX) ------------------------------------------------------------- -select pgv_set('vars1', 'int1', 0); - pgv_set ---------- - -(1 row) - -select pgv_set('vars1', 'trans_int1', 0, true); - pgv_set ---------- - -(1 row) - -begin; - begin autonomous; -ERROR: Extension in_memory is not compatible with autonomous transactions and connection pooling - select pgv_set('vars1', 'int1', 1); -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 2, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - savepoint sp2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - select pgv_set('vars1', 'trans_int1', 3, true); -ERROR: current transaction is aborted, commands ignored until end of transaction block - rollback to sp2; -ERROR: savepoint "sp2" does not exist --- 2 - select pgv_get('vars1', 'trans_int1', null::int); -ERROR: current transaction is aborted, commands ignored until end of transaction block - commit; -rollback; -WARNING: there is no transaction in progress --- vars1:int1, vars1:trans_int1 -select * from pgv_list() order by package, name; - package | name | is_transactional ----------+------------+------------------ - vars1 | int1 | f - vars1 | trans_int1 | t -(2 rows) - --- 1 -select pgv_get('vars1', 'int1', null::int); - pgv_get ---------- - 0 -(1 row) - --- 0 -select pgv_get('vars1', 'trans_int1', null::int); - pgv_get ---------- - 0 -(1 row) - -select pgv_free(); - pgv_free ----------- - -(1 row) - From ff47670e0499dcad08e78392c5a38b53b7a6e118 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 30 Dec 2022 04:21:27 +0300 Subject: [PATCH 136/147] [PGPRO-7614] Fix conflict between atx and pgv_free() Tags: pg_variables, atx --- .travis.yml | 1 - Makefile | 3 +- expected/pg_variables_atx_pkg.out | 274 +++++++++++++++++++++++++ expected/pg_variables_atx_pkg_1.out | 306 ++++++++++++++++++++++++++++ pg_variables.c | 30 +++ sql/pg_variables_atx_pkg.sql | 135 ++++++++++++ 6 files changed, 747 insertions(+), 2 deletions(-) create mode 100644 expected/pg_variables_atx_pkg.out create mode 100644 expected/pg_variables_atx_pkg_1.out create mode 100644 sql/pg_variables_atx_pkg.sql diff --git a/.travis.yml b/.travis.yml index 228302a..45189e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,6 @@ env: - PG_VERSION=11 LEVEL=nightmare - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - - PG_VERSION=10 # XXX: consider fixing nightmare mode matrix: diff --git a/Makefile b/Makefile index f95c39f..7253e93 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ DATA_built = $(EXTENSION)--$(EXTVERSION).sql PGFILEDESC = "pg_variables - sessional variables" -REGRESS = pg_variables pg_variables_any pg_variables_trans pg_variables_atx +REGRESS = pg_variables pg_variables_any pg_variables_trans pg_variables_atx \ + pg_variables_atx_pkg ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out new file mode 100644 index 0000000..a95e180 --- /dev/null +++ b/expected/pg_variables_atx_pkg.out @@ -0,0 +1,274 @@ +-- +-- PGPRO-7614: function pgv_free() inside autonomous transaction +-- +select pgv_free(); + pgv_free +---------- + +(1 row) + +-- +-- +-- Functions pgv_free() + pgv_get() inside autonomous transaction; package +-- with regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + ROLLBACK; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + ROLLBACK; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions. +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" + COMMIT; +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using regular +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" + COMMIT; + SELECT pgv_set('vars', 'int1', 2); + pgv_set +--------- + +(1 row) + + COMMIT; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 2 +(1 row) + +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using transactional +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" + COMMIT; + SELECT pgv_set('vars', 'int1', 2, true); + pgv_set +--------- + +(1 row) + + SELECT pgv_list(); + pgv_list +--------------- + (vars,int1,t) +(1 row) + + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: unrecognized package "vars" +ROLLBACK; +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out new file mode 100644 index 0000000..036c90f --- /dev/null +++ b/expected/pg_variables_atx_pkg_1.out @@ -0,0 +1,306 @@ +-- +-- PGPRO-7614: function pgv_free() inside autonomous transaction +-- +select pgv_free(); + pgv_free +---------- + +(1 row) + +-- +-- +-- Functions pgv_free() + pgv_get() inside autonomous transaction; package +-- with regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_get('vars', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + ROLLBACK; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); +ERROR: variable "int1" already created as NOT TRANSACTIONAL + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + ROLLBACK; + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); +ERROR: variable "int1" already created as NOT TRANSACTIONAL + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside recursive autonomous transactions. +-- +BEGIN; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_set('vars', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using regular +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_get('vars', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + COMMIT; +WARNING: there is no transaction in progress + SELECT pgv_set('vars', 'int1', 2); + pgv_set +--------- + +(1 row) + + COMMIT; +WARNING: there is no transaction in progress + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 2 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using transactional +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_get('vars', 'int1', null::int); +ERROR: current transaction is aborted, commands ignored until end of transaction block + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + + COMMIT; +WARNING: there is no transaction in progress + SELECT pgv_set('vars', 'int1', 2, true); +ERROR: variable "int1" already created as NOT TRANSACTIONAL + SELECT pgv_list(); + pgv_list +--------------- + (vars,int1,f) +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + pgv_get +--------- + 1 +(1 row) + +ROLLBACK; +WARNING: there is no transaction in progress +select pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 9c571d7..a269291 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2152,6 +2152,18 @@ removeObject(TransObject *object, TransObjectType type) #ifdef PGPRO_EE PackageContext *context, *next; + + /* + * Do not delete package inside autonomous transaction: it could be + * used in parent transaction. But we can delete package without any + * states: this means that the package was created in the current + * transaction. + */ + if (getNestLevelATX() > 0 && !dlist_is_empty(&object->states)) + { + GetActualState(object)->is_valid = false; + return; + } #endif package = (Package *) object; @@ -2171,6 +2183,8 @@ removeObject(TransObject *object, TransObjectType type) while (context) { next = context->next; + if (context->hctxTransact) + MemoryContextDelete(context->hctxTransact); pfree(context); context = next; } @@ -2764,11 +2778,15 @@ pgvRestoreContext() PackageContext *next = context->next; TransObject *object = &package->transObject; TransState *state; + bool actual_valid_state; /* Restore transactional variables from context */ package->hctxTransact = context->hctxTransact; package->varHashTransact = context->varHashTransact; + /* Save last actual state of package */ + actual_valid_state = GetActualState(object)->is_valid; + /* Remove all package states, generated in ATX transaction */ while ((state = GetActualState(object)) != context->state) { @@ -2778,6 +2796,18 @@ pgvRestoreContext() removeState(object, TRANS_PACKAGE, state); } + /* + * Package could be removed in the autonomous transaction. So + * need to mark it as invalid. Or removed package could be + * re-created - so need to mark it as valid. + */ + if (actual_valid_state != GetActualState(object)->is_valid) + GetActualState(object)->is_valid = actual_valid_state; + + /* Mark empty package as deleted. */ + if (GetPackState(package)->trans_var_num + numOfRegVars(package) == 0) + GetActualState(object)->is_valid = false; + pfree(context); package->context = next; } diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql new file mode 100644 index 0000000..97a4ad3 --- /dev/null +++ b/sql/pg_variables_atx_pkg.sql @@ -0,0 +1,135 @@ +-- +-- PGPRO-7614: function pgv_free() inside autonomous transaction +-- +select pgv_free(); +-- +-- +-- Functions pgv_free() + pgv_get() inside autonomous transaction; package +-- with regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + SELECT pgv_free(); +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- regular variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + ROLLBACK; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with rollback. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + ROLLBACK; + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside autonomous transaction; package with +-- transactional variable; autonomous transaction with commit. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1, true); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions. +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_free(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + COMMIT; +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using regular +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + COMMIT; + SELECT pgv_set('vars', 'int1', 2); + COMMIT; + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; +-- +-- +-- Function pgv_free() inside recursive autonomous transactions; +-- recreating the package after deletion with using transactional +-- variable. +-- +BEGIN; + SELECT pgv_set('vars', 'int1', 1); + BEGIN AUTONOMOUS; + BEGIN AUTONOMOUS; + SELECT pgv_get('vars', 'int1', null::int); + BEGIN AUTONOMOUS; + SELECT pgv_free(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); + COMMIT; + SELECT pgv_set('vars', 'int1', 2, true); + SELECT pgv_list(); + COMMIT; +-- ERROR: unrecognized package "vars" + SELECT pgv_get('vars', 'int1', null::int); +ROLLBACK; + +select pgv_free(); From a6a399af333c0e895dc09b1b3566fecf73322e66 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Mon, 9 Jan 2023 10:46:13 +0300 Subject: [PATCH 137/147] [PGPRO-7614] Do not free hash_seq_search scans of parent transaction Tags: pg_variables, atx --- expected/pg_variables_atx_pkg.out | 21 +++++++++++++++++++++ expected/pg_variables_atx_pkg_1.out | 25 +++++++++++++++++++++++++ pg_variables.c | 8 ++++++++ sql/pg_variables_atx_pkg.sql | 11 +++++++++++ 4 files changed, 65 insertions(+) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index a95e180..9af46e9 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -266,6 +266,27 @@ ERROR: unrecognized package "vars" SELECT pgv_get('vars', 'int1', null::int); ERROR: unrecognized package "vars" ROLLBACK; +-- +-- +-- Do not free hash_seq_search scans of parent transaction. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + FETCH 1 IN r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + BEGIN AUTONOMOUS; + ROLLBACK; +ROLLBACK; select pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index 036c90f..98a7bb9 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -296,6 +296,31 @@ WARNING: there is no transaction in progress 1 (1 row) +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Do not free hash_seq_search scans of parent transaction. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + FETCH 1 IN r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + ROLLBACK; ROLLBACK; WARNING: there is no transaction in progress select pgv_free(); diff --git a/pg_variables.c b/pg_variables.c index a269291..6f2fa81 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2951,7 +2951,11 @@ freeStatsLists(void) { VariableStatEntry *entry = (VariableStatEntry *) lfirst(cell); +#ifdef PGPRO_EE + hash_seq_term_all_levels(entry->status); +#else hash_seq_term(entry->status); +#endif } variables_stats = NIL; @@ -2960,7 +2964,11 @@ freeStatsLists(void) { PackageStatEntry *entry = (PackageStatEntry *) lfirst(cell); +#ifdef PGPRO_EE + hash_seq_term_all_levels(entry->status); +#else hash_seq_term(entry->status); +#endif } packages_stats = NIL; diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index 97a4ad3..fc73c26 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -131,5 +131,16 @@ BEGIN; -- ERROR: unrecognized package "vars" SELECT pgv_get('vars', 'int1', null::int); ROLLBACK; +-- +-- +-- Do not free hash_seq_search scans of parent transaction. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + FETCH 1 IN r1_cur; + BEGIN AUTONOMOUS; + ROLLBACK; +ROLLBACK; select pgv_free(); From 02d5dac06e42bf66da4bd0949afc9d990f1d7ca3 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 10 Jan 2023 00:09:18 +0300 Subject: [PATCH 138/147] [PGPRO-7614] Delete hash_seq_search scans on all ATX levels and before heap Tags: pg_variables, atx --- expected/pg_variables_atx_pkg.out | 62 +++++++++++++++++++++++++++-- expected/pg_variables_atx_pkg_1.out | 58 +++++++++++++++++++++++++-- pg_variables.c | 38 ++++++++++++++---- sql/pg_variables_atx_pkg.sql | 30 ++++++++++++-- 4 files changed, 172 insertions(+), 16 deletions(-) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index 9af46e9..1d5025f 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -1,7 +1,7 @@ -- -- PGPRO-7614: function pgv_free() inside autonomous transaction -- -select pgv_free(); +SELECT pgv_free(); pgv_free ---------- @@ -268,7 +268,53 @@ ERROR: unrecognized package "vars" ROLLBACK; -- -- --- Do not free hash_seq_search scans of parent transaction. +-- Test for case: do not free hash_seq_search scans of parent transaction +-- at end of the autonomous transaction. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + + SELECT pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) + FETCH 1 IN r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + BEGIN AUTONOMOUS; + ROLLBACK; +-- (3,4) + FETCH 1 IN r1_cur; + pgv_select +------------ + (3,4) +(1 row) + + SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; +ERROR: unrecognized package "test" +ROLLBACK; +-- +-- +-- Test for case: pgv_free() should free hash_seq_search scans of all +-- (current ATX + parent) transactions. -- BEGIN; SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); @@ -278,6 +324,7 @@ BEGIN; (1 row) DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) FETCH 1 IN r1_cur; pgv_select ------------ @@ -285,9 +332,18 @@ BEGIN; (1 row) BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + ROLLBACK; +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; +ERROR: unrecognized package "test" ROLLBACK; -select pgv_free(); +SELECT pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index 98a7bb9..b7fde58 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -1,7 +1,7 @@ -- -- PGPRO-7614: function pgv_free() inside autonomous transaction -- -select pgv_free(); +SELECT pgv_free(); pgv_free ---------- @@ -300,7 +300,53 @@ ROLLBACK; WARNING: there is no transaction in progress -- -- --- Do not free hash_seq_search scans of parent transaction. +-- Test for case: do not free hash_seq_search scans of parent transaction +-- at end of the autonomous transaction. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + pgv_insert +------------ + +(1 row) + + SELECT pgv_insert('test', 'x', row (3::int, 4::int), false); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) + FETCH 1 IN r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + ROLLBACK; +-- (3,4) + FETCH 1 IN r1_cur; +ERROR: cursor "r1_cur" does not exist + SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; +ERROR: cursor "r1_cur" does not exist +ROLLBACK; +WARNING: there is no transaction in progress +-- +-- +-- Test for case: pgv_free() should free hash_seq_search scans of all +-- (current ATX + parent) transactions. -- BEGIN; SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); @@ -310,6 +356,7 @@ BEGIN; (1 row) DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) FETCH 1 IN r1_cur; pgv_select ------------ @@ -320,10 +367,15 @@ BEGIN; ERROR: syntax error at or near "AUTONOMOUS" LINE 1: BEGIN AUTONOMOUS; ^ + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block ROLLBACK; +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; +ERROR: cursor "r1_cur" does not exist ROLLBACK; WARNING: there is no transaction in progress -select pgv_free(); +SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index 6f2fa81..5a319fc 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -310,7 +310,11 @@ list_remove_if(RemoveIfContext ctx) *ctx.list = list_delete_cell(*ctx.list, cell, prev); if (ctx.term) +#ifdef PGPRO_EE + hash_seq_term_all_levels(ctx.getter(entry)); +#else hash_seq_term(ctx.getter(entry)); +#endif pfree(ctx.getter(entry)); pfree(entry); @@ -342,7 +346,11 @@ list_remove_if(RemoveIfContext ctx) *ctx.list = foreach_delete_current(*ctx.list, cell); if (ctx.term) +#ifdef PGPRO_EE + hash_seq_term_all_levels(ctx.getter(entry)); +#else hash_seq_term(ctx.getter(entry)); +#endif pfree(ctx.getter(entry)); pfree(entry); @@ -1338,12 +1346,17 @@ remove_package(PG_FUNCTION_ARGS) package_name = PG_GETARG_TEXT_PP(0); package = getPackage(package_name, true); + /* + * Need to remove variables before packages because here calls hash_seq_term() + * which uses "entry->status->hashp->frozen" but memory context of "hashp" + * for regular variables can be deleted in removePackageInternal(). + */ + remove_variables_package(&variables_stats, package); + removePackageInternal(package); resetVariablesCache(); - remove_variables_package(&variables_stats, package); - PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); } @@ -1430,6 +1443,13 @@ remove_packages(PG_FUNCTION_ARGS) if (packagesHash == NULL) PG_RETURN_VOID(); + /* + * Need to remove variables before packages because here calls hash_seq_term() + * which uses "entry->status->hashp->frozen" but memory context of "hashp" + * for regular variables can be deleted in removePackageInternal(). + */ + remove_variables_all(&variables_stats); + /* Get packages list */ hash_seq_init(&pstat, packagesHash); while ((package = (Package *) hash_seq_search(&pstat)) != NULL) @@ -1438,7 +1458,6 @@ remove_packages(PG_FUNCTION_ARGS) } resetVariablesCache(); - remove_variables_all(&variables_stats); PG_RETURN_VOID(); } @@ -2201,14 +2220,19 @@ removeObject(TransObject *object, TransObjectType type) var->package->varHashRegular; } + /* + * Need to remove variables before state because here calls hash_seq_term() + * which uses "entry->status->hashp->frozen" but memory context of "hashp" + * for regular variables can be deleted in removeState()->freeValue(). + */ + /* Remove object from hash table */ + hash_search(hash, object->name, HASH_REMOVE, &found); + remove_variables_variable(&variables_stats, (Variable*)object); + /* Remove all object's states */ while (!dlist_is_empty(&object->states)) removeState(object, type, GetActualState(object)); - /* Remove object from hash table */ - hash_search(hash, object->name, HASH_REMOVE, &found); - remove_variables_variable(&variables_stats, (Variable *) object); - /* Remove package if it became empty */ if (type == TRANS_VARIABLE && isPackageEmpty(package)) { diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index fc73c26..1d6a6cb 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -1,7 +1,7 @@ -- -- PGPRO-7614: function pgv_free() inside autonomous transaction -- -select pgv_free(); +SELECT pgv_free(); -- -- -- Functions pgv_free() + pgv_get() inside autonomous transaction; package @@ -133,14 +133,38 @@ BEGIN; ROLLBACK; -- -- --- Do not free hash_seq_search scans of parent transaction. +-- Test for case: do not free hash_seq_search scans of parent transaction +-- at end of the autonomous transaction. -- BEGIN; SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + SELECT pgv_insert('test', 'x', row (3::int, 4::int), false); DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) FETCH 1 IN r1_cur; BEGIN AUTONOMOUS; ROLLBACK; +-- (3,4) + FETCH 1 IN r1_cur; + SELECT pgv_remove('test', 'x'); +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; +ROLLBACK; +-- +-- +-- Test for case: pgv_free() should free hash_seq_search scans of all +-- (current ATX + parent) transactions. +-- +BEGIN; + SELECT pgv_insert('test', 'x', row (1::int, 2::int), false); + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); +-- (1,2) + FETCH 1 IN r1_cur; + BEGIN AUTONOMOUS; + SELECT pgv_free(); + ROLLBACK; +-- ERROR: unrecognized package "test" + FETCH 1 IN r1_cur; ROLLBACK; -select pgv_free(); +SELECT pgv_free(); From afdef5405e9de88eb4548ea0f6a9150add44c749 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Wed, 11 Jan 2023 23:19:15 +0300 Subject: [PATCH 139/147] [PGPRO-7614] Fix crash by using cursor after rollback of cursor creation Tags: pg_variables --- expected/pg_variables_trans.out | 48 ++++++++++++++++++++++++ expected/pg_variables_trans_0.out | 48 ++++++++++++++++++++++++ pg_variables.c | 61 +++++++++++++++++++++++++++---- sql/pg_variables_trans.sql | 22 +++++++++++ 4 files changed, 171 insertions(+), 8 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 09f56c8..4370646 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3834,3 +3834,51 @@ SELECT pgv_free(); -- SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); ERROR: could not identify a hash function for type record +-- +-- Test case for PGPRO-7614: crash by using cursor after rollback of cursor +-- creation. +-- +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), true); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_select +------------ +(0 rows) + +ROLLBACK; +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_stats +----------- +(0 rows) + +ROLLBACK; diff --git a/expected/pg_variables_trans_0.out b/expected/pg_variables_trans_0.out index 7c29138..8a3c54c 100644 --- a/expected/pg_variables_trans_0.out +++ b/expected/pg_variables_trans_0.out @@ -3838,3 +3838,51 @@ SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); (1 row) +-- +-- Test case for PGPRO-7614: crash by using cursor after rollback of cursor +-- creation. +-- +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), true); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_select +------------ + (1,2) +(1 row) + + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_select +------------ +(0 rows) + +ROLLBACK; +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + pgv_insert +------------ + +(1 row) + + DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_stats +-------------- + (test,32768) +(1 row) + + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; + pgv_stats +----------- +(0 rows) + +ROLLBACK; diff --git a/pg_variables.c b/pg_variables.c index 5a319fc..2afb5ec 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -166,12 +166,14 @@ typedef struct tagVariableStatEntry Variable *variable; Package *package; Levels levels; + void **user_fctx; /* pointer to funcctx->user_fctx */ } VariableStatEntry; typedef struct tagPackageStatEntry { HASH_SEQ_STATUS *status; Levels levels; + void **user_fctx; /* pointer to funcctx->user_fctx */ } PackageStatEntry; #ifdef PGPRO_EE @@ -268,6 +270,25 @@ PackageStatEntry_status_ptr(void *entry) return ((PackageStatEntry *) entry)->status; } +/* + * VariableStatEntry and PackageStatEntry functions for clear function context. + */ +static void +VariableStatEntry_clear_fctx(void *entry) +{ + VariableStatEntry *e = (VariableStatEntry *) entry; + if (e->user_fctx) + *e->user_fctx = NULL; +} + +static void +PackageStatEntry_clear_fctx(void *entry) +{ + PackageStatEntry *e = (PackageStatEntry *) entry; + if (e->user_fctx) + *e->user_fctx = NULL; +} + /* * Generic remove_if algorithm. * @@ -289,6 +310,7 @@ typedef struct tagRemoveIfContext HASH_SEQ_STATUS *(*getter) (void *); /* status getter */ bool match_first; /* return on first match */ bool term; /* hash_seq_term on match */ + void (*clear_fctx) (void *); /* clear function context */ } RemoveIfContext; static void @@ -316,6 +338,8 @@ list_remove_if(RemoveIfContext ctx) hash_seq_term(ctx.getter(entry)); #endif + ctx.clear_fctx(entry); + pfree(ctx.getter(entry)); pfree(entry); @@ -352,6 +376,8 @@ list_remove_if(RemoveIfContext ctx) hash_seq_term(ctx.getter(entry)); #endif + ctx.clear_fctx(entry); + pfree(ctx.getter(entry)); pfree(entry); @@ -375,7 +401,8 @@ remove_variables_status(List **list, HASH_SEQ_STATUS *status) .eq = VariableStatEntry_status_eq, .getter = VariableStatEntry_status_ptr, .match_first = true, - .term = false + .term = false, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -398,7 +425,8 @@ remove_variables_variable(List **list, Variable *variable) .eq = VariableStatEntry_variable_eq, .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -417,7 +445,8 @@ remove_variables_package(List **list, Package *package) .eq = VariableStatEntry_package_eq, .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -436,7 +465,8 @@ remove_variables_level(List **list, Levels *levels) .eq = VariableStatEntry_level_eq, .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = false + .term = false, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -455,7 +485,8 @@ remove_variables_all(List **list) .eq = VariableStatEntry_eq_all, .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -474,7 +505,8 @@ remove_packages_status(List **list, HASH_SEQ_STATUS *status) .eq = PackageStatEntry_status_eq, .getter = PackageStatEntry_status_ptr, .match_first = true, - .term = false + .term = false, + .clear_fctx = PackageStatEntry_clear_fctx }; list_remove_if(ctx); @@ -493,7 +525,8 @@ remove_packages_level(List **list, Levels *levels) .eq = PackageStatEntry_level_eq, .getter = PackageStatEntry_status_ptr, .match_first = false, - .term = true + .term = true, + .clear_fctx = PackageStatEntry_clear_fctx }; list_remove_if(ctx); @@ -513,7 +546,8 @@ remove_variables_transactional(List **list) .eq = VariableStatEntry_is_transactional, .getter = VariableStatEntry_status_ptr, .match_first = false, - .term = true + .term = true, + .clear_fctx = VariableStatEntry_clear_fctx }; list_remove_if(ctx); @@ -1027,6 +1061,7 @@ variable_select(PG_FUNCTION_ARGS) #ifdef PGPRO_EE entry->levels.atxlevel = getNestLevelATX(); #endif + entry->user_fctx = &funcctx->user_fctx; variables_stats = lcons((void *) entry, variables_stats); MemoryContextSwitchTo(oldcontext); @@ -1036,6 +1071,15 @@ variable_select(PG_FUNCTION_ARGS) funcctx = SRF_PERCALL_SETUP(); + if (funcctx->user_fctx == NULL) + { + /* + * VariableStatEntry was removed. For example, after call + * 'ROLLBACK TO SAVEPOINT ...' + */ + SRF_RETURN_DONE(funcctx); + } + /* Get next hash record */ rstat = (HASH_SEQ_STATUS *) funcctx->user_fctx; item = (HashRecordEntry *) hash_seq_search(rstat); @@ -1672,6 +1716,7 @@ get_packages_stats(PG_FUNCTION_ARGS) #ifdef PGPRO_EE entry->levels.atxlevel = getNestLevelATX(); #endif + entry->user_fctx = &funcctx->user_fctx; packages_stats = lcons((void *) entry, packages_stats); MemoryContextSwitchTo(ctx); } diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index ae69df7..23b7afd 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1169,3 +1169,25 @@ SELECT pgv_free(); -- Test case for issue #38 [PGPRO-4676] -- SELECT pgv_insert('test', 'x5', ROW ((2::int, 1::int)), TRUE); + +-- +-- Test case for PGPRO-7614: crash by using cursor after rollback of cursor +-- creation. +-- +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), true); + DECLARE r1_cur CURSOR FOR SELECT pgv_select('test', 'x'); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; +ROLLBACK; + +BEGIN; + SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); + DECLARE r1_cur CURSOR FOR SELECT pgv_stats(); + SAVEPOINT sp1; + FETCH 1 in r1_cur; + ROLLBACK TO SAVEPOINT sp1; + FETCH 1 in r1_cur; +ROLLBACK; From 98737561940703ce93a6511854d1f1bb433ef7d5 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 13 Jan 2023 15:16:40 +0300 Subject: [PATCH 140/147] [PGPRO-7614] Fix error that caused by replacement of package state at end of autonomous transaction Tags: pg_variables, atx --- expected/pg_variables_atx_pkg.out | 31 +++++++++++++++++++++++++ expected/pg_variables_atx_pkg_1.out | 35 +++++++++++++++++++++++++++++ pg_variables.c | 9 +++++++- sql/pg_variables_atx_pkg.sql | 16 +++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index 1d5025f..c69b325 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -343,6 +343,37 @@ BEGIN; FETCH 1 IN r1_cur; ERROR: unrecognized package "test" ROLLBACK; +-- +-- +-- Test for case: pgv_set() created regular a variable; rollback +-- removes package state and creates a new state to make package valid. +-- Commit of next autonomous transaction should not replace this new +-- state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 1); + pgv_set +--------- + +(1 row) + + ROLLBACK; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 2); + pgv_set +--------- + +(1 row) + + COMMIT; +ROLLBACK; +SELECT pgv_remove('vars', 'int1'); + pgv_remove +------------ + +(1 row) + SELECT pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index b7fde58..e2bb3b2 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -375,6 +375,41 @@ ERROR: current transaction is aborted, commands ignored until end of transactio ERROR: cursor "r1_cur" does not exist ROLLBACK; WARNING: there is no transaction in progress +-- +-- +-- Test for case: pgv_set() created regular a variable; rollback +-- removes package state and creates a new state to make package valid. +-- Commit of next autonomous transaction should not replace this new +-- state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_set('vars', 'int1', 1); +ERROR: current transaction is aborted, commands ignored until end of transaction block + ROLLBACK; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_set('vars', 'int1', 2); + pgv_set +--------- + +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +ROLLBACK; +WARNING: there is no transaction in progress +SELECT pgv_remove('vars', 'int1'); + pgv_remove +------------ + +(1 row) + SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index 2afb5ec..ea0ba02 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2354,7 +2354,14 @@ rollbackSavepoint(TransObject *object, TransObjectType type) /* ...create a new state to make package valid. */ initObjectHistory(object, type); #ifdef PGPRO_EE - GetActualState(object)->levels.atxlevel = getNestLevelATX(); + /* + * Package inside autonomous transaction should not be detected + * as 'object has been changed in upper level' because in this + * case we will remove state in releaseSavepoint() but this + * state may be used pgvRestoreContext(). So atxlevel should + * be 0. + */ + GetActualState(object)->levels.atxlevel = 0; #endif GetActualState(object)->levels.level = GetCurrentTransactionNestLevel() - 1; if (!dlist_is_empty(changesStack)) diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index 1d6a6cb..c117796 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -166,5 +166,21 @@ BEGIN; -- ERROR: unrecognized package "test" FETCH 1 IN r1_cur; ROLLBACK; +-- +-- +-- Test for case: pgv_set() created regular a variable; rollback +-- removes package state and creates a new state to make package valid. +-- Commit of next autonomous transaction should not replace this new +-- state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 1); + ROLLBACK; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 2); + COMMIT; +ROLLBACK; +SELECT pgv_remove('vars', 'int1'); SELECT pgv_free(); From 2b5fd99f4cd26ebddc4159b8a3f68848149be42a Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Mon, 16 Jan 2023 13:47:20 +0300 Subject: [PATCH 141/147] [PGPRO-7614] Fix error caused by unchanged package state's ATX-level at autonomous transaction commit Tags: pg_variables, atx --- expected/pg_variables_atx_pkg.out | 34 ++++++++++++++++++++- expected/pg_variables_atx_pkg_1.out | 38 +++++++++++++++++++++++- pg_variables.c | 46 ++++++++++++++++++++--------- sql/pg_variables_atx_pkg.sql | 19 +++++++++++- 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index c69b325..f6c1d13 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -345,7 +345,7 @@ ERROR: unrecognized package "test" ROLLBACK; -- -- --- Test for case: pgv_set() created regular a variable; rollback +-- Test for case: pgv_set() created a regular variable; rollback -- removes package state and creates a new state to make package valid. -- Commit of next autonomous transaction should not replace this new -- state (this is not allowed for autonomous transaction). @@ -374,6 +374,38 @@ SELECT pgv_remove('vars', 'int1'); (1 row) +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). COMMIT changes this level to (atxlevel=1, level=0). +-- In the next autonomous transaction (atxlevel=1, level=1) we erroneously +-- detect that the package changed in upper transaction and remove the +-- package state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 2); + pgv_set +--------- + +(1 row) + + COMMIT; + BEGIN AUTONOMOUS; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + SELECT pgv_set('vars', 'int1', 2, true); + pgv_set +--------- + +(1 row) + + COMMIT; +ROLLBACK; SELECT pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index e2bb3b2..a579e44 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -377,7 +377,7 @@ ROLLBACK; WARNING: there is no transaction in progress -- -- --- Test for case: pgv_set() created regular a variable; rollback +-- Test for case: pgv_set() created a regular variable; rollback -- removes package state and creates a new state to make package valid. -- Commit of next autonomous transaction should not replace this new -- state (this is not allowed for autonomous transaction). @@ -410,6 +410,42 @@ SELECT pgv_remove('vars', 'int1'); (1 row) +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). COMMIT changes this level to (atxlevel=1, level=0). +-- In the next autonomous transaction (atxlevel=1, level=1) we erroneously +-- detect that the package changed in upper transaction and remove the +-- package state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_set('vars', 'int1', 2); +ERROR: current transaction is aborted, commands ignored until end of transaction block + COMMIT; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + SELECT pgv_set('vars', 'int1', 2, true); + pgv_set +--------- + +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +ROLLBACK; +WARNING: there is no transaction in progress SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index ea0ba02..9b0775b 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -64,7 +64,7 @@ static void resetVariablesCache(void); /* Functions to work with transactional objects */ static void createSavepoint(TransObject *object, TransObjectType type); -static void releaseSavepoint(TransObject *object, TransObjectType type); +static void releaseSavepoint(TransObject *object, TransObjectType type, bool sub); static void rollbackSavepoint(TransObject *object, TransObjectType type); static void copyValue(VarState *src, VarState *dest, Variable *destVar); @@ -2408,11 +2408,14 @@ rollbackSavepoint(TransObject *object, TransObjectType type) * Remove previous state of object */ static void -releaseSavepoint(TransObject *object, TransObjectType type) +releaseSavepoint(TransObject *object, TransObjectType type, bool sub) { dlist_head *states = &object->states; Assert(GetActualState(object)->levels.level == GetCurrentTransactionNestLevel()); +#ifdef PGPRO_EE + Assert(GetActualState(object)->levels.atxlevel == getNestLevelATX()); +#endif /* * If the object is not valid and does not exist at a higher level (or if @@ -2438,6 +2441,15 @@ releaseSavepoint(TransObject *object, TransObjectType type) nodeToDelete = dlist_next_node(states, dlist_head_node(states)); stateToDelete = dlist_container(TransState, node, nodeToDelete); +#ifdef PGPRO_EE + /* + * We can not delete package state inside autonomous transaction + * because the state can be used in pgvRestoreContext(). + * Exception: the state was created within this autonomous transaction. + */ + Assert(type != TRANS_PACKAGE || getNestLevelATX() == 0 || + stateToDelete->levels.atxlevel == getNestLevelATX()); +#endif removeState(object, type, stateToDelete); } @@ -2450,6 +2462,12 @@ releaseSavepoint(TransObject *object, TransObjectType type) /* Change subxact level due to release */ GetActualState(object)->levels.level--; + +#ifdef PGPRO_EE + /* Change ATX level due to finish autonomous transaction */ + if (!sub && getNestLevelATX() > 0) + GetActualState(object)->levels.atxlevel = 0; +#endif } static void @@ -2647,7 +2665,7 @@ typedef enum Action * Apply savepoint actions on list of variables or packages. */ static void -applyAction(Action action, TransObjectType type, dlist_head *list) +applyAction(Action action, TransObjectType type, dlist_head *list, bool sub) { dlist_iter iter; @@ -2677,7 +2695,7 @@ applyAction(Action action, TransObjectType type, dlist_head *list) GetActualState(variable)->is_valid = false; } - releaseSavepoint(object, type); + releaseSavepoint(object, type, sub); break; } } @@ -2688,7 +2706,7 @@ applyAction(Action action, TransObjectType type, dlist_head *list) * apply corresponding action on them */ static void -processChanges(Action action) +processChanges(Action action, bool sub) { ChangesStackNode *bottom_list; @@ -2697,8 +2715,8 @@ processChanges(Action action) bottom_list = dlist_container(ChangesStackNode, node, dlist_pop_head_node(changesStack)); - applyAction(action, TRANS_VARIABLE, bottom_list->changedVarsList); - applyAction(action, TRANS_PACKAGE, bottom_list->changedPacksList); + applyAction(action, TRANS_VARIABLE, bottom_list->changedVarsList, sub); + applyAction(action, TRANS_PACKAGE, bottom_list->changedPacksList, sub); /* Remove changes list of current level */ MemoryContextDelete(bottom_list->ctx); @@ -2866,10 +2884,10 @@ pgvRestoreContext() /* Remove all package states, generated in ATX transaction */ while ((state = GetActualState(object)) != context->state) { + removeState(object, TRANS_PACKAGE, state); if (dlist_is_empty(&object->states)) elog(ERROR, "pg_variables extension can not find " "transaction state for package"); - removeState(object, TRANS_PACKAGE, state); } /* @@ -2935,10 +2953,10 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, compatibility_check(); break; case SUBXACT_EVENT_COMMIT_SUB: - processChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT, true); break; case SUBXACT_EVENT_ABORT_SUB: - processChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT, true); break; case SUBXACT_EVENT_PRE_COMMIT_SUB: break; @@ -2965,16 +2983,16 @@ pgvTransCallback(XactEvent event, void *arg) { case XACT_EVENT_PRE_COMMIT: compatibility_check(); - processChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT, false); break; case XACT_EVENT_ABORT: - processChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT, false); break; case XACT_EVENT_PARALLEL_PRE_COMMIT: - processChanges(RELEASE_SAVEPOINT); + processChanges(RELEASE_SAVEPOINT, false); break; case XACT_EVENT_PARALLEL_ABORT: - processChanges(ROLLBACK_TO_SAVEPOINT); + processChanges(ROLLBACK_TO_SAVEPOINT, false); break; default: break; diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index c117796..ee21dfc 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -168,7 +168,7 @@ BEGIN; ROLLBACK; -- -- --- Test for case: pgv_set() created regular a variable; rollback +-- Test for case: pgv_set() created a regular variable; rollback -- removes package state and creates a new state to make package valid. -- Commit of next autonomous transaction should not replace this new -- state (this is not allowed for autonomous transaction). @@ -182,5 +182,22 @@ BEGIN; COMMIT; ROLLBACK; SELECT pgv_remove('vars', 'int1'); +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). COMMIT changes this level to (atxlevel=1, level=0). +-- In the next autonomous transaction (atxlevel=1, level=1) we erroneously +-- detect that the package changed in upper transaction and remove the +-- package state (this is not allowed for autonomous transaction). +-- +BEGIN; + BEGIN AUTONOMOUS; + SELECT pgv_set('vars', 'int1', 2); + COMMIT; + BEGIN AUTONOMOUS; + SELECT pgv_free(); + SELECT pgv_set('vars', 'int1', 2, true); + COMMIT; +ROLLBACK; SELECT pgv_free(); From c7898fce63c0b795541691b9117c491d04e87835 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Tue, 17 Jan 2023 15:22:55 +0300 Subject: [PATCH 142/147] [PGPRO-7614] Fix error caused by changing ATX level of package state at sub-transaction commit Tags: pg_variables, atx --- expected/pg_variables_atx_pkg.out | 24 ++++++++++++++++++++++++ expected/pg_variables_atx_pkg_1.out | 22 ++++++++++++++++++++++ pg_variables.c | 10 +++++----- sql/pg_variables_atx_pkg.sql | 14 ++++++++++++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index f6c1d13..527d5cc 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -406,6 +406,30 @@ BEGIN; COMMIT; ROLLBACK; +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). ROLLBACK changes this level to (atxlevel=0, level=0). +-- But ROLLBACK shouldn't change atxlevel in case rollback of sub-transaction. +-- +BEGIN; + BEGIN AUTONOMOUS; + SAVEPOINT sp1; + SELECT pgv_set('vars1', 'int1', 0); + pgv_set +--------- + +(1 row) + + ROLLBACK TO sp1; + COMMIT; +ROLLBACK; +SELECT pgv_remove('vars1', 'int1'); + pgv_remove +------------ + +(1 row) + SELECT pgv_free(); pgv_free ---------- diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index a579e44..d45c6ce 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -446,6 +446,28 @@ LINE 1: BEGIN AUTONOMOUS; WARNING: there is no transaction in progress ROLLBACK; WARNING: there is no transaction in progress +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). ROLLBACK changes this level to (atxlevel=0, level=0). +-- But ROLLBACK shouldn't change atxlevel in case rollback of sub-transaction. +-- +BEGIN; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SAVEPOINT sp1; +ERROR: current transaction is aborted, commands ignored until end of transaction block + SELECT pgv_set('vars1', 'int1', 0); +ERROR: current transaction is aborted, commands ignored until end of transaction block + ROLLBACK TO sp1; +ERROR: savepoint "sp1" does not exist + COMMIT; +ROLLBACK; +WARNING: there is no transaction in progress +SELECT pgv_remove('vars1', 'int1'); +ERROR: unrecognized package "vars1" SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index 9b0775b..fd6e2f9 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -65,7 +65,7 @@ static void resetVariablesCache(void); /* Functions to work with transactional objects */ static void createSavepoint(TransObject *object, TransObjectType type); static void releaseSavepoint(TransObject *object, TransObjectType type, bool sub); -static void rollbackSavepoint(TransObject *object, TransObjectType type); +static void rollbackSavepoint(TransObject *object, TransObjectType type, bool sub); static void copyValue(VarState *src, VarState *dest, Variable *destVar); static void freeValue(VarState *varstate, bool is_record); @@ -2329,7 +2329,7 @@ numOfRegVars(Package *package) * Rollback object to its previous state */ static void -rollbackSavepoint(TransObject *object, TransObjectType type) +rollbackSavepoint(TransObject *object, TransObjectType type, bool sub) { TransState *state; @@ -2359,9 +2359,9 @@ rollbackSavepoint(TransObject *object, TransObjectType type) * as 'object has been changed in upper level' because in this * case we will remove state in releaseSavepoint() but this * state may be used pgvRestoreContext(). So atxlevel should - * be 0. + * be 0 in case rollback of autonomous transaction. */ - GetActualState(object)->levels.atxlevel = 0; + GetActualState(object)->levels.atxlevel = sub ? getNestLevelATX() : 0; #endif GetActualState(object)->levels.level = GetCurrentTransactionNestLevel() - 1; if (!dlist_is_empty(changesStack)) @@ -2677,7 +2677,7 @@ applyAction(Action action, TransObjectType type, dlist_head *list, bool sub) switch (action) { case ROLLBACK_TO_SAVEPOINT: - rollbackSavepoint(object, type); + rollbackSavepoint(object, type, sub); break; case RELEASE_SAVEPOINT: diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index ee21dfc..a4a4943 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -199,5 +199,19 @@ BEGIN; SELECT pgv_set('vars', 'int1', 2, true); COMMIT; ROLLBACK; +-- +-- +-- Test for case: pgv_set() created a regular variable and package with +-- (atxlevel=1, level=1). ROLLBACK changes this level to (atxlevel=0, level=0). +-- But ROLLBACK shouldn't change atxlevel in case rollback of sub-transaction. +-- +BEGIN; + BEGIN AUTONOMOUS; + SAVEPOINT sp1; + SELECT pgv_set('vars1', 'int1', 0); + ROLLBACK TO sp1; + COMMIT; +ROLLBACK; +SELECT pgv_remove('vars1', 'int1'); SELECT pgv_free(); From db204b08720f4157a4918dde1aa9c5f84b601cdf Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 27 Jan 2023 23:28:23 +0300 Subject: [PATCH 143/147] [PGPRO-7614] Add cosmetic changes and padding Tags: pg_variables --- pg_variables.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index fd6e2f9..ab6149a 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -166,14 +166,14 @@ typedef struct tagVariableStatEntry Variable *variable; Package *package; Levels levels; - void **user_fctx; /* pointer to funcctx->user_fctx */ + void **user_fctx; /* pointer to funcctx->user_fctx */ } VariableStatEntry; typedef struct tagPackageStatEntry { HASH_SEQ_STATUS *status; Levels levels; - void **user_fctx; /* pointer to funcctx->user_fctx */ + void **user_fctx; /* pointer to funcctx->user_fctx */ } PackageStatEntry; #ifdef PGPRO_EE @@ -1391,8 +1391,9 @@ remove_package(PG_FUNCTION_ARGS) package = getPackage(package_name, true); /* - * Need to remove variables before packages because here calls hash_seq_term() - * which uses "entry->status->hashp->frozen" but memory context of "hashp" + * Need to remove variables before removing package because + * remove_variables_package() calls hash_seq_term() which uses + * "entry->status->hashp->frozen" but memory context of "hashp" * for regular variables can be deleted in removePackageInternal(). */ remove_variables_package(&variables_stats, package); @@ -1488,8 +1489,9 @@ remove_packages(PG_FUNCTION_ARGS) PG_RETURN_VOID(); /* - * Need to remove variables before packages because here calls hash_seq_term() - * which uses "entry->status->hashp->frozen" but memory context of "hashp" + * Need to remove variables before removing packages because + * remove_variables_all() calls hash_seq_term() which uses + * "entry->status->hashp->frozen" but memory context of "hashp" * for regular variables can be deleted in removePackageInternal(). */ remove_variables_all(&variables_stats); @@ -2266,9 +2268,10 @@ removeObject(TransObject *object, TransObjectType type) } /* - * Need to remove variables before state because here calls hash_seq_term() - * which uses "entry->status->hashp->frozen" but memory context of "hashp" - * for regular variables can be deleted in removeState()->freeValue(). + * Need to remove variables before removing state because + * remove_variables_variable() calls hash_seq_term() which uses + * "entry->status->hashp->frozen" but memory context of "hashp" + * for regular variables can be deleted in removeState() in freeValue(). */ /* Remove object from hash table */ hash_search(hash, object->name, HASH_REMOVE, &found); @@ -2358,8 +2361,8 @@ rollbackSavepoint(TransObject *object, TransObjectType type, bool sub) * Package inside autonomous transaction should not be detected * as 'object has been changed in upper level' because in this * case we will remove state in releaseSavepoint() but this - * state may be used pgvRestoreContext(). So atxlevel should - * be 0 in case rollback of autonomous transaction. + * state may be used in pgvRestoreContext(). So atxlevel should + * be 0 in case of rollback of autonomous transaction. */ GetActualState(object)->levels.atxlevel = sub ? getNestLevelATX() : 0; #endif From c7e55864dee0f10626fe6bfec654d0690e01d2f7 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 27 Jan 2023 19:44:55 +0300 Subject: [PATCH 144/147] [PGPRO-7287] New PgproRegisterXactCallback to filter by event kind Tags: pg_variables --- pg_variables.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index b1d1b47..633b029 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2951,7 +2951,11 @@ _PG_init(void) NULL, NULL); +#if defined(PGPRO_EE) && (PG_VERSION_NUM >= 150000) + PgproRegisterXactCallback(pgvTransCallback, NULL, XACT_EVENT_KIND_VANILLA | XACT_EVENT_KIND_ATX); +#else RegisterXactCallback(pgvTransCallback, NULL); +#endif RegisterSubXactCallback(pgvSubTransCallback, NULL); /* Install hooks. */ From 16d0f974a1115935dbc1709b745d07d5de56b3a6 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Sat, 4 Feb 2023 00:39:05 +0300 Subject: [PATCH 145/147] [PGPRO-7742] Use PgproRegisterXactCallback for all EE-versions Tags: pg_variables --- pg_variables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index d87662c..1d3127f 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2953,7 +2953,7 @@ _PG_init(void) NULL, NULL); -#if defined(PGPRO_EE) && (PG_VERSION_NUM >= 150000) +#ifdef PGPRO_EE PgproRegisterXactCallback(pgvTransCallback, NULL, XACT_EVENT_KIND_VANILLA | XACT_EVENT_KIND_ATX); #else RegisterXactCallback(pgvTransCallback, NULL); From 59f616e267053ca6ef76bef7634ab363c3796735 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Thu, 2 Mar 2023 20:28:20 +0300 Subject: [PATCH 146/147] [PGPRO-7856] Correction for object state releasing in case object was not deleted Tags: atx --- expected/pg_variables_atx_pkg.out | 35 ++++++++++++++++++++++++++ expected/pg_variables_atx_pkg_1.out | 39 +++++++++++++++++++++++++++++ pg_variables.c | 10 +++++--- pg_variables.h | 2 +- sql/pg_variables_atx_pkg.sql | 21 ++++++++++++++++ 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/expected/pg_variables_atx_pkg.out b/expected/pg_variables_atx_pkg.out index 527d5cc..e9c8412 100644 --- a/expected/pg_variables_atx_pkg.out +++ b/expected/pg_variables_atx_pkg.out @@ -436,3 +436,38 @@ SELECT pgv_free(); (1 row) +-- +-- +-- PGPRO-7856 +-- Test for case: we don't remove the package object without any variables at +-- the end of autonomous transaction but need to move the state of this object +-- to upper level. +-- +BEGIN; + BEGIN AUTONOMOUS; + SAVEPOINT sp1; + SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + pgv_set +--------- + +(1 row) + + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + RELEASE sp1; + ROLLBACK; + BEGIN AUTONOMOUS; + SAVEPOINT sp2; + SAVEPOINT sp3; + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +ROLLBACK; diff --git a/expected/pg_variables_atx_pkg_1.out b/expected/pg_variables_atx_pkg_1.out index d45c6ce..8e36d0a 100644 --- a/expected/pg_variables_atx_pkg_1.out +++ b/expected/pg_variables_atx_pkg_1.out @@ -474,3 +474,42 @@ SELECT pgv_free(); (1 row) +-- +-- +-- PGPRO-7856 +-- Test for case: we don't remove the package object without any variables at +-- the end of autonomous transaction but need to move the state of this object +-- to upper level. +-- +BEGIN; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SAVEPOINT sp1; +ERROR: current transaction is aborted, commands ignored until end of transaction block + SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); +ERROR: current transaction is aborted, commands ignored until end of transaction block + SELECT pgv_free(); +ERROR: current transaction is aborted, commands ignored until end of transaction block + RELEASE sp1; +ERROR: current transaction is aborted, commands ignored until end of transaction block + ROLLBACK; + BEGIN AUTONOMOUS; +ERROR: syntax error at or near "AUTONOMOUS" +LINE 1: BEGIN AUTONOMOUS; + ^ + SAVEPOINT sp2; +ERROR: SAVEPOINT can only be used in transaction blocks + SAVEPOINT sp3; +ERROR: SAVEPOINT can only be used in transaction blocks + SELECT pgv_free(); + pgv_free +---------- + +(1 row) + + COMMIT; +WARNING: there is no transaction in progress +ROLLBACK; +WARNING: there is no transaction in progress diff --git a/pg_variables.c b/pg_variables.c index b7b1302..c8ee939 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2206,7 +2206,7 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete } /* Remove package or variable (either transactional or regular) */ -void +bool removeObject(TransObject *object, TransObjectType type) { bool found; @@ -2228,7 +2228,7 @@ removeObject(TransObject *object, TransObjectType type) if (getNestLevelATX() > 0 && !dlist_is_empty(&object->states)) { GetActualState(object)->is_valid = false; - return; + return false; } #endif @@ -2289,6 +2289,8 @@ removeObject(TransObject *object, TransObjectType type) } resetVariablesCache(); + + return true; } /* @@ -2429,8 +2431,8 @@ releaseSavepoint(TransObject *object, TransObjectType type, bool sub) dlist_is_empty(changesStack)) ) { - removeObject(object, type); - return; + if (removeObject(object, type)) + return; } /* diff --git a/pg_variables.h b/pg_variables.h index afa9b22..6508e9f 100644 --- a/pg_variables.h +++ b/pg_variables.h @@ -193,7 +193,7 @@ extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader); extern bool delete_record(Variable *variable, Datum value, bool is_null); extern void insert_record_copy(RecordVar *dest_record, Datum src_tuple, Variable *variable); -extern void removeObject(TransObject *object, TransObjectType type); +extern bool removeObject(TransObject *object, TransObjectType type); #define GetActualState(object) \ (dlist_head_element(TransState, node, &((TransObject *) object)->states)) diff --git a/sql/pg_variables_atx_pkg.sql b/sql/pg_variables_atx_pkg.sql index a4a4943..49113d6 100644 --- a/sql/pg_variables_atx_pkg.sql +++ b/sql/pg_variables_atx_pkg.sql @@ -215,3 +215,24 @@ ROLLBACK; SELECT pgv_remove('vars1', 'int1'); SELECT pgv_free(); +-- +-- +-- PGPRO-7856 +-- Test for case: we don't remove the package object without any variables at +-- the end of autonomous transaction but need to move the state of this object +-- to upper level. +-- +BEGIN; + BEGIN AUTONOMOUS; + SAVEPOINT sp1; + SELECT pgv_set('vars2', 'any1', 'variable exists'::text, true); + SELECT pgv_free(); + RELEASE sp1; + ROLLBACK; + + BEGIN AUTONOMOUS; + SAVEPOINT sp2; + SAVEPOINT sp3; + SELECT pgv_free(); + COMMIT; +ROLLBACK; From fe2a1f6114bd21c9a0c2b967e7fc19d21e21ae7e Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Fri, 22 Sep 2023 12:47:12 +0300 Subject: [PATCH 147/147] travis-ci for v16 --- .travis.yml | 4 ++++ Dockerfile.tmpl | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 45189e8..80d5de7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,9 @@ notifications: on_failure: always env: + - PG_VERSION=16 LEVEL=nightmare + - PG_VERSION=16 LEVEL=hardcore + - PG_VERSION=16 - PG_VERSION=15 LEVEL=nightmare - PG_VERSION=15 LEVEL=hardcore - PG_VERSION=15 @@ -44,3 +47,4 @@ matrix: - env: PG_VERSION=13 LEVEL=nightmare - env: PG_VERSION=14 LEVEL=nightmare - env: PG_VERSION=15 LEVEL=nightmare + - env: PG_VERSION=16 LEVEL=nightmare diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 0bcd176..2792b6e 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -6,7 +6,7 @@ RUN apk add --no-cache \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ zlib-dev libedit-dev linux-headers \ - clang clang-analyzer; + pkgconf icu-dev clang clang15 clang-analyzer; # Install fresh valgrind RUN apk add valgrind \