diff --git a/Makefile b/Makefile index 1eb4ae3..2022602 100755 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ DATA = $(wildcard updates/*--*.sql) sql/$(EXTENSION)--$(EXTVERSION).sql TESTS = 00_init 01_oncommitdelete 02_oncommitpreserve \ 03_createontruncate 04_rename 05_useindex \ 06_createas 07_createlike 08_plplgsql \ - 09_transaction 10_foreignkey + 09_transaction 10_foreignkey 11_after_error REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) REGRESS_OPTS = --inputdir=test diff --git a/pgtt.c b/pgtt.c index 7eae46c..f04b68b 100755 --- a/pgtt.c +++ b/pgtt.c @@ -360,9 +360,9 @@ gtt_ProcessUtility(GTT_PROCESSUTILITY_PROTO) } /* - * Look at utility command to look at CREATE TABLE / DROP TABLE - * and INSERT INTO statements if a Global Temporary Table is - * concerned. + * Look at utility command to search CREATE TABLE / DROP TABLE + * and INSERT INTO statements to see if a Global Temporary Table + * is concerned. * Return true if all work is done and the origin statement must * be forgotten. False mean that the statement must be processed * normally. @@ -1531,8 +1531,18 @@ gtt_post_parse_analyze(ParseState *pstate, Query *query) if (gtt.relname[0] != '\0') { + /* After an error and rollback the table is still registered in cache but must be initialized */ + if (gtt.created && OidIsValid(gtt.temp_relid) + && !SearchSysCacheExists1(RELOID, ObjectIdGetDatum(gtt.temp_relid)) + ) + { + elog(DEBUG1, "invalid temporary table with relid %d (%s), reseting.", gtt.temp_relid, gtt.relname); + gtt.created = false; + gtt.temp_relid = 0; + } /* Create the temporary table if it does not exists */ - if (!gtt.created) { + if (!gtt.created) + { elog(DEBUG1, "global temporary table from relid %d does not exists create it: %s", rte->relid, gtt.relname); /* Call create temporary table */ if ((gtt.temp_relid = create_temporary_table_internal(gtt.relid, gtt.preserved)) != InvalidOid) diff --git a/test/expected/05_useindex.out b/test/expected/05_useindex.out index d3319ec..879040a 100644 --- a/test/expected/05_useindex.out +++ b/test/expected/05_useindex.out @@ -74,14 +74,13 @@ SELECT tablename, indexname FROM pg_indexes WHERE tablename = 't_glob_temptable1 INSERT INTO t_glob_temptable1 VALUES (2, 'two'); -- Verify that the index is used +SET enable_bitmapscan TO off; EXPLAIN (COSTS OFF) SELECT * FROM t_glob_temptable1 WHERE id = 2; - QUERY PLAN ------------------------------------------------------ - Bitmap Heap Scan on t_glob_temptable1 - Recheck Cond: (id = 2) - -> Bitmap Index Scan on t_glob_temptable1_id_idx - Index Cond: (id = 2) -(4 rows) + QUERY PLAN +---------------------------------------------------------------- + Index Scan using t_glob_temptable1_id_idx on t_glob_temptable1 + Index Cond: (id = 2) +(2 rows) -- Reconnect and drop it \c - - diff --git a/test/expected/08_plplgsql.out b/test/expected/08_plplgsql.out index f5f8509..1da98cd 100644 --- a/test/expected/08_plplgsql.out +++ b/test/expected/08_plplgsql.out @@ -94,6 +94,7 @@ SELECT * FROM t_glob_temptable1; -- Reconnect without dropping the global temporary table \c - - LOAD 'pgtt'; +SET pgtt.enabled TO off; -- Verify that only the temporary table have been dropped -- Only the "template" unlogged table should exists SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; diff --git a/test/expected/11_after_error.out b/test/expected/11_after_error.out new file mode 100644 index 0000000..a5b88a8 --- /dev/null +++ b/test/expected/11_after_error.out @@ -0,0 +1,95 @@ +-- Import the library +LOAD 'pgtt'; +-- Create a GTT like table to test ON COMMIT DELETE ROWS +CREATE GLOBAL TEMPORARY TABLE t_glob_temptable1 (id integer, lbl text) ON COMMIT DELETE ROWS; +WARNING: GLOBAL is deprecated in temporary table creation +LINE 1: CREATE GLOBAL TEMPORARY TABLE t_glob_temptable1 (id integer,... + ^ +-- Look at Global Temporary Table definition +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; + nspname | relname | preserved | code +-------------+-------------------+-----------+---------------------- + pgtt_schema | t_glob_temptable1 | f | id integer, lbl text +(1 row) + +-- A "template" unlogged table should exists +SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + nspname | relname +-------------+------------------- + pgtt_schema | t_glob_temptable1 +(1 row) + +BEGIN; +-- With the first insert some value in the temporary table +INSERT INTO t_glob_temptable1 VALUES (1, 'One'); +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + regexp_replace | relname +----------------+------------------- + pgtt_schema | t_glob_temptable1 + pg_temp_x | t_glob_temptable1 +(2 rows) + +-- Verify the registering of the temporary table +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; + nspname | relname | preserved | code +-------------+-------------------+-----------+---------------------- + pgtt_schema | t_glob_temptable1 | f | id integer, lbl text +(1 row) + +-- Second insert failure +INSERT INTO t_glob_temptable1 VALUES ('Two', 2); +ERROR: invalid input syntax for type integer: "Two" +LINE 1: INSERT INTO t_glob_temptable1 VALUES ('Two', 2); + ^ +ROLLBACK; +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + regexp_replace | relname +----------------+------------------- + pgtt_schema | t_glob_temptable1 +(1 row) + +-- Insert a new row +BEGIN; +-- With the first insert some value in the temporary table +-- Should not return an error that table doesn't exists +INSERT INTO t_glob_temptable1 VALUES (2, 'Two'); +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + regexp_replace | relname +----------------+------------------- + pgtt_schema | t_glob_temptable1 + pg_temp_x | t_glob_temptable1 +(2 rows) + +-- Verify the insert +SELECT * FROM t_glob_temptable1; + id | lbl +----+----- + 2 | Two +(1 row) + +COMMIT; +-- Reconnect and drop the GTT +\c - - +LOAD 'pgtt'; +SHOW search_path; + search_path +-------------------- + public,pgtt_schema +(1 row) + +DROP TABLE t_glob_temptable1; +-- Look at Global Temporary Table definition +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; -- should be empty + nspname | relname | preserved | code +---------+---------+-----------+------ +(0 rows) + +-- The "template" unlogged table should not exists anymore +SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + nspname | relname +---------+--------- +(0 rows) + diff --git a/test/sql/05_useindex.sql b/test/sql/05_useindex.sql index df67c50..cdefc18 100644 --- a/test/sql/05_useindex.sql +++ b/test/sql/05_useindex.sql @@ -57,6 +57,7 @@ SELECT tablename, indexname FROM pg_indexes WHERE tablename = 't_glob_temptable1 INSERT INTO t_glob_temptable1 VALUES (2, 'two'); -- Verify that the index is used +SET enable_bitmapscan TO off; EXPLAIN (COSTS OFF) SELECT * FROM t_glob_temptable1 WHERE id = 2; -- Reconnect and drop it diff --git a/test/sql/08_plplgsql.sql b/test/sql/08_plplgsql.sql index 0e49e7d..486313a 100644 --- a/test/sql/08_plplgsql.sql +++ b/test/sql/08_plplgsql.sql @@ -61,6 +61,8 @@ SELECT * FROM t_glob_temptable1; \c - - LOAD 'pgtt'; +SET pgtt.enabled TO off; + -- Verify that only the temporary table have been dropped -- Only the "template" unlogged table should exists SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; diff --git a/test/sql/11_after_error.sql b/test/sql/11_after_error.sql new file mode 100644 index 0000000..eb5724b --- /dev/null +++ b/test/sql/11_after_error.sql @@ -0,0 +1,60 @@ +-- Import the library +LOAD 'pgtt'; + +-- Create a GTT like table to test ON COMMIT DELETE ROWS +CREATE GLOBAL TEMPORARY TABLE t_glob_temptable1 (id integer, lbl text) ON COMMIT DELETE ROWS; + +-- Look at Global Temporary Table definition +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; + +-- A "template" unlogged table should exists +SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + +BEGIN; + +-- With the first insert some value in the temporary table +INSERT INTO t_glob_temptable1 VALUES (1, 'One'); + +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + +-- Verify the registering of the temporary table +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; + +-- Second insert failure +INSERT INTO t_glob_temptable1 VALUES ('Two', 2); + +ROLLBACK; + +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + +-- Insert a new row +BEGIN; + +-- With the first insert some value in the temporary table +-- Should not return an error that table doesn't exists +INSERT INTO t_glob_temptable1 VALUES (2, 'Two'); + +-- Look if we have two tables now +SELECT regexp_replace(n.nspname, '\d+', 'x', 'g'), c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; + +-- Verify the insert +SELECT * FROM t_glob_temptable1; + +COMMIT; + +-- Reconnect and drop the GTT +\c - - + +LOAD 'pgtt'; + +SHOW search_path; +DROP TABLE t_glob_temptable1; + +-- Look at Global Temporary Table definition +SELECT nspname, relname, preserved, code FROM pgtt_schema.pg_global_temp_tables; -- should be empty + +-- The "template" unlogged table should not exists anymore +SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON (c.relnamespace=n.oid) WHERE relname = 't_glob_temptable1'; +