Skip to content

Commit

Permalink
Fix error with temporary table not created but still in cache after a…
Browse files Browse the repository at this point in the history
… rollback.
  • Loading branch information
darold committed Nov 8, 2020
1 parent f31c05c commit 9c99940
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -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
Expand Down
18 changes: 14 additions & 4 deletions pgtt.c
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 6 additions & 7 deletions test/expected/05_useindex.out
Expand Up @@ -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 - -
Expand Down
1 change: 1 addition & 0 deletions test/expected/08_plplgsql.out
Expand Up @@ -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';
Expand Down
95 changes: 95 additions & 0 deletions 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)

1 change: 1 addition & 0 deletions test/sql/05_useindex.sql
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions test/sql/08_plplgsql.sql
Expand Up @@ -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';
Expand Down
60 changes: 60 additions & 0 deletions 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';

0 comments on commit 9c99940

Please sign in to comment.