From a9279299d97416b6b1fddc10c9dc55e6069b1a8f Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 9 Oct 2020 16:57:14 +0300 Subject: [PATCH 01/22] Issue #27: Error with transactional pgv_insert/pgv_remove --- expected/pg_variables_trans.out | 111 ++++++++++++++++++++++++++++++++ pg_variables.c | 31 ++++++++- pg_variables.h | 1 + sql/pg_variables_trans.sql | 36 +++++++++++ 4 files changed, 176 insertions(+), 3 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 025cc77..e1a4247 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2032,3 +2032,114 @@ SELECT pgv_free(); (1 row) +-- Variables should be insertable after pgv_remove +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +ROLLBACK; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ +(0 rows) + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_remove('test', 'x'); + pgv_remove +------------ + +(1 row) + +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | x | t +(1 row) + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +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), TRUE); + pgv_insert +------------ + +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); + pgv_insert +------------ + +(1 row) + +COMMIT; +SELECT * FROM pgv_list() order by package, name; + package | name | is_transactional +---------+------+------------------ + test | y | t +(1 row) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 0c6dca2..35f48d1 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -403,7 +403,10 @@ 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); + else + check_attributes(variable, tupdesc); } LastTypeId = tupType; @@ -901,6 +904,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 +913,7 @@ remove_variable(PG_FUNCTION_ARGS) else removeObject(&variable->transObject, TRANS_VARIABLE); - resetVariablesCache(false); + resetVariablesCache(true); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -945,7 +949,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; + + /* Set all the variables from package is 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) @@ -1596,6 +1620,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)) 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..21a656b 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -534,3 +534,39 @@ ROLLBACK; SELECT package FROM pgv_stats() ORDER BY package; SELECT pgv_free(); + +-- Variables should be insertable after pgv_remove +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_remove('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +ROLLBACK; + +SELECT * FROM pgv_list() order by package, name; + +BEGIN; +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_remove('test', 'x'); +SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +COMMIT; + +SELECT * FROM pgv_list() order by package, name; + +-- Variables should be insertable after pgv_free +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_free(); +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +ROLLBACK; + +SELECT * FROM pgv_list() order by package, name; + +BEGIN; +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_free(); +SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +COMMIT; + +SELECT * FROM pgv_list() order by package, name; + +SELECT pgv_free(); From caf9314918f7e44aa41d2e6a895fede239229819 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 14 Oct 2020 16:19:34 +0300 Subject: [PATCH 02/22] Issue #27: Error with transactional pgv_insert/pgv_remove - review --- expected/pg_variables_trans.out | 16 ++++++++-------- pg_variables.c | 19 +++++++++---------- sql/pg_variables_trans.sql | 16 ++++++++-------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index e1a4247..34c2f85 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2034,7 +2034,7 @@ SELECT pgv_free(); -- Variables should be insertable after pgv_remove BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); pgv_insert ------------ @@ -2046,7 +2046,7 @@ SELECT pgv_remove('test', 'x'); (1 row) -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), TRUE); pgv_insert ------------ @@ -2059,7 +2059,7 @@ SELECT * FROM pgv_list() order by package, name; (0 rows) BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); pgv_insert ------------ @@ -2071,7 +2071,7 @@ SELECT pgv_remove('test', 'x'); (1 row) -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), TRUE); pgv_insert ------------ @@ -2086,7 +2086,7 @@ SELECT * FROM pgv_list() order by package, name; -- Variables should be insertable after pgv_free BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); pgv_insert ------------ @@ -2098,7 +2098,7 @@ SELECT pgv_free(); (1 row) -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); pgv_insert ------------ @@ -2112,7 +2112,7 @@ SELECT * FROM pgv_list() order by package, name; (1 row) BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); pgv_insert ------------ @@ -2124,7 +2124,7 @@ SELECT pgv_free(); (1 row) -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); pgv_insert ------------ diff --git a/pg_variables.c b/pg_variables.c index 35f48d1..304bf5d 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); @@ -913,7 +913,7 @@ remove_variable(PG_FUNCTION_ARGS) else removeObject(&variable->transObject, TRANS_VARIABLE); - resetVariablesCache(true); + resetVariablesCache(); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -940,7 +940,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(); @@ -955,7 +955,7 @@ removePackageInternal(Package *package) HASH_SEQ_STATUS vstat; int i; - /* Set all the variables from package is deleted */ + /* Mark all the valid variables from package as deleted */ for (i = 0; i < 2; i++) { if ((htab = pack_htab(package, i)) != NULL) @@ -1007,11 +1007,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; } @@ -1037,7 +1036,7 @@ remove_packages(PG_FUNCTION_ARGS) removePackageInternal(package); } - resetVariablesCache(true); + resetVariablesCache(); PG_RETURN_VOID(); } @@ -1753,7 +1752,7 @@ removeObject(TransObject *object, TransObjectType type) GetActualState(&package->transObject)->is_valid = false; } - resetVariablesCache(true); + resetVariablesCache(); } /* @@ -2121,7 +2120,7 @@ processChanges(Action action) MemoryContextDelete(ModuleContext); packagesHash = NULL; ModuleContext = NULL; - resetVariablesCache(true); + resetVariablesCache(); changesStack = NULL; changesStackContext = NULL; } diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 21a656b..a5784c0 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -537,34 +537,34 @@ SELECT pgv_free(); -- Variables should be insertable after pgv_remove BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); SELECT pgv_remove('test', 'x'); -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), TRUE); ROLLBACK; SELECT * FROM pgv_list() order by package, name; BEGIN; -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); SELECT pgv_remove('test', 'x'); -SELECT pgv_insert('test', 'x', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'x', ROW (3::int, 4::int), TRUE); COMMIT; SELECT * FROM pgv_list() order by package, name; -- Variables should be insertable after pgv_free BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); SELECT pgv_free(); -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); ROLLBACK; SELECT * FROM pgv_list() order by package, name; BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); SELECT pgv_free(); -SELECT pgv_insert('test', 'y', ROW (1::int), TRUE); +SELECT pgv_insert('test', 'y', ROW (3::int, 4::int), TRUE); COMMIT; SELECT * FROM pgv_list() order by package, name; From 38de8dfbdc1ecd076aef213da162b54544699857 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 14 Oct 2020 18:42:43 +0300 Subject: [PATCH 03/22] Issue #27: Add fix and improve tests. --- expected/pg_variables_trans.out | 242 ++++++++++++++++++++++++++++++-- pg_variables.c | 5 + sql/pg_variables_trans.sql | 58 +++++++- 3 files changed, 289 insertions(+), 16 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 34c2f85..d73ef69 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2032,7 +2032,7 @@ SELECT pgv_free(); (1 row) --- Variables should be insertable after pgv_remove +-- Variables should be insertable after pgv_remove (variable) BEGIN; SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); pgv_insert @@ -2040,18 +2040,43 @@ SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); (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 (3::int, 4::int), TRUE); +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 @@ -2065,18 +2090,43 @@ SELECT pgv_insert('test', 'x', ROW (1::int, 2::int), TRUE); (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 (3::int, 4::int), TRUE); +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 @@ -2084,7 +2134,65 @@ SELECT * FROM pgv_list() order by package, name; test | x | t (1 row) --- Variables should be insertable after pgv_free +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 @@ -2092,51 +2200,167 @@ SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); (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', 'y', ROW (3::int, 4::int), TRUE); +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 | x | t + test | y | t (1 row) BEGIN; -SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); +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', 'y', ROW (3::int, 4::int), TRUE); +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 | y | t + test | z | t (1 row) +SELECT pgv_select('test', 'z'); + pgv_select +------------ + (1,3) + (2,4) +(2 rows) + SELECT pgv_free(); pgv_free ---------- diff --git a/pg_variables.c b/pg_variables.c index 304bf5d..ee1fa3d 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -404,9 +404,14 @@ variable_insert(PG_FUNCTION_ARGS) */ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); if (variable->is_deleted) + { init_record(record, tupdesc, variable); + variable->is_deleted = false; + } else + { check_attributes(variable, tupdesc); + } } LastTypeId = tupType; diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index a5784c0..c4b8c90 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -535,38 +535,82 @@ SELECT package FROM pgv_stats() ORDER BY package; SELECT pgv_free(); --- Variables should be insertable after pgv_remove +-- 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 (3::int, 4::int), TRUE); +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 (3::int, 4::int), TRUE); +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_free +-- Variables should be insertable after pgv_remove (package) BEGIN; SELECT pgv_insert('test', 'y', ROW (1::int, 2::int), TRUE); -SELECT pgv_free(); -SELECT pgv_insert('test', 'y', ROW (3::int, 4::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', 'y', ROW (3::int, 4::int), TRUE); +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 712d0128130df33257bfb9e08ac8105fc1ed1937 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 23 Oct 2020 10:24:23 +0300 Subject: [PATCH 04/22] Issue #27: Fix for backend termination on transaction commit. --- expected/pg_variables_trans.out | 644 ++++++++++++++++++++++++++++++++ pg_variables.c | 29 +- sql/pg_variables_trans.sql | 246 ++++++++++++ 3 files changed, 904 insertions(+), 15 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index d73ef69..76fc339 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2367,3 +2367,647 @@ 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; diff --git a/pg_variables.c b/pg_variables.c index ee1fa3d..fe09532 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -595,28 +595,27 @@ 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(); + + /* 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); if (SRF_IS_FIRSTCALL()) { - text *package_name; - text *var_name; - Package *package; - Variable *variable; MemoryContext oldcontext; RecordVar *record; - CHECK_ARGS_FOR_NULL(); - - /* 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); - record = &(GetActualValue(variable).record); - funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index c4b8c90..b4f28d5 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -614,3 +614,249 @@ 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; From 1b9dc66f2559de925df0271f84039a5099776a28 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 23 Oct 2020 10:43:39 +0300 Subject: [PATCH 05/22] Issue #27: Add fix for leaked hash_seq_search. Fix warning: leaked hash_seq_search scan for hash table. --- expected/pg_variables_trans.out | 499 ++++++++++++++++++++++++++++++++ pg_variables.c | 44 ++- sql/pg_variables_trans.sql | 147 ++++++++++ 3 files changed, 683 insertions(+), 7 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 76fc339..7f05a55 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3011,3 +3011,502 @@ SELECT pgv_remove('test', 'x'); (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) + diff --git a/pg_variables.c b/pg_variables.c index fe09532..bad8711 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -129,6 +129,9 @@ 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 scan for hash table */ +static List *rstats = NIL; + /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -595,10 +598,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(); @@ -612,12 +615,13 @@ variable_select(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; - RecordVar *record; + MemoryContext oldcontext; + RecordVar *record; record = &(GetActualValue(variable).record); funcctx = SRF_FIRSTCALL_INIT(); - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + oldcontext = MemoryContextSwitchTo(TopTransactionContext); funcctx->tuple_desc = record->tupdesc; @@ -625,6 +629,8 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; + rstats = lcons((void *)rstat, rstats); + MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); PG_FREE_IF_COPY(var_name, 1); @@ -644,6 +650,7 @@ variable_select(PG_FUNCTION_ARGS) } else { + rstats = list_delete(rstats, rstat); pfree(rstat); SRF_RETURN_DONE(funcctx); } @@ -2196,6 +2203,18 @@ pgvTransCallback(XactEvent event, void *arg) break; } } + + if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) + { + ListCell *cell; + + foreach(cell, rstats) + { + hash_seq_term((HASH_SEQ_STATUS *) lfirst(cell)); + } + + rstats = NIL; + } } /* @@ -2208,6 +2227,17 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); + + { + ListCell *cell; + + foreach(cell, rstats) + { + hash_seq_term((HASH_SEQ_STATUS *) lfirst(cell)); + } + + rstats = NIL; + } } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index b4f28d5..c9cb2ad 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -860,3 +860,150 @@ 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(); From ddf4a22ac2c1723429ee28803a75613627a233f1 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 23 Oct 2020 17:09:35 +0300 Subject: [PATCH 06/22] Issue #27: Add tests and comment. --- expected/pg_variables_trans.out | 46 +++++++++++++++++++++++++++++++++ pg_variables.c | 15 ++++++++++- sql/pg_variables_trans.sql | 23 +++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 7f05a55..eac7be2 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3510,3 +3510,49 @@ SELECT 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; diff --git a/pg_variables.c b/pg_variables.c index bad8711..c4fd2b4 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -129,7 +129,20 @@ 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 scan for hash table */ +/* + * List to store all the running hash_seq_search 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, becouse it wount be reset by the time + * pgvTransCallback is called. + */ static List *rstats = NIL; /* Returns a lists of packages and variables changed at current subxact level */ diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index c9cb2ad..19ee43d 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1007,3 +1007,26 @@ 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; From 1f5e5971942b6cc0723829152ca1d8b62a7c2db6 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Fri, 23 Oct 2020 18:14:41 +0300 Subject: [PATCH 07/22] Issue #27: Add fix for leaked hash_seq_search for pgv_stats. --- expected/pg_variables_trans.out | 97 +++++++++++++++++++++++++++++++++ pg_variables.c | 74 ++++++++++++++++--------- sql/pg_variables_trans.sql | 45 +++++++++++++++ 3 files changed, 190 insertions(+), 26 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index eac7be2..2ce8138 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3556,3 +3556,100 @@ 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); + 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/pg_variables.c b/pg_variables.c index c4fd2b4..0af9120 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -143,8 +143,10 @@ static MemoryContext changesStackContext = NULL; * TopTransactionContext is handy here, becouse it wount be reset by the time * pgvTransCallback is called. */ -static List *rstats = NIL; +static List *variables_stats = NIL; +static List *packages_stats = NIL; +static void freeStatsLists(bool deep); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ ( \ @@ -642,7 +644,7 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; - rstats = lcons((void *)rstat, rstats); + variables_stats = lcons((void *)rstat, variables_stats); MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); @@ -663,7 +665,7 @@ variable_select(PG_FUNCTION_ARGS) } else { - rstats = list_delete(rstats, rstat); + variables_stats = list_delete(variables_stats, rstat); pfree(rstat); SRF_RETURN_DONE(funcctx); } @@ -1232,13 +1234,14 @@ get_packages_stats(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; MemoryContext oldcontext; - HASH_SEQ_STATUS *pstat; + HASH_SEQ_STATUS *rstat; Package *package; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; + //elog(INFO, " >>> "); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1257,11 +1260,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; @@ -1274,9 +1282,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]; @@ -1308,7 +1316,8 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - pfree(pstat); + packages_stats = list_delete(packages_stats, rstat); + pfree(rstat); SRF_RETURN_DONE(funcctx); } } @@ -2218,16 +2227,7 @@ pgvTransCallback(XactEvent event, void *arg) } if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) - { - ListCell *cell; - - foreach(cell, rstats) - { - hash_seq_term((HASH_SEQ_STATUS *) lfirst(cell)); - } - - rstats = NIL; - } + freeStatsLists(true); } /* @@ -2241,16 +2241,38 @@ variable_ExecutorEnd(QueryDesc *queryDesc) else standard_ExecutorEnd(queryDesc); + freeStatsLists(false); +} + +/* + * Free hash_seq_search scans + */ +void +freeStatsLists(bool deep) +{ + ListCell *cell; + HASH_SEQ_STATUS *status; + + foreach(cell, variables_stats) { - ListCell *cell; + status = (HASH_SEQ_STATUS *) lfirst(cell); + hash_seq_term(status); - foreach(cell, rstats) - { - hash_seq_term((HASH_SEQ_STATUS *) lfirst(cell)); - } + if (deep) + pfree(status); + } + + variables_stats = NIL; - rstats = NIL; + foreach(cell, packages_stats) + { + status = (HASH_SEQ_STATUS *) lfirst(cell); + hash_seq_term(status); + + pfree(status); } + + packages_stats = NIL; } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 19ee43d..2b83ab5 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1030,3 +1030,48 @@ 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 d90fd21bb6161506fa4c6b45a98c55f8f107d9fc Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 27 Oct 2020 14:05:09 +0300 Subject: [PATCH 08/22] Issue #27: Add fix for VALGRING failed test. --- pg_variables.c | 148 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 18 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 0af9120..7e29b76 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -130,7 +130,8 @@ static dlist_head *changesStack = NULL; static MemoryContext changesStackContext = NULL; /* - * List to store all the running hash_seq_search scan for hash table. + * 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 @@ -143,10 +144,116 @@ static MemoryContext changesStackContext = NULL; * TopTransactionContext is handy here, becouse it wount be reset by the time * pgvTransCallback is called. */ -static List *variables_stats = NIL; +static List *variables_stats = NIL; static List *packages_stats = NIL; -static void freeStatsLists(bool deep); +typedef struct tagHtabToStat { + HTAB *hash; + HASH_SEQ_STATUS *status; + Variable *variable; + Package *package; +} 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; +} + +/* + * Generic remove_if algorithm for HtabToStat. + * + * + eq if function pointer used to compare list entry to the value. + * + if match_first is true return on first match. + */ +static void +HtabToStat_remove_if(List **l, void *value, + bool (*eq)(HtabToStat *, void *), + bool match_first) +{ + 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; + } + } +} + +/* + * 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 entrys for package. + */ +static void +remove_variables_package(List **l, Package *package) +{ + HtabToStat_remove_if(l, package, HtabToStat_package_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() \ ( \ @@ -632,6 +739,7 @@ variable_select(PG_FUNCTION_ARGS) { MemoryContext oldcontext; RecordVar *record; + HtabToStat *htab_to_stat; record = &(GetActualValue(variable).record); funcctx = SRF_FIRSTCALL_INIT(); @@ -644,7 +752,12 @@ variable_select(PG_FUNCTION_ARGS) hash_seq_init(rstat, record->rhash); funcctx->user_fctx = rstat; - variables_stats = lcons((void *)rstat, variables_stats); + 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; + variables_stats = lcons((void *)htab_to_stat, variables_stats); MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(package_name, 0); @@ -665,8 +778,7 @@ variable_select(PG_FUNCTION_ARGS) } else { - variables_stats = list_delete(variables_stats, rstat); - pfree(rstat); + remove_variables_status(&variables_stats, rstat); SRF_RETURN_DONE(funcctx); } } @@ -968,6 +1080,8 @@ remove_package(PG_FUNCTION_ARGS) resetVariablesCache(); + remove_variables_package(&variables_stats, package); + PG_FREE_IF_COPY(package_name, 0); PG_RETURN_VOID(); } @@ -1063,6 +1177,7 @@ remove_packages(PG_FUNCTION_ARGS) } resetVariablesCache(); + remove_variables_all(&variables_stats); PG_RETURN_VOID(); } @@ -1317,7 +1432,7 @@ get_packages_stats(PG_FUNCTION_ARGS) else { packages_stats = list_delete(packages_stats, rstat); - pfree(rstat); + // pfree(rstat); SRF_RETURN_DONE(funcctx); } } @@ -1777,6 +1892,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)) @@ -2227,7 +2343,7 @@ pgvTransCallback(XactEvent event, void *arg) } if (event == XACT_EVENT_PRE_COMMIT || event == XACT_EVENT_ABORT) - freeStatsLists(true); + freeStatsLists(); } /* @@ -2241,25 +2357,23 @@ variable_ExecutorEnd(QueryDesc *queryDesc) else standard_ExecutorEnd(queryDesc); - freeStatsLists(false); + freeStatsLists(); } /* * Free hash_seq_search scans */ -void -freeStatsLists(bool deep) +static void +freeStatsLists(void) { ListCell *cell; HASH_SEQ_STATUS *status; + HtabToStat *htab_to_stat; foreach(cell, variables_stats) { - status = (HASH_SEQ_STATUS *) lfirst(cell); - hash_seq_term(status); - - if (deep) - pfree(status); + htab_to_stat = (HtabToStat *) lfirst(cell); + hash_seq_term(htab_to_stat->status); } variables_stats = NIL; @@ -2268,8 +2382,6 @@ freeStatsLists(bool deep) { status = (HASH_SEQ_STATUS *) lfirst(cell); hash_seq_term(status); - - pfree(status); } packages_stats = NIL; From 0730e206979f6c8572fa23e6ed97a4c5c1705ec7 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 28 Oct 2020 14:34:52 +0300 Subject: [PATCH 09/22] Issue #27: Add fix for Travis Ci failed tests. --- pg_variables.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 7e29b76..b22dd29 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -152,6 +152,7 @@ typedef struct tagHtabToStat { HASH_SEQ_STATUS *status; Variable *variable; Package *package; + int level; } HtabToStat; /* @@ -181,6 +182,12 @@ 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. * @@ -192,6 +199,7 @@ 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; @@ -214,6 +222,23 @@ HtabToStat_remove_if(List **l, void *value, 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 } /* @@ -243,6 +268,15 @@ remove_variables_package(List **l, Package *package) HtabToStat_remove_if(l, package, HtabToStat_package_eq, false); } +/* + * Remove all the entrys for level. + */ +static void +remove_variables_level(List **l, int level) +{ + HtabToStat_remove_if(l, &level, HtabToStat_level_eq, false); +} + /* * Remove all. */ @@ -757,6 +791,7 @@ variable_select(PG_FUNCTION_ARGS) 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); @@ -1356,7 +1391,6 @@ get_packages_stats(PG_FUNCTION_ARGS) { TupleDesc tupdesc; - //elog(INFO, " >>> "); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1432,7 +1466,7 @@ get_packages_stats(PG_FUNCTION_ARGS) else { packages_stats = list_delete(packages_stats, rstat); - // pfree(rstat); + pfree(rstat); SRF_RETURN_DONE(funcctx); } } @@ -2312,6 +2346,8 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, break; } } + + remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); } /* From 1a868834571e592bf2efadbc21e551285c856351 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 28 Oct 2020 17:53:00 +0300 Subject: [PATCH 10/22] Issue #27: Fix tests for pgv_stats on 9.5 --- expected/pg_variables_trans_1.out | 3655 +++++++++++++++++++++++++++++ 1 file changed, 3655 insertions(+) create mode 100644 expected/pg_variables_trans_1.out 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) + From 20f4ef3272f58129522e54cb72dfa5307ae08b60 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Fri, 30 Oct 2020 12:57:53 +0300 Subject: [PATCH 11/22] Fix some comments --- pg_variables.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index b22dd29..9e83352 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -141,8 +141,8 @@ static MemoryContext changesStackContext = NULL; * 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, becouse it wount be reset by the time - * pgvTransCallback is called. + * 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; @@ -191,8 +191,8 @@ HtabToStat_level_eq(HtabToStat *entry, void *value) /* * Generic remove_if algorithm for HtabToStat. * - * + eq if function pointer used to compare list entry to the value. - * + if match_first is true return on first match. + * '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, @@ -260,7 +260,7 @@ remove_variables_variable(List **l, Variable *variable) } /* - * Remove all the entrys for package. + * Remove all the entries for package. */ static void remove_variables_package(List **l, Package *package) @@ -269,7 +269,7 @@ remove_variables_package(List **l, Package *package) } /* - * Remove all the entrys for level. + * Remove all the entries for level. */ static void remove_variables_level(List **l, int level) From 4b85a7cab60cbea89a2ce731d649f7801bc4ec6c Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 3 Nov 2020 12:02:19 +0300 Subject: [PATCH 12/22] Issue #27: Fix list remove generic algo. --- pg_variables.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pg_variables.c b/pg_variables.c index 9e83352..120509f 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -226,7 +226,7 @@ HtabToStat_remove_if(List **l, void *value, /* * See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1cff1b95ab6ddae32faa3efe0d95a820dbfdc164 * - * Version > 13 have different lists interface. + * Version >= 13 have different lists interface. */ ListCell *cell; HtabToStat *entry = NULL; @@ -236,7 +236,14 @@ HtabToStat_remove_if(List **l, void *value, entry = (HtabToStat *) lfirst(cell); if (eq(entry, value)) + { *l = foreach_delete_current(*l, cell); + pfree(entry->status); + pfree(entry); + + if (match_first) + return; + } } #endif } From a68605013157bb76fa45434ba67f7dd9874cc1f6 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 3 Nov 2020 14:15:36 +0300 Subject: [PATCH 13/22] Issue #27: Fix savepoint in transaction issue. --- expected/pg_variables_trans.out | 129 ++++++++++++++++++++++ expected/pg_variables_trans_1.out | 129 ++++++++++++++++++++++ pg_variables.c | 177 ++++++++++++++++++++++++++++-- 3 files changed, 425 insertions(+), 10 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 2ce8138..bee104d 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3653,3 +3653,132 @@ SELECT 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 index bc55b84..ad84f9d 100644 --- a/expected/pg_variables_trans_1.out +++ b/expected/pg_variables_trans_1.out @@ -3653,3 +3653,132 @@ SELECT 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/pg_variables.c b/pg_variables.c index 120509f..0fb344b 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -147,16 +147,23 @@ static MemoryContext changesStackContext = NULL; static List *variables_stats = NIL; static List *packages_stats = NIL; -typedef struct tagHtabToStat { +typedef struct tagHtabToStat +{ HTAB *hash; HASH_SEQ_STATUS *status; Variable *variable; Package *package; - int level; + int level; } HtabToStat; +typedef struct tagPackageStatEntry +{ + HASH_SEQ_STATUS *status; + int level; +} PackageStatEntry; + /* - * A bunch of comp functions for HtabToStat members here. + * A bunch of comp functions for HtabToStat and PackageStatEntry members here. */ static bool HtabToStat_status_eq(HtabToStat *entry, void *value) @@ -188,6 +195,18 @@ HtabToStat_level_eq(HtabToStat *entry, void *value) return entry->level == *(int *) value; } +static bool +PackageStatEntry_status_eq(PackageStatEntry *entry, void *value) +{ + return entry->status == (HASH_SEQ_STATUS *) value; +} + +static bool +PackageStatEntry_level_eq(PackageStatEntry *entry, void *value) +{ + return entry->level == *(int *) value; +} + /* * Generic remove_if algorithm for HtabToStat. * @@ -248,6 +267,75 @@ HtabToStat_remove_if(List **l, void *value, #endif } +/* + * Generic remove_if algorithm for PackageStatEntry. + * + * 'eq' - is a function pointer used to compare list entries to the 'value'. + * 'match_first' - if true return on first match. + */ +static void +PackageStatEntry_remove_if(List **l, void *value, + bool (*eq)(PackageStatEntry *, void *), + bool match_first, + bool term) +{ +#if (PG_VERSION_NUM < 130000) + ListCell *cell, *next, *prev = NULL; + PackageStatEntry *entry = NULL; + + for (cell = list_head(*l); cell; cell = next) + { + entry = (PackageStatEntry *) lfirst(cell); + next = lnext(cell); + + if (eq(entry, value)) + { + *l = list_delete_cell(*l, cell, prev); + + if (term) + hash_seq_term(entry->status); + + 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; + PackageStatEntry *entry = NULL; + + foreach(cell, *l) + { + entry = (PackageStatEntry *) lfirst(cell); + + if (eq(entry, value)) + { + *l = foreach_delete_current(*l, cell); + + if (term) + hash_seq_term(entry->status); + + pfree(entry->status); + pfree(entry); + + if (match_first) + return; + } + } +#endif +} + /* * Remove first entry for status. */ @@ -294,6 +382,26 @@ remove_variables_all(List **l) *l = NIL; } +/* + * Remove first entry for status for packages. + */ +static void +remove_packages_status(List **l, HASH_SEQ_STATUS *status) +{ + /* Match first, not term */ + PackageStatEntry_remove_if(l, status, PackageStatEntry_status_eq, true, false); +} + +/* + * Remove first entry for status for packages. + */ +static void +remove_packages_level(List **l, int level) +{ + /* Match all, term */ + PackageStatEntry_remove_if(l, &level, PackageStatEntry_level_eq, false, true); +} + static void freeStatsLists(void); /* Returns a lists of packages and variables changed at current subxact level */ #define get_actual_changes_list() \ @@ -1416,7 +1524,8 @@ get_packages_stats(PG_FUNCTION_ARGS) */ if (packagesHash) { - MemoryContext ctx; + MemoryContext ctx; + PackageStatEntry *entry; ctx = MemoryContextSwitchTo(TopTransactionContext); rstat = (HASH_SEQ_STATUS *) palloc0(sizeof(HASH_SEQ_STATUS)); @@ -1424,7 +1533,10 @@ get_packages_stats(PG_FUNCTION_ARGS) hash_seq_init(rstat, packagesHash); funcctx->user_fctx = rstat; - packages_stats = lcons((void *)rstat, packages_stats); + entry = palloc0(sizeof(PackageStatEntry)); + entry->status = rstat; + entry->level = GetCurrentTransactionNestLevel(); + packages_stats = lcons((void *)entry, packages_stats); MemoryContextSwitchTo(ctx); } else @@ -1472,8 +1584,9 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - packages_stats = list_delete(packages_stats, rstat); - pfree(rstat); + //packages_stats = list_delete(packages_stats, rstat); + //pfree(rstat); + remove_packages_status(&packages_stats, rstat); SRF_RETURN_DONE(funcctx); } } @@ -2355,6 +2468,50 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, } remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); + remove_packages_level(&packages_stats, GetCurrentTransactionNestLevel()); + +/*#if (PG_VERSION_NUM < 130000) + { + ListCell *cell, *next, *prev = NULL; + PackageStatEntry *entry = NULL; + + for (cell = list_head(packages_stats); cell; cell = next) + { + entry = (PackageStatEntry *) lfirst(cell); + next = lnext(cell); + + if (entry->level == GetCurrentTransactionNestLevel()) + { + packages_stats = list_delete_cell(packages_stats, cell, prev); + hash_seq_term(entry->status); + pfree(entry->status); + pfree(entry); + } + else + { + prev = cell; + } + } + } +#else + { + ListCell *cell; + PackageStatEntry *entry = NULL; + + foreach(cell, packages_stats) + { + entry = (PackageStatEntry *) lfirst(cell); + + if (entry->level == GetCurrentTransactionNestLevel()) + { + packages_stats = foreach_delete_current(packages_stats, cell); + hash_seq_term(entry->status); + pfree(entry->status); + pfree(entry); + } + } + } +#endif*/ } /* @@ -2410,8 +2567,8 @@ static void freeStatsLists(void) { ListCell *cell; - HASH_SEQ_STATUS *status; HtabToStat *htab_to_stat; + PackageStatEntry *entry; foreach(cell, variables_stats) { @@ -2423,8 +2580,8 @@ freeStatsLists(void) foreach(cell, packages_stats) { - status = (HASH_SEQ_STATUS *) lfirst(cell); - hash_seq_term(status); + entry = (PackageStatEntry *) lfirst(cell); + hash_seq_term(entry->status); } packages_stats = NIL; From 2e9c17564db7b0f1f01027d9c84db81eefeff988 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 3 Nov 2020 15:19:10 +0300 Subject: [PATCH 14/22] Issue #27: Fix savepoint in transaction issue, add sql. --- sql/pg_variables_trans.sql | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 2b83ab5..b6fd7a3 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1075,3 +1075,58 @@ 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 ef847a46a94b793acbd651971d2b6cffde5d5d8e Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 3 Nov 2020 15:36:47 +0300 Subject: [PATCH 15/22] Issue #27: Fix savepoint in transaction issue, add expected result for 9 and 10. --- expected/pg_variables_trans_2.out | 3784 +++++++++++++++++++++++++++++ 1 file changed, 3784 insertions(+) create mode 100644 expected/pg_variables_trans_2.out diff --git a/expected/pg_variables_trans_2.out b/expected/pg_variables_trans_2.out new file mode 100644 index 0000000..102d550 --- /dev/null +++ b/expected/pg_variables_trans_2.out @@ -0,0 +1,3784 @@ +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,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) + From 62b0dfee3ce263c1940d988562b060c103e572f0 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 3 Nov 2020 16:21:46 +0300 Subject: [PATCH 16/22] Issue #27: Fix savepoint in transaction issue, add expected result for 9.5. --- expected/pg_variables_trans_1.out | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out index ad84f9d..697ae93 100644 --- a/expected/pg_variables_trans_1.out +++ b/expected/pg_variables_trans_1.out @@ -3668,14 +3668,14 @@ 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; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) @@ -3692,14 +3692,14 @@ 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 --------------- + pgv_stats +----------- (test,0) (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) @@ -3716,14 +3716,14 @@ 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 --------------- + pgv_stats +----------- (test,0) (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) @@ -3739,15 +3739,15 @@ 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; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) SAVEPOINT comm2; FETCH 1 in r2_cur; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) @@ -3763,14 +3763,14 @@ 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; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) FETCH 1 in r2_cur; - pgv_stats --------------- + pgv_stats +----------- (test,0) (1 row) From 0f7307942316294efecf6ebb217abc95a4d8f651 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 5 Nov 2020 11:11:59 +0300 Subject: [PATCH 17/22] 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 --- pg_variables.c | 338 ++++++++++++++++++++++++------------------------- 1 file changed, 163 insertions(+), 175 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 0fb344b..0db7887 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -147,14 +147,14 @@ static MemoryContext changesStackContext = NULL; static List *variables_stats = NIL; static List *packages_stats = NIL; -typedef struct tagHtabToStat +typedef struct tagVariableStatEntry { HTAB *hash; HASH_SEQ_STATUS *status; Variable *variable; Package *package; int level; -} HtabToStat; +} VariableStatEntry; typedef struct tagPackageStatEntry { @@ -163,142 +163,111 @@ typedef struct tagPackageStatEntry } PackageStatEntry; /* - * A bunch of comp functions for HtabToStat and PackageStatEntry members here. + * Compare functions for VariableStatEntry and PackageStatEntry members. */ static bool -HtabToStat_status_eq(HtabToStat *entry, void *value) +VariableStatEntry_status_eq(void *entry, void *value) { - return entry->status == (HASH_SEQ_STATUS *) value; + return ((VariableStatEntry *) entry)->status == (HASH_SEQ_STATUS *) value; } static bool -HtabToStat_variable_eq(HtabToStat *entry, void *value) +VariableStatEntry_variable_eq(void *entry, void *value) { - return entry->variable == (Variable *) value; + return ((VariableStatEntry *) entry)->variable == (Variable *) value; } static bool -HtabToStat_package_eq(HtabToStat *entry, void *value) +VariableStatEntry_package_eq(void *entry, void *value) { - return entry->package == (Package *) value; + return ((VariableStatEntry *) entry)->package == (Package *) value; } static bool -HtabToStat_eq_all(HtabToStat *entry, void *value) +VariableStatEntry_eq_all(void *entry, void *value) { return true; } static bool -HtabToStat_level_eq(HtabToStat *entry, void *value) +VariableStatEntry_level_eq(void *entry, void *value) { - return entry->level == *(int *) value; + return ((VariableStatEntry *) entry)->level == *(int *) value; } static bool -PackageStatEntry_status_eq(PackageStatEntry *entry, void *value) +PackageStatEntry_status_eq(void *entry, void *value) { - return entry->status == (HASH_SEQ_STATUS *) value; + return ((PackageStatEntry *) entry)->status == (HASH_SEQ_STATUS *) value; } static bool -PackageStatEntry_level_eq(PackageStatEntry *entry, void *value) +PackageStatEntry_level_eq(void *entry, void *value) { - return entry->level == *(int *) value; + return ((PackageStatEntry *) 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. + * VariableStatEntry and PackageStatEntry status member getters. */ -static void -HtabToStat_remove_if(List **l, void *value, - bool (*eq)(HtabToStat *, void *), - bool match_first) +static HASH_SEQ_STATUS * +VariableStatEntry_status_ptr(void *entry) { -#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); - pfree(entry->status); - pfree(entry); + return ((VariableStatEntry *) entry)->status; +} - if (match_first) - return; - } - } -#endif +static HASH_SEQ_STATUS * +PackageStatEntry_status_ptr(void *entry) +{ + return ((PackageStatEntry *) entry)->status; } /* - * Generic remove_if algorithm for PackageStatEntry. + * 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. * - * 'eq' - is a function pointer used to compare list entries to the 'value'. - * 'match_first' - if true return on first match. */ +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 -PackageStatEntry_remove_if(List **l, void *value, - bool (*eq)(PackageStatEntry *, void *), - bool match_first, - bool term) +list_remove_if(RemoveIfContext ctx) { #if (PG_VERSION_NUM < 130000) - ListCell *cell, *next, *prev = NULL; - PackageStatEntry *entry = NULL; + ListCell *cell, *next, *prev = NULL; + void *entry = NULL; - for (cell = list_head(*l); cell; cell = next) + for (cell = list_head(*ctx.list); cell; cell = next) { - entry = (PackageStatEntry *) lfirst(cell); + entry = lfirst(cell); next = lnext(cell); - if (eq(entry, value)) + if (ctx.eq(entry, ctx.value)) { - *l = list_delete_cell(*l, cell, prev); + *ctx.list = list_delete_cell(*ctx.list, cell, prev); - if (term) - hash_seq_term(entry->status); + if (ctx.term) + hash_seq_term(ctx.getter(entry)); - pfree(entry->status); + pfree(ctx.getter(entry)); pfree(entry); - if (match_first) + if (ctx.match_first) return; } else @@ -312,24 +281,24 @@ PackageStatEntry_remove_if(List **l, void *value, * * Version >= 13 have different lists interface. */ - ListCell *cell; - PackageStatEntry *entry = NULL; + ListCell *cell; + void *entry = NULL; - foreach(cell, *l) + foreach(cell, *ctx.list) { - entry = (PackageStatEntry *) lfirst(cell); + entry = lfirst(cell); - if (eq(entry, value)) + if (ctx.eq(entry, ctx.value)) { - *l = foreach_delete_current(*l, cell); + *ctx.list = foreach_delete_current(*ctx.list, cell); - if (term) - hash_seq_term(entry->status); + if (ctx.term) + hash_seq_term(ctx.getter(entry)); - pfree(entry->status); + pfree(ctx.getter(entry)); pfree(entry); - if (match_first) + if (ctx.match_first) return; } } @@ -340,66 +309,130 @@ PackageStatEntry_remove_if(List **l, void *value, * Remove first entry for status. */ static void -remove_variables_status(List **l, HASH_SEQ_STATUS *status) +remove_variables_status(List **list, HASH_SEQ_STATUS *status) { - HtabToStat_remove_if(l, status, HtabToStat_status_eq, true); + 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 **l, Variable *variable) +remove_variables_variable(List **list, Variable *variable) { - HtabToStat_remove_if(l, variable, HtabToStat_variable_eq, true); + /* + * 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 **l, Package *package) +remove_variables_package(List **list, Package *package) { - HtabToStat_remove_if(l, package, HtabToStat_package_eq, false); + 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 **l, int level) +remove_variables_level(List **list, int level) { - HtabToStat_remove_if(l, &level, HtabToStat_level_eq, false); + RemoveIfContext ctx = + { + .list = list, + .value = &level, + .eq = VariableStatEntry_level_eq, + .getter = VariableStatEntry_status_ptr, + .match_first = false, + .term = false + }; + list_remove_if(ctx); } /* - * Remove all. + * Delete variables stats list. */ static void -remove_variables_all(List **l) +remove_variables_all(List **list) { - HtabToStat_remove_if(l, NULL, HtabToStat_eq_all, false); - *l = NIL; + 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 entry for status for packages. + * Remove first entrie with status for packages list. */ static void -remove_packages_status(List **l, HASH_SEQ_STATUS *status) +remove_packages_status(List **list, HASH_SEQ_STATUS *status) { - /* Match first, not term */ - PackageStatEntry_remove_if(l, status, PackageStatEntry_status_eq, true, false); + RemoveIfContext ctx = + { + .list = list, + .value = status, + .eq = PackageStatEntry_status_eq, + .getter = PackageStatEntry_status_ptr, + .match_first = true, + .term = false + }; + list_remove_if(ctx); } /* - * Remove first entry for status for packages. + * Remove all the entries with level for packages list. */ static void -remove_packages_level(List **l, int level) +remove_packages_level(List **list, int level) { - /* Match all, term */ - PackageStatEntry_remove_if(l, &level, PackageStatEntry_level_eq, false, true); + 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); @@ -886,9 +919,9 @@ variable_select(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; - RecordVar *record; - HtabToStat *htab_to_stat; + MemoryContext oldcontext; + RecordVar *record; + VariableStatEntry *entry; record = &(GetActualValue(variable).record); funcctx = SRF_FIRSTCALL_INIT(); @@ -901,13 +934,13 @@ 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); + 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); @@ -2469,49 +2502,6 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, remove_variables_level(&variables_stats, GetCurrentTransactionNestLevel()); remove_packages_level(&packages_stats, GetCurrentTransactionNestLevel()); - -/*#if (PG_VERSION_NUM < 130000) - { - ListCell *cell, *next, *prev = NULL; - PackageStatEntry *entry = NULL; - - for (cell = list_head(packages_stats); cell; cell = next) - { - entry = (PackageStatEntry *) lfirst(cell); - next = lnext(cell); - - if (entry->level == GetCurrentTransactionNestLevel()) - { - packages_stats = list_delete_cell(packages_stats, cell, prev); - hash_seq_term(entry->status); - pfree(entry->status); - pfree(entry); - } - else - { - prev = cell; - } - } - } -#else - { - ListCell *cell; - PackageStatEntry *entry = NULL; - - foreach(cell, packages_stats) - { - entry = (PackageStatEntry *) lfirst(cell); - - if (entry->level == GetCurrentTransactionNestLevel()) - { - packages_stats = foreach_delete_current(packages_stats, cell); - hash_seq_term(entry->status); - pfree(entry->status); - pfree(entry); - } - } - } -#endif*/ } /* @@ -2567,20 +2557,18 @@ static void freeStatsLists(void) { ListCell *cell; - HtabToStat *htab_to_stat; - PackageStatEntry *entry; foreach(cell, variables_stats) { - htab_to_stat = (HtabToStat *) lfirst(cell); - hash_seq_term(htab_to_stat->status); + VariableStatEntry *entry = (VariableStatEntry *) lfirst(cell); + hash_seq_term(entry->status); } variables_stats = NIL; foreach(cell, packages_stats) { - entry = (PackageStatEntry *) lfirst(cell); + PackageStatEntry *entry = (PackageStatEntry *) lfirst(cell); hash_seq_term(entry->status); } From 88633c3bec33abbb2fd68a41c5c2cbe324aaec20 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 5 Nov 2020 16:26:14 +0300 Subject: [PATCH 18/22] 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. --- expected/pg_variables_trans.out | 15 +++++++-------- expected/pg_variables_trans_1.out | 15 +++++++-------- expected/pg_variables_trans_2.out | 15 +++++++-------- sql/pg_variables_trans.sql | 8 +++++--- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index bee104d..944a8a4 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2850,14 +2850,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2875,14 +2874,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2900,14 +2898,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -3440,6 +3437,7 @@ FETCH 1 in r1_cur; (2,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z2'); pgv_remove ------------ @@ -3447,7 +3445,7 @@ SELECT pgv_remove('test', 'z2'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z2" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z2'); ERROR: unrecognized variable "z2" @@ -3472,6 +3470,7 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z3'); pgv_remove ------------ @@ -3479,7 +3478,7 @@ SELECT pgv_remove('test', 'z3'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z3" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z3'); pgv_select diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out index 697ae93..278272a 100644 --- a/expected/pg_variables_trans_1.out +++ b/expected/pg_variables_trans_1.out @@ -2850,14 +2850,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2875,14 +2874,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2900,14 +2898,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -3440,6 +3437,7 @@ FETCH 1 in r1_cur; (2,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z2'); pgv_remove ------------ @@ -3447,7 +3445,7 @@ SELECT pgv_remove('test', 'z2'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z2" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z2'); ERROR: unrecognized variable "z2" @@ -3472,6 +3470,7 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z3'); pgv_remove ------------ @@ -3479,7 +3478,7 @@ SELECT pgv_remove('test', 'z3'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z3" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z3'); pgv_select diff --git a/expected/pg_variables_trans_2.out b/expected/pg_variables_trans_2.out index 102d550..df11ca6 100644 --- a/expected/pg_variables_trans_2.out +++ b/expected/pg_variables_trans_2.out @@ -2850,14 +2850,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2875,14 +2874,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -2900,14 +2898,13 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; 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" @@ -3440,6 +3437,7 @@ FETCH 1 in r1_cur; (2,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z2'); pgv_remove ------------ @@ -3447,7 +3445,7 @@ SELECT pgv_remove('test', 'z2'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z2" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z2'); ERROR: unrecognized variable "z2" @@ -3472,6 +3470,7 @@ FETCH 1 in r1_cur; (1,2) (1 row) +CLOSE r1_cur; SELECT pgv_remove('test', 'z3'); pgv_remove ------------ @@ -3479,7 +3478,7 @@ SELECT pgv_remove('test', 'z3'); (1 row) FETCH 1 in r1_cur; -ERROR: unrecognized variable "z3" +ERROR: cursor "r1_cur" does not exist ROLLBACK; SELECT pgv_select('test', 'z3'); pgv_select diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index b6fd7a3..afd610f 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -802,8 +802,8 @@ 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'); -FETCH 1 in r1_cur; ROLLBACK; SELECT pgv_select('test', 'x'); @@ -811,8 +811,8 @@ 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'); -FETCH 1 in r1_cur; ROLLBACK; SELECT pgv_select('test', 'x'); @@ -820,8 +820,8 @@ 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(); -FETCH 1 in r1_cur; ROLLBACK; SELECT pgv_select('test', 'x'); @@ -983,6 +983,7 @@ 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; @@ -994,6 +995,7 @@ 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; From 0541b0695968a847560427b635e4f309123adc86 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Tue, 10 Nov 2020 20:58:40 +0300 Subject: [PATCH 19/22] 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. --- expected/pg_variables_trans.out | 51 ++++++++++--------------------- expected/pg_variables_trans_1.out | 51 ++++++++++--------------------- expected/pg_variables_trans_2.out | 51 ++++++++++--------------------- pg_variables.c | 43 ++++++++++++++++++++------ sql/pg_variables_trans.sql | 27 ++++++++-------- 5 files changed, 96 insertions(+), 127 deletions(-) diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 944a8a4..8113fc8 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -2987,27 +2987,19 @@ SELECT pgv_select('test', 'x'); --- --- 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 ------------- +--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) -COMMIT; --- --- Tests for "leaked hash_seq_search scan for hash table" --- @@ -3486,23 +3478,12 @@ SELECT pgv_select('test', 'z3'); (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" +--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 ---------- diff --git a/expected/pg_variables_trans_1.out b/expected/pg_variables_trans_1.out index 278272a..b6f8132 100644 --- a/expected/pg_variables_trans_1.out +++ b/expected/pg_variables_trans_1.out @@ -2987,27 +2987,19 @@ SELECT pgv_select('test', 'x'); --- --- 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 ------------- +--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) -COMMIT; --- --- Tests for "leaked hash_seq_search scan for hash table" --- @@ -3486,23 +3478,12 @@ SELECT pgv_select('test', 'z3'); (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" +--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 ---------- diff --git a/expected/pg_variables_trans_2.out b/expected/pg_variables_trans_2.out index df11ca6..a07ad7d 100644 --- a/expected/pg_variables_trans_2.out +++ b/expected/pg_variables_trans_2.out @@ -2987,27 +2987,19 @@ SELECT pgv_select('test', 'x'); --- --- 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 ------------- +--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) -COMMIT; --- --- Tests for "leaked hash_seq_search scan for hash table" --- @@ -3486,23 +3478,12 @@ SELECT pgv_select('test', 'z3'); (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" +--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 ---------- diff --git a/pg_variables.c b/pg_variables.c index 0db7887..d9f1273 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -10,6 +10,7 @@ #include "postgres.h" #include "fmgr.h" #include "funcapi.h" +#include "miscadmin.h" #include "access/htup_details.h" #include "access/xact.h" @@ -1617,8 +1618,6 @@ get_packages_stats(PG_FUNCTION_ARGS) } else { - //packages_stats = list_delete(packages_stats, rstat); - //pfree(rstat); remove_packages_status(&packages_stats, rstat); SRF_RETURN_DONE(funcctx); } @@ -2462,15 +2461,43 @@ processChanges(Action action) } } +/* + * ATX and connection pooling are not compatible with pg_variables. + */ static void compatibility_check(void) { #ifdef PGPRO_EE -# 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"); + +# if (PG_VERSION_NUM < 100000) + /* + * This versions does not have dedicated macro to check compatibility. + * So, use simple check here. + */ +# define PG_COMPATIBILITY_CHECK(name) \ + if (getNestLevelATX() != 0) \ + elog(ERROR, "%s extension is not compatible with autonomous " \ + "transactions and connection pooling", name); +# else + /* + * Since ee12 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. + */ +# ifndef PG_COMPATIBILITY_CHECK +# define PG_COMPATIBILITY_CHECK_LOCK +# define PG_COMPATIBILITY_CHECK(name) +# endif + + PG_COMPATIBILITY_CHECK("pg_variables"); + +# ifdef PG_COMPATIBILITY_CHECK_LOCK +# undef PG_COMPATIBILITY_CHECK_LOCK +# undef PG_COMPATIBILITY_CHECK +# endif # endif + #endif } @@ -2486,8 +2513,8 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, switch (event) { case SUBXACT_EVENT_START_SUB: - pushChangesStack(); compatibility_check(); + pushChangesStack(); break; case SUBXACT_EVENT_COMMIT_SUB: processChanges(RELEASE_SAVEPOINT); @@ -2546,8 +2573,6 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); - - freeStatsLists(); } /* diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index afd610f..8cb1d2f 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -854,13 +854,14 @@ 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; - +--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" --- @@ -1001,12 +1002,12 @@ 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'); +--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 From 58416ba51e974451193f869e73459036e1e1faf1 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Wed, 11 Nov 2020 18:35:51 +0300 Subject: [PATCH 20/22] Issue #27: Make sanitizer happy. Sanitizer did failed on query like "SELECT pgv_select(...) LIMIT 1" --- pg_variables.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pg_variables.c b/pg_variables.c index d9f1273..4639938 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2573,6 +2573,8 @@ variable_ExecutorEnd(QueryDesc *queryDesc) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); + + freeStatsLists(); } /* From 6d7aa7a209979125a5f39b1c014a89a1e37ab5de Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 12 Nov 2020 16:20:25 +0300 Subject: [PATCH 21/22] Issue #27: Fix fail under sanitizer with connoll. --- pg_variables.c | 62 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 4639938..739ef30 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -16,6 +16,7 @@ #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" @@ -2467,38 +2468,63 @@ processChanges(Action action) 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 + /* All the ee have ATX. */ +# define ATX_CHECK (getNestLevelATX() != 0) + /* ee10 and less does not have connpool. */ +# if (PG_VERSION_NUM >= 110000) +# define CONNPOOL_CHECK (!IsDedicatedBackend) +# else +# define CONNPOOL_CHECK (false) +# endif + # if (PG_VERSION_NUM < 100000) /* * This versions does not have dedicated macro to check compatibility. - * So, use simple check here. + * So, use simple check here for ATX. */ -# define PG_COMPATIBILITY_CHECK(name) \ - if (getNestLevelATX() != 0) \ - elog(ERROR, "%s extension is not compatible with autonomous " \ - "transactions and connection pooling", name); + if (ATX_CHECK) + { + freeStatsLists(); + elog(ERROR, "pg_variables extension is not compatible with " + "autonomous transactions"); + } # else /* - * Since ee12 there is PG_COMPATIBILITY_CHECK macro to check compatibility. + * 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. */ -# ifndef PG_COMPATIBILITY_CHECK -# define PG_COMPATIBILITY_CHECK_LOCK -# define PG_COMPATIBILITY_CHECK(name) -# endif +# ifdef PG_COMPATIBILITY_CHECK + { + if (ATX_CHECK || CONNPOOL_CHECK) + freeStatsLists(); - PG_COMPATIBILITY_CHECK("pg_variables"); + PG_COMPATIBILITY_CHECK("pg_variables"); + } +# endif /* PG_COMPATIBILITY_CHECK */ +# endif /* PG_VERSION_NUM */ -# ifdef PG_COMPATIBILITY_CHECK_LOCK -# undef PG_COMPATIBILITY_CHECK_LOCK -# undef PG_COMPATIBILITY_CHECK -# endif -# endif +# undef ATX_CHECK +# undef CONNPOOL_CHECK -#endif +#endif /* PGPRO_EE */ } /* @@ -2513,8 +2539,8 @@ pgvSubTransCallback(SubXactEvent event, SubTransactionId mySubid, switch (event) { case SUBXACT_EVENT_START_SUB: - compatibility_check(); pushChangesStack(); + compatibility_check(); break; case SUBXACT_EVENT_COMMIT_SUB: processChanges(RELEASE_SAVEPOINT); From ce86d27165319cec4c99927f517ba91060d86f35 Mon Sep 17 00:00:00 2001 From: Maxim Orlov Date: Thu, 12 Nov 2020 21:04:35 +0300 Subject: [PATCH 22/22] Issue #27: Improve compatibility check. Use pg_compatibility_check_no_error function to precheck compatibility in order to free memory. --- pg_variables.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pg_variables.c b/pg_variables.c index 739ef30..4bd6bdd 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -2484,21 +2484,12 @@ compatibility_check(void) */ #ifdef PGPRO_EE - /* All the ee have ATX. */ -# define ATX_CHECK (getNestLevelATX() != 0) - /* ee10 and less does not have connpool. */ -# if (PG_VERSION_NUM >= 110000) -# define CONNPOOL_CHECK (!IsDedicatedBackend) -# else -# define CONNPOOL_CHECK (false) -# endif - # if (PG_VERSION_NUM < 100000) /* * This versions does not have dedicated macro to check compatibility. * So, use simple check here for ATX. */ - if (ATX_CHECK) + if (getNestLevelATX() != 0) { freeStatsLists(); elog(ERROR, "pg_variables extension is not compatible with " @@ -2513,7 +2504,7 @@ compatibility_check(void) */ # ifdef PG_COMPATIBILITY_CHECK { - if (ATX_CHECK || CONNPOOL_CHECK) + if (!pg_compatibility_check_no_error()) freeStatsLists(); PG_COMPATIBILITY_CHECK("pg_variables");