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 Sep 30, 2021
1 parent 5b16ac0 commit 42f8b31
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 17 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 truncate_nested()
RETURNS trigger LANGUAGE plpgsql
AS $$
BEGIN
TRUNCATE truncate_nested;
RETURN NEW;
END;
$$;
CREATE TRIGGER truncate_nested
BEFORE TRUNCATE ON truncate_normal
FOR EACH STATEMENT EXECUTE FUNCTION 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 truncate_nested()
RETURNS trigger LANGUAGE plpgsql
AS $$
BEGIN
TRUNCATE truncate_nested;
RETURN NEW;
END;
$$;

CREATE TRIGGER truncate_nested
BEFORE TRUNCATE ON truncate_normal
FOR EACH STATEMENT EXECUTE FUNCTION 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;
11 changes: 6 additions & 5 deletions tsl/src/remote/dist_ddl.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,19 +471,20 @@ 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;

Assert(num_regular_tables <= list_length(stmt->relations));
Assert(num_regular_tables == list_length(stmt->relations));

/* We only support TRUNCATE on single distributed hypertables
* 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 42f8b31

Please sign in to comment.