Skip to content

Commit

Permalink
Fix memory context bug executing TRUNCATE.
Browse files Browse the repository at this point in the history
Inside the `process_truncate` function is created a new relations list
removing the distributed hypertables and this new list is assigned to
the current statement relation list. This new list is allocated into
the `PortalContext` that is destroyed at the end of the statement
execution.

The problem arise on the subsequent `TRUNCATE` call because the
compiled plpgsql code is cached into another memory context and the
elements of the relations inside this cache is pointing to an invalid
memory area because the `PortalContext` is destroyed.

Fixed it by doesn't changing the original relation list of the
statement and instead of call the `standard_ProcessUtility` we call
direct the `ExecuteTruncate` over the new created list without the
distributed hypertables.

Fixes timescale#3580, timescale#3622, timescale#3181
  • Loading branch information
fabriziomello committed Oct 1, 2021
1 parent 609b5ea commit 0f44971
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 16 deletions.
15 changes: 7 additions & 8 deletions src/process_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,8 @@ process_truncate(ProcessUtilityArgs *args)
List *hypertables = NIL;
List *relations = NIL;

Assert(IsA(stmt, TruncateStmt));

/* For all hypertables, we drop the now empty chunks. We also propagate the
* TRUNCATE call to the compressed version of the hypertable, if it exists.
*/
Expand Down Expand Up @@ -1008,15 +1010,12 @@ process_truncate(ProcessUtilityArgs *args)
}
}

/* Update relations list to include only tables that hold data. On an
* access node, distributed hypertables hold no data and chunks are
* foreign tables, so those tables are excluded. */
stmt->relations = relations;

if (stmt->relations != NIL)
/* Truncate only regular tables */
if (relations != NIL)
{
/* Call standard PostgreSQL handler for remaining tables */
prev_ProcessUtility(args);
TruncateStmt relations_stmt = *stmt;
relations_stmt.relations = relations;
ExecuteTruncate(&relations_stmt);
}

/* For all hypertables, we drop the now empty chunks */
Expand Down
54 changes: 51 additions & 3 deletions test/expected/truncate.out
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,54 @@ SELECT * FROM truncate_normal;
1
(1 row)

-- fix for bug #3580
CREATE TABLE truncate_nested (color int);
INSERT INTO truncate_nested VALUES (2);
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
1 | 2
(1 row)

CREATE FUNCTION fn_truncate_nested()
RETURNS trigger LANGUAGE plpgsql
AS $$
BEGIN
TRUNCATE truncate_nested;
RETURN NEW;
END;
$$;
CREATE TRIGGER tg_truncate_nested
BEFORE TRUNCATE ON truncate_normal
FOR EACH STATEMENT EXECUTE FUNCTION fn_truncate_nested();
TRUNCATE truncate_normal;
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
(0 rows)

INSERT INTO truncate_normal VALUES (3);
INSERT INTO truncate_nested VALUES (4);
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
3 | 4
(1 row)

TRUNCATE truncate_normal;
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
(0 rows)

INSERT INTO truncate_normal VALUES (5);
INSERT INTO truncate_nested VALUES (6);
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
5 | 6
(1 row)

SELECT * FROM test.show_subtables('"two_Partitions"');
Child | Tablespace
----------------------------------------+------------
Expand All @@ -174,8 +222,8 @@ SELECT * FROM "two_Partitions";
------------+-----------+----------+----------+----------+-------------
(0 rows)

SELECT * FROM truncate_normal;
color
-------
SELECT * FROM truncate_normal, truncate_nested;
color | color
-------+-------
(0 rows)

36 changes: 35 additions & 1 deletion test/sql/truncate.sql
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,44 @@ CREATE TABLE truncate_normal (color int);
INSERT INTO truncate_normal VALUES (1);
SELECT * FROM truncate_normal;

-- fix for bug #3580
CREATE TABLE truncate_nested (color int);
INSERT INTO truncate_nested VALUES (2);

SELECT * FROM truncate_normal, truncate_nested;

CREATE FUNCTION fn_truncate_nested()
RETURNS trigger LANGUAGE plpgsql
AS $$
BEGIN
TRUNCATE truncate_nested;
RETURN NEW;
END;
$$;

CREATE TRIGGER tg_truncate_nested
BEFORE TRUNCATE ON truncate_normal
FOR EACH STATEMENT EXECUTE FUNCTION fn_truncate_nested();

TRUNCATE truncate_normal;
SELECT * FROM truncate_normal, truncate_nested;

INSERT INTO truncate_normal VALUES (3);
INSERT INTO truncate_nested VALUES (4);

SELECT * FROM truncate_normal, truncate_nested;

TRUNCATE truncate_normal;
SELECT * FROM truncate_normal, truncate_nested;

INSERT INTO truncate_normal VALUES (5);
INSERT INTO truncate_nested VALUES (6);
SELECT * FROM truncate_normal, truncate_nested;

SELECT * FROM test.show_subtables('"two_Partitions"');

TRUNCATE "two_Partitions", truncate_normal CASCADE;
-- should be empty
SELECT * FROM test.show_subtables('"two_Partitions"');
SELECT * FROM "two_Partitions";
SELECT * FROM truncate_normal;
SELECT * FROM truncate_normal, truncate_nested;
9 changes: 5 additions & 4 deletions tsl/src/remote/dist_ddl.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,10 @@ dist_ddl_preprocess(ProcessUtilityArgs *args)
case T_TruncateStmt:
{
TruncateStmt *stmt = (TruncateStmt *) args->parsetree;
/* Calculate number of regular tables. Note that distributed
* hypertables are filtered from the relations list in
* process_utility.c, so the list might actually be empty. */

/* Calculate number of regular tables. Note that number of
* distributed hypertables should be the same of the relations
* list */
unsigned int num_regular_tables =
list_length(stmt->relations) - num_hypertables - num_dist_hypertable_members;

Expand All @@ -483,7 +484,7 @@ dist_ddl_preprocess(ProcessUtilityArgs *args)
* since other tables might not exist on data nodes and multiple
* distributed hypertables might be distributed across different
* sets of nodes. */
if (num_dist_hypertables == 1 && num_regular_tables == 0 && num_hypertables == 0 &&
if (num_dist_hypertables == 1 && num_regular_tables == 1 && num_hypertables == 0 &&
num_dist_hypertable_members == 0)
set_dist_exec_type(DIST_DDL_EXEC_ON_START);
else
Expand Down

0 comments on commit 0f44971

Please sign in to comment.