Skip to content

Commit 6adce4c

Browse files
committed
Handle TRUNCATE without upcall and handle ONLY modifier
This change refactors the handling of TRUNCATE so that it is performed directly in process utility without doing an upcall to PL/pgSQL. It also adds handling for the ONLY modifier to TRUNCATE, which shouldn't work on a hypertable. TRUNCATE now generates an error if TRUNCATE ONLY is used on a hypertable.
1 parent b7ebe06 commit 6adce4c

File tree

9 files changed

+130
-92
lines changed

9 files changed

+130
-92
lines changed

sql/ddl_internal.sql

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,6 @@ BEGIN
112112
END
113113
$BODY$;
114114

115-
116-
CREATE OR REPLACE FUNCTION _timescaledb_internal.truncate_hypertable(
117-
schema_name NAME,
118-
table_name NAME,
119-
cascade BOOLEAN = FALSE
120-
)
121-
RETURNS VOID
122-
LANGUAGE PLPGSQL VOLATILE
123-
SET search_path = '_timescaledb_internal'
124-
AS
125-
$BODY$
126-
DECLARE
127-
hypertable_row _timescaledb_catalog.hypertable;
128-
chunk_row _timescaledb_catalog.chunk;
129-
BEGIN
130-
--TODO: should this cascade?
131-
PERFORM _timescaledb_internal.drop_chunks_impl(NULL, table_name, schema_name, cascade, true);
132-
END
133-
$BODY$;
134-
135115
--documentation of these function located in chunk_index.h
136116
CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_index_clone(chunk_index_oid OID) RETURNS OID
137117
AS '@MODULE_PATHNAME@', 'chunk_index_clone' LANGUAGE C VOLATILE STRICT;

sql/updates/pre-0.8.0--0.9.0-dev.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ DROP FUNCTION _timescaledb_internal.verify_hypertable_indexes(regclass);
3939
DROP FUNCTION _timescaledb_internal.validate_triggers(regclass);
4040
DROP FUNCTION _timescaledb_internal.chunk_create_table(int, name);
4141
DROP FUNCTION _timescaledb_internal.ddl_change_owner(oid, name);
42+
DROP FUNCTION _timescaledb_internal.truncate_hypertable(name,name,boolean);
4243

4344
-- Remove redundant index
4445
DROP INDEX _timescaledb_catalog.dimension_slice_dimension_id_range_start_range_end_idx;

src/catalog.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,6 @@ const static InternalFunctionDef internal_function_definitions[_MAX_INTERNAL_FUN
110110
[DDL_ADD_CHUNK_CONSTRAINT] = {
111111
.name = "chunk_constraint_add_table_constraint",
112112
.args = 1,
113-
},
114-
[TRUNCATE_HYPERTABLE] = {
115-
.name = "truncate_hypertable",
116-
.args = 3
117113
}
118114
};
119115

src/catalog.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ typedef enum CatalogTable
4747
typedef enum InternalFunction
4848
{
4949
DDL_ADD_CHUNK_CONSTRAINT,
50-
TRUNCATE_HYPERTABLE,
5150
_MAX_INTERNAL_FUNCTIONS,
5251
} InternalFunction;
5352

src/process_utility.c

Lines changed: 88 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <utils/lsyscache.h>
2323
#include <utils/syscache.h>
2424
#include <utils/builtins.h>
25+
#include <utils/guc.h>
2526
#include <utils/snapmgr.h>
2627
#include <parser/parse_utilcmd.h>
2728

@@ -50,14 +51,6 @@ static ProcessUtility_hook_type prev_ProcessUtility_hook;
5051

5152
static bool expect_chunk_modification = false;
5253

53-
/* Macros for DDL upcalls to PL/pgSQL */
54-
55-
#define process_truncate_hypertable(hypertable, cascade) \
56-
CatalogInternalCall3(TRUNCATE_HYPERTABLE, \
57-
NameGetDatum(&(hypertable)->fd.schema_name), \
58-
NameGetDatum(&(hypertable)->fd.table_name), \
59-
BoolGetDatum(cascade))
60-
6154
typedef struct ProcessUtilityArgs
6255
{
6356
#if PG10
@@ -147,37 +140,6 @@ relation_not_only(RangeVar *rv)
147140
errmsg("ONLY option not supported on hypertable operations")));
148141
}
149142

150-
/* Truncate a hypertable */
151-
static void
152-
process_truncate(Node *parsetree)
153-
{
154-
TruncateStmt *truncatestmt = (TruncateStmt *) parsetree;
155-
Cache *hcache = hypertable_cache_pin();
156-
ListCell *cell;
157-
158-
foreach(cell, truncatestmt->relations)
159-
{
160-
RangeVar *relation = lfirst(cell);
161-
Oid relid;
162-
163-
if (NULL == relation)
164-
continue;
165-
166-
relid = RangeVarGetRelid(relation, NoLock, true);
167-
168-
if (OidIsValid(relid))
169-
{
170-
Hypertable *ht = hypertable_cache_get_entry(hcache, relid);
171-
172-
if (ht != NULL)
173-
{
174-
process_truncate_hypertable(ht, truncatestmt->behavior == DROP_CASCADE);
175-
}
176-
}
177-
}
178-
cache_release(hcache);
179-
}
180-
181143
/* Change the schema of a hypertable */
182144
static void
183145
process_alterobjectschema(Node *parsetree)
@@ -358,6 +320,77 @@ process_vacuum(Node *parsetree, ProcessUtilityContext context)
358320
return true;
359321
}
360322

323+
static void
324+
process_truncate_chunk(Hypertable *ht, Oid chunk_relid, void *arg)
325+
{
326+
TruncateStmt *stmt = arg;
327+
ObjectAddress objaddr = {
328+
.classId = RelationRelationId,
329+
.objectId = chunk_relid,
330+
};
331+
332+
performDeletion(&objaddr, stmt->behavior, 0);
333+
}
334+
335+
#if PG10
336+
#define TRUNCATE_RECURSE(rv) \
337+
(rv)->inh
338+
#elif PG96
339+
#define TRUNCATE_RECURSE(rv) \
340+
((rv)->inhOpt == INH_DEFAULT ? SQL_inheritance : ((rv)->inhOpt == INH_YES))
341+
#endif
342+
343+
/*
344+
* Truncate a hypertable.
345+
*/
346+
static bool
347+
process_truncate(ProcessUtilityArgs *args)
348+
{
349+
TruncateStmt *stmt = (TruncateStmt *) args->parsetree;
350+
Cache *hcache = hypertable_cache_pin();
351+
ListCell *cell;
352+
353+
/* Call standard process utility first to truncate all tables */
354+
prev_ProcessUtility(args);
355+
356+
/* For all hypertables, we drop the now empty chunks */
357+
foreach(cell, stmt->relations)
358+
{
359+
RangeVar *rv = lfirst(cell);
360+
Oid relid;
361+
362+
if (NULL == rv)
363+
continue;
364+
365+
relid = RangeVarGetRelid(rv, NoLock, true);
366+
367+
if (OidIsValid(relid))
368+
{
369+
Hypertable *ht = hypertable_cache_get_entry(hcache, relid);
370+
371+
if (ht != NULL)
372+
{
373+
if (!TRUNCATE_RECURSE(rv))
374+
ereport(ERROR,
375+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
376+
errmsg("cannot truncate only a hypertable"),
377+
errhint("Do not specify the ONLY keyword, or use truncate"
378+
" only on the chunks directly.")));
379+
380+
/* Delete the metadata */
381+
chunk_delete_by_hypertable_id(ht->fd.id);
382+
383+
/* Drop the chunk tables */
384+
foreach_chunk(ht, process_truncate_chunk, stmt);
385+
}
386+
}
387+
}
388+
389+
cache_release(hcache);
390+
391+
return true;
392+
}
393+
361394
static void
362395
process_drop_table_chunk(Hypertable *ht, Oid chunk_relid, void *arg)
363396
{
@@ -1628,32 +1661,29 @@ process_create_trigger_end(Node *parsetree)
16281661
* Handle DDL commands before they have been processed by PostgreSQL.
16291662
*/
16301663
static bool
1631-
process_ddl_command_start(Node *parsetree,
1632-
const char *query_string,
1633-
ProcessUtilityContext context,
1634-
char *completion_tag)
1664+
process_ddl_command_start(ProcessUtilityArgs *args)
16351665
{
16361666
bool handled = false;
16371667

1638-
switch (nodeTag(parsetree))
1668+
switch (nodeTag(args->parsetree))
16391669
{
1640-
case T_TruncateStmt:
1641-
process_truncate(parsetree);
1642-
break;
16431670
case T_AlterObjectSchemaStmt:
1644-
process_alterobjectschema(parsetree);
1671+
process_alterobjectschema(args->parsetree);
1672+
break;
1673+
case T_TruncateStmt:
1674+
handled = process_truncate(args);
16451675
break;
16461676
case T_AlterTableStmt:
1647-
process_altertable_start(parsetree);
1677+
process_altertable_start(args->parsetree);
16481678
break;
16491679
case T_RenameStmt:
1650-
process_rename(parsetree);
1680+
process_rename(args->parsetree);
16511681
break;
16521682
case T_IndexStmt:
1653-
process_index_start(parsetree);
1683+
process_index_start(args->parsetree);
16541684
break;
16551685
case T_CreateTrigStmt:
1656-
process_create_trigger_start(parsetree);
1686+
process_create_trigger_start(args->parsetree);
16571687
break;
16581688
case T_DropStmt:
16591689

@@ -1663,19 +1693,19 @@ process_ddl_command_start(Node *parsetree,
16631693
* table is dropped, the drop respects CASCADE in the expected
16641694
* way.
16651695
*/
1666-
process_drop(parsetree);
1696+
process_drop(args->parsetree);
16671697
break;
16681698
case T_CopyStmt:
1669-
handled = process_copy(parsetree, query_string, completion_tag);
1699+
handled = process_copy(args->parsetree, args->query_string, args->completion_tag);
16701700
break;
16711701
case T_VacuumStmt:
1672-
handled = process_vacuum(parsetree, context);
1702+
handled = process_vacuum(args->parsetree, args->context);
16731703
break;
16741704
case T_ReindexStmt:
1675-
handled = process_reindex(parsetree);
1705+
handled = process_reindex(args->parsetree);
16761706
break;
16771707
case T_ClusterStmt:
1678-
handled = process_cluster_start(parsetree, context);
1708+
handled = process_cluster_start(args->parsetree, args->context);
16791709
break;
16801710
default:
16811711
break;
@@ -1762,10 +1792,7 @@ timescaledb_ddl_command_start(
17621792
return;
17631793
}
17641794

1765-
if (!process_ddl_command_start(args.parsetree,
1766-
args.query_string,
1767-
args.context,
1768-
args.completion_tag))
1795+
if (!process_ddl_command_start(&args))
17691796
prev_ProcessUtility(&args);
17701797
}
17711798

test/expected/pg_dump.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ SELECT count(*)
4949
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
5050
count
5151
-------
52-
95
52+
94
5353
(1 row)
5454

5555
SELECT * FROM test.show_columns('public."two_Partitions"');
@@ -235,7 +235,7 @@ SELECT count(*)
235235
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
236236
count
237237
-------
238-
95
238+
94
239239
(1 row)
240240

241241
--main table and chunk schemas should be the same

test/expected/truncate_hypertable.out renamed to test/expected/truncate.out

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,29 @@ CREATE TRIGGER _test_truncate_after
133133
TRUNCATE "two_Partitions";
134134
WARNING: FIRING trigger when: BEFORE level: STATEMENT op: TRUNCATE cnt: <NULL> trigger_name _test_truncate_before
135135
WARNING: FIRING trigger when: AFTER level: STATEMENT op: TRUNCATE cnt: <NULL> trigger_name _test_truncate_after
136-
ERROR: cannot drop table _hyper_1_5_chunk because other objects depend on it
136+
ERROR: cannot drop table _timescaledb_internal._hyper_1_5_chunk because other objects depend on it
137+
-- cannot TRUNCATE ONLY a hypertable
138+
TRUNCATE ONLY "two_Partitions" CASCADE;
139+
ERROR: cannot truncate only a hypertable
137140
\set ON_ERROR_STOP 1
138-
TRUNCATE "two_Partitions" CASCADE;
141+
-- create a regular table to make sure we can truncate it in the same call
142+
CREATE TABLE truncate_normal (color int);
143+
INSERT INTO truncate_normal VALUES (1);
144+
SELECT * FROM truncate_normal;
145+
color
146+
-------
147+
1
148+
(1 row)
149+
150+
SELECT * FROM test.show_subtables('"two_Partitions"');
151+
Child | Tablespace
152+
----------------------------------------+------------
153+
_timescaledb_internal._hyper_1_5_chunk |
154+
_timescaledb_internal._hyper_1_6_chunk |
155+
_timescaledb_internal._hyper_1_7_chunk |
156+
(3 rows)
157+
158+
TRUNCATE "two_Partitions", truncate_normal CASCADE;
139159
WARNING: FIRING trigger when: BEFORE level: STATEMENT op: TRUNCATE cnt: <NULL> trigger_name _test_truncate_before
140160
WARNING: FIRING trigger when: AFTER level: STATEMENT op: TRUNCATE cnt: <NULL> trigger_name _test_truncate_after
141161
-- should be empty
@@ -149,3 +169,8 @@ SELECT * FROM "two_Partitions";
149169
------------+-----------+----------+----------+----------+-------------
150170
(0 rows)
151171

172+
SELECT * FROM truncate_normal;
173+
color
174+
-------
175+
(0 rows)
176+

test/sql/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ set(TEST_FILES
4242
tablespace.sql
4343
timestamp.sql
4444
triggers.sql
45-
truncate_hypertable.sql
45+
truncate.sql
4646
update.sql
4747
upsert.sql
4848
util.sql

test/sql/truncate_hypertable.sql renamed to test/sql/truncate.sql

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,19 @@ CREATE TRIGGER _test_truncate_after
5353

5454
\set ON_ERROR_STOP 0
5555
TRUNCATE "two_Partitions";
56+
-- cannot TRUNCATE ONLY a hypertable
57+
TRUNCATE ONLY "two_Partitions" CASCADE;
5658
\set ON_ERROR_STOP 1
5759

58-
TRUNCATE "two_Partitions" CASCADE;
60+
-- create a regular table to make sure we can truncate it in the same call
61+
CREATE TABLE truncate_normal (color int);
62+
INSERT INTO truncate_normal VALUES (1);
63+
SELECT * FROM truncate_normal;
64+
65+
SELECT * FROM test.show_subtables('"two_Partitions"');
66+
67+
TRUNCATE "two_Partitions", truncate_normal CASCADE;
5968
-- should be empty
6069
SELECT * FROM test.show_subtables('"two_Partitions"');
6170
SELECT * FROM "two_Partitions";
71+
SELECT * FROM truncate_normal;

0 commit comments

Comments
 (0)