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 001/103] 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 002/103] 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 003/103] 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 004/103] 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 005/103] 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 006/103] 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 007/103] 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 008/103] 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 009/103] 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 010/103] 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 011/103] 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 012/103] 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 013/103] 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 014/103] 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 015/103] 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 016/103] 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 017/103] 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 018/103] 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 019/103] 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 020/103] 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 021/103] 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 022/103] 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 023/103] 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 024/103] 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 025/103] 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 026/103] 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 027/103] 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 028/103] 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 029/103] 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 030/103] 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 031/103] 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 032/103] 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 033/103] 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 034/103] 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 035/103] 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 036/103] 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 037/103] 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 038/103] 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 039/103] 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 040/103] 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 041/103] 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 042/103] 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 043/103] 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 044/103] 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 045/103] 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 046/103] 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 047/103] 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 048/103] 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 049/103] 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 050/103] 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 051/103] 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 052/103] 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 053/103] 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 054/103] 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 055/103] 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 056/103] 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 057/103] 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 058/103] 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 059/103] 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 060/103] 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 061/103] 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 062/103] 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 063/103] 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 064/103] 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 065/103] 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 066/103] 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 067/103] 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 068/103] 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 069/103] 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 070/103] 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 071/103] 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 072/103] 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 073/103] 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 074/103] 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 075/103] 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 076/103] 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 077/103] 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 078/103] 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 079/103] 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 080/103] 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 081/103] 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 082/103] 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 083/103] 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 084/103] 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 085/103] 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 086/103] [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 087/103] [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 088/103] 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 089/103] 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 090/103] 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 091/103] [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 092/103] [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 093/103] [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 094/103] [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 095/103] [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 096/103] [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 097/103] [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 098/103] [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 099/103] [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 100/103] [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 101/103] [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 102/103] [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 103/103] 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 \