Skip to content

Commit

Permalink
Remove empty chunks on TRUNCATE hypertable.
Browse files Browse the repository at this point in the history
  • Loading branch information
olofr committed Jul 26, 2017
1 parent 68a6cba commit f23bf58
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 5 deletions.
25 changes: 25 additions & 0 deletions sql/ddl_internal.sql
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,28 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.timescale_trigger_names()
$BODY$
SELECT array['_timescaledb_main_insert_trigger', '_timescaledb_main_after_insert_trigger'];
$BODY$;


CREATE OR REPLACE FUNCTION _timescaledb_internal.truncate_hypertable(
schema_name NAME,
table_name NAME
)
RETURNS VOID
LANGUAGE PLPGSQL VOLATILE
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
hypertable_row _timescaledb_catalog.hypertable;
BEGIN

SELECT * INTO STRICT hypertable_row
FROM _timescaledb_catalog.hypertable ht
WHERE ht.schema_name = truncate_hypertable.schema_name
AND ht.table_name = truncate_hypertable.table_name;

DELETE FROM _timescaledb_catalog.chunk
WHERE hypertable_id = hypertable_row.id;

END
$BODY$;
45 changes: 42 additions & 3 deletions src/metadata_queries.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,23 @@ prepare_plan(const char *src, int nargs, Oid *argtypes)
#define CHUNK_CREATE_ARGS (Oid[]) {INT4ARRAYOID, INT8ARRAYOID}
#define CHUNK_CREATE "SELECT * FROM _timescaledb_internal.chunk_create($1, $2)"

/* plan for creating a chunk via create_chunk(). */
DEFINE_PLAN(create_chunk_plan, CHUNK_CREATE, 2, CHUNK_CREATE_ARGS)

/* old_schema, old_name, new_schema, new_name */
#define RENAME_HYPERTABLE_ARGS (Oid[]) {NAMEOID, NAMEOID, TEXTOID, TEXTOID}
#define RENAME_HYPERTABLE "SELECT * FROM _timescaledb_internal.rename_hypertable($1, $2, $3, $4)"

/* plan for creating a chunk via create_chunk(). */
DEFINE_PLAN(create_chunk_plan, CHUNK_CREATE, 2, CHUNK_CREATE_ARGS)

/* plan to rename hypertable */
DEFINE_PLAN(rename_hypertable_plan, RENAME_HYPERTABLE, 4, RENAME_HYPERTABLE_ARGS)

/* schema, name */
#define TRUNCATE_HYPERTABLE_ARGS (Oid[]) {NAMEOID, NAMEOID}
#define TRUNCATE_HYPERTABLE "SELECT * FROM _timescaledb_internal.truncate_hypertable($1, $2)"

/* plan to truncate hypertable */
DEFINE_PLAN(truncate_hypertable_plan, TRUNCATE_HYPERTABLE, 2, TRUNCATE_HYPERTABLE_ARGS)

static HeapTuple
chunk_tuple_create_spi_connected(Hyperspace *hs, Point *p, SPIPlanPtr plan)
{
Expand Down Expand Up @@ -158,3 +165,35 @@ spi_hypertable_rename(Hypertable *ht, char *new_schema_name, char *new_table_nam

return;
}

static void
hypertable_truncate_spi_connected(Hypertable *ht, SPIPlanPtr plan)
{
int ret;
Datum args[2];

args[0] = PointerGetDatum(NameStr(ht->fd.schema_name));
args[1] = PointerGetDatum(NameStr(ht->fd.table_name));

ret = SPI_execute_plan(plan, args, NULL, false, 2);

if (ret <= 0)
elog(ERROR, "Got an SPI error %d", ret);

return;
}

void
spi_hypertable_truncate(Hypertable *ht)
{
SPIPlanPtr plan = truncate_hypertable_plan();

if (SPI_connect() < 0)
elog(ERROR, "Got an SPI connect error");

hypertable_truncate_spi_connected(ht, plan);

SPI_finish();

return;
}
1 change: 1 addition & 0 deletions src/metadata_queries.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ typedef struct Hypertable Hypertable;

extern Chunk *spi_chunk_create(Hyperspace *hs, Point *p);
extern void spi_hypertable_rename(Hypertable *ht, char *new_schema_name, char *new_table_name);
extern void spi_hypertable_truncate(Hypertable *ht);

#endif /* TIMESCALEDB_METADATA_QUERIES_H */
27 changes: 27 additions & 0 deletions src/process_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,33 @@ timescaledb_ProcessUtility(Node *parsetree,
return;
}

/* Truncate a hypertable */
if (IsA(parsetree, TruncateStmt)) {
TruncateStmt *truncatestmt = (TruncateStmt *) parsetree;
ListCell *cell;

foreach(cell, truncatestmt->relations)
{
Oid relId = RangeVarGetRelid(lfirst(cell), NoLock, true);

if (OidIsValid(relId))
{
Cache *hcache = hypertable_cache_pin();
Hypertable *hentry = hypertable_cache_get_entry(hcache, relId);

if (hentry != NULL) {
executor_level_enter();
spi_hypertable_truncate(hentry);
executor_level_exit();
}
cache_release(hcache);
}

}

prev_ProcessUtility((Node *) truncatestmt, query_string, context, params, dest, completion_tag);
return;
}

/* Change the schema of hypertable */
if (IsA(parsetree, AlterObjectSchemaStmt))
Expand Down
4 changes: 2 additions & 2 deletions test/expected/pg_dump.out
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
126
127
(1 row)

\c postgres
Expand All @@ -66,7 +66,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
126
127
(1 row)

\c single
Expand Down
129 changes: 129 additions & 0 deletions test/expected/truncate_hypertable.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
\o /dev/null
\ir include/insert_two_partitions.sql
\ir create_single_db.sql
SET client_min_messages = WARNING;
DROP DATABASE IF EXISTS single;
SET client_min_messages = NOTICE;
CREATE DATABASE single;
\c single
CREATE EXTENSION IF NOT EXISTS timescaledb;
\c single
CREATE TABLE PUBLIC."two_Partitions" (
"timeCustom" BIGINT NOT NULL,
device_id TEXT NOT NULL,
series_0 DOUBLE PRECISION NULL,
series_1 DOUBLE PRECISION NULL,
series_2 DOUBLE PRECISION NULL,
series_bool BOOLEAN NULL
);
CREATE INDEX ON PUBLIC."two_Partitions" (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL;
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, series_0) WHERE series_0 IS NOT NULL;
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL;
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL;
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL;
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, device_id);
SELECT * FROM create_hypertable('"public"."two_Partitions"'::regclass, 'timeCustom'::name, 'device_id'::name, associated_schema_name=>'_timescaledb_internal'::text, number_partitions => 2);
\set QUIET off
BEGIN;
\COPY public."two_Partitions" FROM 'data/ds1_dev1_1.tsv' NULL AS '';
COMMIT;
INSERT INTO public."two_Partitions"("timeCustom", device_id, series_0, series_1) VALUES
(1257987600000000000, 'dev1', 1.5, 1),
(1257987600000000000, 'dev1', 1.5, 2),
(1257894000000000000, 'dev2', 1.5, 1),
(1257894002000000000, 'dev1', 2.5, 3);
INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1) VALUES
(1257894000000000000, 'dev2', 1.5, 2);
\set QUIET on
\o
SELECT * FROM _timescaledb_catalog.hypertable;
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+----------------+------------------------+-------------------------+----------------
1 | public | two_Partitions | _timescaledb_internal | _hyper_1 | 2
(1 row)

SELECT * FROM _timescaledb_catalog.chunk;
id | hypertable_id | schema_name | table_name
----+---------------+-----------------------+------------------
1 | 1 | _timescaledb_internal | _hyper_1_1_chunk
2 | 1 | _timescaledb_internal | _hyper_1_2_chunk
3 | 1 | _timescaledb_internal | _hyper_1_3_chunk
4 | 1 | _timescaledb_internal | _hyper_1_4_chunk
(4 rows)

\dt "_timescaledb_internal".*
List of relations
Schema | Name | Type | Owner
-----------------------+------------------+-------+----------
_timescaledb_internal | _hyper_1_1_chunk | table | postgres
_timescaledb_internal | _hyper_1_2_chunk | table | postgres
_timescaledb_internal | _hyper_1_3_chunk | table | postgres
_timescaledb_internal | _hyper_1_4_chunk | table | postgres
(4 rows)

SELECT * FROM "two_Partitions";
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
---------------------+-----------+----------+----------+----------+-------------
1257894000000000000 | dev1 | 1.5 | 1 | 2 | t
1257894000000000000 | dev1 | 1.5 | 2 | |
1257894000000001000 | dev1 | 2.5 | 3 | |
1257894001000000000 | dev1 | 3.5 | 4 | |
1257894002000000000 | dev1 | 5.5 | 6 | | t
1257894002000000000 | dev1 | 5.5 | 7 | | f
1257894002000000000 | dev1 | 2.5 | 3 | |
1257897600000000000 | dev1 | 4.5 | 5 | | f
1257987600000000000 | dev1 | 1.5 | 1 | |
1257987600000000000 | dev1 | 1.5 | 2 | |
1257894000000000000 | dev2 | 1.5 | 1 | |
1257894000000000000 | dev2 | 1.5 | 2 | |
(12 rows)

SET client_min_messages = WARNING;
TRUNCATE "two_Partitions";
SELECT * FROM _timescaledb_catalog.hypertable;
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+----------------+------------------------+-------------------------+----------------
1 | public | two_Partitions | _timescaledb_internal | _hyper_1 | 2
(1 row)

SELECT * FROM _timescaledb_catalog.chunk;
id | hypertable_id | schema_name | table_name
----+---------------+-------------+------------
(0 rows)

-- should be empty
\set ON_ERROR_STOP 0
\dt "_timescaledb_internal".*
List of relations
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)

\set ON_ERROR_STOP 1
\d+ "two_Partitions"
Table "public.two_Partitions"
Column | Type | Modifiers | Storage | Stats target | Description
-------------+------------------+-----------+----------+--------------+-------------
timeCustom | bigint | not null | plain | |
device_id | text | not null | extended | |
series_0 | double precision | | plain | |
series_1 | double precision | | plain | |
series_2 | double precision | | plain | |
series_bool | boolean | | plain | |
Indexes:
"two_Partitions_device_id_timeCustom_idx" btree (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL
"two_Partitions_timeCustom_device_id_idx" btree ("timeCustom" DESC NULLS LAST, device_id)
"two_Partitions_timeCustom_idx" btree ("timeCustom" DESC)
"two_Partitions_timeCustom_series_0_idx" btree ("timeCustom" DESC NULLS LAST, series_0) WHERE series_0 IS NOT NULL
"two_Partitions_timeCustom_series_1_idx" btree ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL
"two_Partitions_timeCustom_series_2_idx" btree ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL
"two_Partitions_timeCustom_series_bool_idx" btree ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL
Triggers:
_timescaledb_main_after_insert_trigger AFTER INSERT ON "two_Partitions" FOR EACH STATEMENT EXECUTE PROCEDURE _timescaledb_internal.main_table_after_insert_trigger()
_timescaledb_main_insert_trigger BEFORE INSERT ON "two_Partitions" FOR EACH ROW EXECUTE PROCEDURE _timescaledb_internal.main_table_insert_trigger()

SELECT * FROM "two_Partitions";
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
------------+-----------+----------+----------+----------+-------------
(0 rows)

24 changes: 24 additions & 0 deletions test/sql/truncate_hypertable.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
\o /dev/null
\ir include/insert_two_partitions.sql
\o

SELECT * FROM _timescaledb_catalog.hypertable;
SELECT * FROM _timescaledb_catalog.chunk;
\dt "_timescaledb_internal".*
SELECT * FROM "two_Partitions";

SET client_min_messages = WARNING;
TRUNCATE "two_Partitions";

SELECT * FROM _timescaledb_catalog.hypertable;
SELECT * FROM _timescaledb_catalog.chunk;

-- should be empty
\set ON_ERROR_STOP 0
\dt "_timescaledb_internal".*
\set ON_ERROR_STOP 1

\d+ "two_Partitions"
SELECT * FROM "two_Partitions";


0 comments on commit f23bf58

Please sign in to comment.