mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 18:13:18 +08:00
Fix memory context bug executing TRUNCATE
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 allocating the new relations list to the same memory context of the original list. Fixes #3580, fixes #3622, fixes #3182
This commit is contained in:
parent
0f64e11db4
commit
74ca546565
@ -7,8 +7,12 @@ accidentally triggering the load of a previous DB version.**
|
||||
## Unreleased
|
||||
|
||||
**Bugfixes**
|
||||
* #3580 Fix memory context bug executing TRUNCATE
|
||||
* #3654 Fix index attnum mapping in reorder_chunk
|
||||
|
||||
**Thanks**
|
||||
* @hardikm10, @DavidPavlicek and @pafiti for reporting bugs on TRUNCATE
|
||||
|
||||
## 2.4.2 (2021-09-21)
|
||||
|
||||
This release contains bug fixes since the 2.4.1 release.
|
||||
|
@ -913,6 +913,8 @@ process_truncate(ProcessUtilityArgs *args)
|
||||
ListCell *cell;
|
||||
List *hypertables = NIL;
|
||||
List *relations = NIL;
|
||||
bool list_changed = false;
|
||||
MemoryContext parsetreectx = GetMemoryChunkContext(args->parsetree);
|
||||
|
||||
/* 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.
|
||||
@ -922,8 +924,9 @@ process_truncate(ProcessUtilityArgs *args)
|
||||
{
|
||||
RangeVar *rv = lfirst(cell);
|
||||
Oid relid;
|
||||
bool list_append = false;
|
||||
|
||||
if (NULL == rv)
|
||||
if (!rv)
|
||||
continue;
|
||||
|
||||
/* Grab AccessExclusiveLock, same as regular TRUNCATE processing grabs
|
||||
@ -931,87 +934,113 @@ process_truncate(ProcessUtilityArgs *args)
|
||||
relid = RangeVarGetRelid(rv, AccessExclusiveLock, true);
|
||||
|
||||
if (!OidIsValid(relid))
|
||||
continue;
|
||||
|
||||
switch (get_rel_relkind(relid))
|
||||
{
|
||||
case RELKIND_VIEW:
|
||||
/* We should add invalid relations to the list to raise error on the
|
||||
* standard_ProcessUtility when we're trying to TRUNCATE a nonexistent relation */
|
||||
list_append = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (get_rel_relkind(relid))
|
||||
{
|
||||
ContinuousAgg *cagg = ts_continuous_agg_find_by_relid(relid);
|
||||
|
||||
if (NULL != cagg)
|
||||
case RELKIND_VIEW:
|
||||
{
|
||||
Hypertable *ht;
|
||||
ContinuousAgg *cagg = ts_continuous_agg_find_by_relid(relid);
|
||||
|
||||
if (!relation_should_recurse(rv))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("cannot truncate only a continuous aggregate")));
|
||||
if (cagg)
|
||||
{
|
||||
Hypertable *ht;
|
||||
MemoryContext oldctx;
|
||||
|
||||
ht = ts_hypertable_get_by_id(cagg->data.mat_hypertable_id);
|
||||
Assert(ht != NULL);
|
||||
rv = makeRangeVar(NameStr(ht->fd.schema_name), NameStr(ht->fd.table_name), -1);
|
||||
if (!relation_should_recurse(rv))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("cannot truncate only a continuous aggregate")));
|
||||
|
||||
/* Invalidate the entire continuous aggregate since it no
|
||||
* longer has any data */
|
||||
ts_cm_functions->continuous_agg_invalidate(ht, PG_INT64_MIN, PG_INT64_MAX);
|
||||
ht = ts_hypertable_get_by_id(cagg->data.mat_hypertable_id);
|
||||
Assert(ht != NULL);
|
||||
|
||||
/* Create list item into the same context of the list */
|
||||
oldctx = MemoryContextSwitchTo(parsetreectx);
|
||||
rv = makeRangeVar(NameStr(ht->fd.schema_name),
|
||||
NameStr(ht->fd.table_name),
|
||||
-1);
|
||||
MemoryContextSwitchTo(oldctx);
|
||||
|
||||
/* Invalidate the entire continuous aggregate since it no
|
||||
* longer has any data */
|
||||
ts_cm_functions->continuous_agg_invalidate(ht, PG_INT64_MIN, PG_INT64_MAX);
|
||||
|
||||
/* mark list as changed because we'll add the materialization hypertable */
|
||||
list_changed = true;
|
||||
}
|
||||
|
||||
list_append = true;
|
||||
break;
|
||||
}
|
||||
|
||||
relations = lappend(relations, rv);
|
||||
break;
|
||||
}
|
||||
case RELKIND_RELATION:
|
||||
{
|
||||
Hypertable *ht =
|
||||
ts_hypertable_cache_get_entry(hcache, relid, CACHE_FLAG_MISSING_OK);
|
||||
|
||||
if (ht == NULL)
|
||||
relations = lappend(relations, rv);
|
||||
else
|
||||
case RELKIND_RELATION:
|
||||
{
|
||||
ContinuousAggHypertableStatus agg_status;
|
||||
Hypertable *ht =
|
||||
ts_hypertable_cache_get_entry(hcache, relid, CACHE_FLAG_MISSING_OK);
|
||||
|
||||
agg_status = ts_continuous_agg_hypertable_status(ht->fd.id);
|
||||
if (!ht)
|
||||
list_append = true;
|
||||
else
|
||||
{
|
||||
ContinuousAggHypertableStatus agg_status;
|
||||
|
||||
ts_hypertable_permissions_check_by_id(ht->fd.id);
|
||||
agg_status = ts_continuous_agg_hypertable_status(ht->fd.id);
|
||||
|
||||
if ((agg_status & HypertableIsMaterialization) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot TRUNCATE a hypertable underlying a continuous "
|
||||
"aggregate"),
|
||||
errhint("TRUNCATE the continuous aggregate instead.")));
|
||||
ts_hypertable_permissions_check_by_id(ht->fd.id);
|
||||
|
||||
if (agg_status == HypertableIsRawTable)
|
||||
/* The truncation invalidates all associated continuous aggregates */
|
||||
ts_cm_functions->continuous_agg_invalidate(ht,
|
||||
TS_TIME_NOBEGIN,
|
||||
TS_TIME_NOEND);
|
||||
if ((agg_status & HypertableIsMaterialization) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot TRUNCATE a hypertable underlying a continuous "
|
||||
"aggregate"),
|
||||
errhint("TRUNCATE the continuous aggregate instead.")));
|
||||
|
||||
if (!relation_should_recurse(rv))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("cannot truncate only a hypertable"),
|
||||
errhint("Do not specify the ONLY keyword, or use truncate"
|
||||
" only on the chunks directly.")));
|
||||
if (agg_status == HypertableIsRawTable)
|
||||
/* The truncation invalidates all associated continuous aggregates */
|
||||
ts_cm_functions->continuous_agg_invalidate(ht,
|
||||
TS_TIME_NOBEGIN,
|
||||
TS_TIME_NOEND);
|
||||
|
||||
hypertables = lappend(hypertables, ht);
|
||||
if (!relation_should_recurse(rv))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("cannot truncate only a hypertable"),
|
||||
errhint("Do not specify the ONLY keyword, or use truncate"
|
||||
" only on the chunks directly.")));
|
||||
|
||||
if (!hypertable_is_distributed(ht))
|
||||
relations = lappend(relations, rv);
|
||||
hypertables = lappend(hypertables, ht);
|
||||
|
||||
if (!hypertable_is_distributed(ht))
|
||||
list_append = true;
|
||||
else
|
||||
/* mark list as changed because we'll not add the distributed hypertable
|
||||
*/
|
||||
list_changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
relations = lappend(relations, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Append the relation to the list in the same parse tree memory context */
|
||||
if (list_append)
|
||||
{
|
||||
MemoryContext oldctx = MemoryContextSwitchTo(parsetreectx);
|
||||
relations = lappend(relations, rv);
|
||||
MemoryContextSwitchTo(oldctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* Update relations list just when changed 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. */
|
||||
if (list_changed)
|
||||
stmt->relations = relations;
|
||||
|
||||
if (stmt->relations != NIL)
|
||||
{
|
||||
|
@ -152,6 +152,58 @@ SELECT * FROM truncate_normal;
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- fix for bug #3580
|
||||
\set ON_ERROR_STOP 0
|
||||
TRUNCATE nonexistentrelation;
|
||||
ERROR: relation "nonexistentrelation" does not exist
|
||||
\set ON_ERROR_STOP 1
|
||||
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
|
||||
----------------------------------------+------------
|
||||
@ -174,8 +226,8 @@ SELECT * FROM "two_Partitions";
|
||||
------------+-----------+----------+----------+----------+-------------
|
||||
(0 rows)
|
||||
|
||||
SELECT * FROM truncate_normal;
|
||||
color
|
||||
-------
|
||||
SELECT * FROM truncate_normal, truncate_nested;
|
||||
color | color
|
||||
-------+-------
|
||||
(0 rows)
|
||||
|
||||
|
@ -66,10 +66,47 @@ CREATE TABLE truncate_normal (color int);
|
||||
INSERT INTO truncate_normal VALUES (1);
|
||||
SELECT * FROM truncate_normal;
|
||||
|
||||
-- fix for bug #3580
|
||||
\set ON_ERROR_STOP 0
|
||||
TRUNCATE nonexistentrelation;
|
||||
\set ON_ERROR_STOP 1
|
||||
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;
|
||||
|
@ -552,7 +552,6 @@ _timescaledb_internal._dist_hyper_2_5_chunk| 4|f | 0|
|
||||
-- Clean up
|
||||
RESET ROLE;
|
||||
TRUNCATE disttable;
|
||||
TRUNCATE costtable;
|
||||
SELECT * FROM delete_data_node('data_node_1', force => true);
|
||||
WARNING: insufficient number of data nodes for distributed hypertable "disttable"
|
||||
NOTICE: the number of partitions in dimension "device" was decreased to 1
|
||||
|
@ -254,7 +254,6 @@ $$);
|
||||
-- Clean up
|
||||
RESET ROLE;
|
||||
TRUNCATE disttable;
|
||||
TRUNCATE costtable;
|
||||
SELECT * FROM delete_data_node('data_node_1', force => true);
|
||||
SELECT * FROM delete_data_node('data_node_2', force => true);
|
||||
DROP DATABASE :DN_DBNAME_1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user