1
0
mirror of https://github.com/timescale/timescaledb.git synced 2025-05-16 02:23:49 +08:00

Add support to rename and change schema on hypertable.

This commit is contained in:
Olof Rensfelt 2017-07-06 13:15:48 +02:00 committed by Olof Rensfelt
parent 4888bd8635
commit a5725d9875
10 changed files with 242 additions and 43 deletions

@ -11,6 +11,8 @@ BEGIN
ELSIF TG_OP = 'DELETE' THEN
EXECUTE format('DROP INDEX IF EXISTS %I.%I', OLD.schema_name, OLD.index_name);
RETURN OLD;
ELSIF TG_OP = 'UPDATE' THEN
RETURN NEW;
END IF;
PERFORM _timescaledb_internal.on_trigger_error(TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME);

@ -358,3 +358,31 @@ BEGIN
END IF;
END
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.rename_hypertable(
old_schema NAME,
old_table_name NAME,
new_schema TEXT,
new_table_name TEXT
)
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
WHERE schema_name = old_schema AND table_name = old_table_name;
UPDATE _timescaledb_catalog.hypertable SET
schema_name = new_schema,
table_name = new_table_name
WHERE
schema_name = old_schema AND
table_name = old_table_name;
END
$BODY$;

@ -38,7 +38,6 @@ DECLARE
hypertable_row _timescaledb_catalog.hypertable;
BEGIN
IF TG_OP = 'UPDATE' THEN
PERFORM _timescaledb_internal.on_trigger_error(TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME);
RETURN NEW;
ELSIF TG_OP = 'INSERT' THEN
-- create index on all chunks

@ -43,6 +43,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable (
associated_schema_name NAME NOT NULL,
associated_table_prefix NAME NOT NULL,
num_dimensions SMALLINT NOT NULL CHECK (num_dimensions > 0),
UNIQUE (id, schema_name), -- So hypertable_index can use it as foreign key
UNIQUE (schema_name, table_name),
UNIQUE (associated_schema_name, associated_table_prefix)
);
@ -127,10 +128,11 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constrain
-- Represents an index on the hypertable
CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_index (
hypertable_id INTEGER NOT NULL REFERENCES _timescaledb_catalog.hypertable(id) ON DELETE CASCADE,
hypertable_id INTEGER NOT NULL,
main_schema_name NAME NOT NULL, -- schema name of main table (needed for a uniqueness constraint)
main_index_name NAME NOT NULL, -- index name on main table
definition TEXT NOT NULL, -- def with /*INDEX_NAME*/ and /*TABLE_NAME*/ placeholders
FOREIGN KEY (hypertable_id, main_schema_name) REFERENCES _timescaledb_catalog.hypertable(id, schema_name) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (hypertable_id, main_index_name),
UNIQUE(main_schema_name, main_index_name) -- globally unique since index names globally unique
);
@ -147,7 +149,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_index (
definition TEXT NOT NULL,
UNIQUE (schema_name, table_name, index_name),
FOREIGN KEY (schema_name, table_name) REFERENCES _timescaledb_catalog.chunk (schema_name, table_name) ON DELETE CASCADE,
FOREIGN KEY (main_schema_name, main_index_name) REFERENCES _timescaledb_catalog.hypertable_index (main_schema_name, main_index_name) ON DELETE CASCADE
FOREIGN KEY (main_schema_name, main_index_name) REFERENCES _timescaledb_catalog.hypertable_index (main_schema_name, main_index_name) ON DELETE CASCADE ON UPDATE CASCADE
);
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_index', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_timescaledb_catalog.chunk_index','id'), '');

@ -2,10 +2,12 @@
#include <catalog/pg_type.h>
#include <commands/trigger.h>
#include <executor/spi.h>
#include <utils/builtins.h>
#include "metadata_queries.h"
#include "chunk.h"
#include "dimension.h"
#include "hypertable.h"
/* Utility function to prepare an SPI plan */
static SPIPlanPtr
@ -47,9 +49,16 @@ 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)"
/* 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)
static HeapTuple
chunk_tuple_create_spi_connected(Hyperspace *hs, Point *p, SPIPlanPtr plan)
{
@ -104,3 +113,48 @@ spi_chunk_create(Hyperspace *hs, Point *p)
return chunk;
}
static void
hypertable_rename_spi_connected(Hypertable *ht, char *new_schema_name, char *new_table_name, SPIPlanPtr plan)
{
int ret;
Datum args[4];
args[0] = PointerGetDatum(NameStr(ht->fd.schema_name));
args[1] = PointerGetDatum(NameStr(ht->fd.table_name));
args[2] = CStringGetTextDatum(new_schema_name);
args[3] = CStringGetTextDatum(new_table_name);
ret = SPI_execute_plan(plan, args, NULL, false, 4);
if (ret <= 0)
elog(ERROR, "Got an SPI error %d", ret);
return;
}
void
spi_hypertable_rename(Hypertable *ht, char *new_schema_name, char *new_table_name)
{
SPIPlanPtr plan = rename_hypertable_plan();
if (strlen(new_schema_name) > sizeof(NameData) - 1) {
elog(ERROR, "New schema name '%s' is too long", new_schema_name);
}
if (strlen(new_table_name) > sizeof(NameData) - 1) {
elog(ERROR, "New schema name '%s' is too long", new_table_name);
}
if (SPI_connect() < 0)
elog(ERROR, "Got an SPI connect error");
hypertable_rename_spi_connected(ht, new_schema_name, new_table_name, plan);
SPI_finish();
return;
}

@ -6,7 +6,9 @@
typedef struct Chunk Chunk;
typedef struct Hyperspace Hyperspace;
typedef struct Point Point;
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);
#endif /* TIMESCALEDB_METADATA_QUERIES_H */

@ -8,6 +8,7 @@
#include "hypertable_cache.h"
#include "extension.h"
#include "executor.h"
#include "metadata_queries.h"
void _process_utility_init(void);
void _process_utility_fini(void);
@ -35,7 +36,8 @@ prev_ProcessUtility(Node *parsetree,
}
}
/* Hook-intercept for ProcessUtility. */
/* Hook-intercept for ProcessUtility. Used to set COPY completion tag and */
/* renaming of hypertables. */
static void
timescaledb_ProcessUtility(Node *parsetree,
const char *query_string,
@ -50,7 +52,31 @@ timescaledb_ProcessUtility(Node *parsetree,
return;
}
/* We don't support renaming hypertables yet so we need to block it */
/* Change the schema of hypertable */
if (IsA(parsetree, AlterObjectSchemaStmt))
{
AlterObjectSchemaStmt *alterstmt = (AlterObjectSchemaStmt *) parsetree;
Oid relId = RangeVarGetRelid(alterstmt->relation, NoLock, true);
if (OidIsValid(relId))
{
Cache *hcache = hypertable_cache_pin();
Hypertable *hentry = hypertable_cache_get_entry(hcache, relId);
if (hentry != NULL && alterstmt->objectType == OBJECT_TABLE) {
executor_level_enter();
spi_hypertable_rename(hentry,
alterstmt->newschema,
NameStr(hentry->fd.table_name));
executor_level_exit();
}
cache_release(hcache);
}
prev_ProcessUtility((Node *) alterstmt, query_string, context, params, dest, completion_tag);
return;
}
/* Rename hypertable */
if (IsA(parsetree, RenameStmt))
{
RenameStmt *renamestmt = (RenameStmt *) parsetree;
@ -61,15 +87,19 @@ timescaledb_ProcessUtility(Node *parsetree,
Cache *hcache = hypertable_cache_pin();
Hypertable *hentry = hypertable_cache_get_entry(hcache, relid);
if (hentry != NULL && renamestmt->renameType == OBJECT_TABLE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Renaming hypertables is not yet supported")));
if (hentry != NULL && renamestmt->renameType == OBJECT_TABLE) {
executor_level_enter();
spi_hypertable_rename(hentry,
NameStr(hentry->fd.schema_name),
renamestmt->newname);
executor_level_exit();
}
cache_release(hcache);
}
prev_ProcessUtility((Node *) renamestmt, query_string, context, params, dest, completion_tag);
return;
}
if (IsA(parsetree, CopyStmt))
{
/*

@ -317,32 +317,97 @@ Check constraints:
"constraint_8" CHECK (_timescaledb_internal.get_partition_for_key(device_id) >= 0 AND _timescaledb_internal.get_partition_for_key(device_id) < 1073741823)
Inherits: "two_Partitions"
-- Test that renaming hypertable is blocked
\set VERBOSITY default
\set ON_ERROR_STOP 0
-- Test that renaming hypertable works
\d _timescaledb_internal._hyper_1_1_chunk
Table "_timescaledb_internal._hyper_1_1_chunk"
Column | Type | Modifiers
-------------+------------------+-----------
timeCustom | bigint | not null
device_id | text | not null
series_0 | double precision |
series_1 | double precision |
series_2 | double precision |
series_bool | boolean |
Indexes:
"1-two_Partitions_device_id_timeCustom_idx" btree (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL
"2-two_Partitions_timeCustom_series_0_idx" btree ("timeCustom" DESC NULLS LAST, series_0) WHERE series_0 IS NOT NULL
"3-two_Partitions_timeCustom_series_1_idx" btree ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL
"4-two_Partitions_timeCustom_series_2_idx" btree ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL
"5-two_Partitions_timeCustom_series_bool_idx" btree ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL
"6-two_Partitions_timeCustom_device_id_idx" btree ("timeCustom" DESC NULLS LAST, device_id)
"7-two_Partitions_timeCustom_idx" btree ("timeCustom" DESC)
Check constraints:
"constraint_1" CHECK ("timeCustom" >= '1257892416000000000'::bigint AND "timeCustom" < '1257895008000000000'::bigint)
"constraint_2" CHECK (_timescaledb_internal.get_partition_for_key(device_id) >= 1073741823 AND _timescaledb_internal.get_partition_for_key(device_id) < 2147483647)
Inherits: "two_Partitions"
ALTER TABLE "two_Partitions" RENAME TO "newname";
ERROR: Renaming hypertables is not yet supported
\set ON_ERROR_STOP 1
-- Test that renaming ordinary table works
CREATE TABLE renametable (foo int);
ALTER TABLE "renametable" RENAME TO "newname";
SELECT * FROM "newname";
foo
-----
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)
SELECT * FROM _timescaledb_catalog.hypertable;
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+------------+------------------------+-------------------------+----------------
1 | public | newname | _timescaledb_internal | _hyper_1 | 2
(1 row)
CREATE SCHEMA "newschema";
ALTER TABLE "newname" SET SCHEMA "newschema";
SELECT * FROM "newschema"."newname";
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)
SELECT * FROM _timescaledb_catalog.hypertable;
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+------------+------------------------+-------------------------+----------------
1 | newschema | newname | _timescaledb_internal | _hyper_1 | 2
(1 row)
SELECT * FROM _timescaledb_catalog.hypertable_index WHERE main_schema_name <> 'newschema';
hypertable_id | main_schema_name | main_index_name | definition
---------------+------------------+-----------------+------------
(0 rows)
SELECT * FROM _timescaledb_catalog.chunk_index WHERE main_schema_name <> 'newschema';
id | schema_name | table_name | index_name | main_schema_name | main_index_name | definition
----+-------------+------------+------------+------------------+-----------------+------------
(0 rows)
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
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+------------+------------------------+-------------------------+----------------
1 | newschema | newname | _timescaledb_internal | _hyper_1 | 2
(1 row)
DROP TABLE "two_Partitions" CASCADE;
DROP TABLE "newschema"."newname" CASCADE;
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table _timescaledb_internal._hyper_1_1_chunk
drop cascades to table _timescaledb_internal._hyper_1_2_chunk
drop cascades to table _timescaledb_internal._hyper_1_3_chunk
drop cascades to table _timescaledb_internal._hyper_1_4_chunk
NOTICE: index "1-two_Partitions_device_id_timeCustom_idx" does not exist, skipping
NOTICE: index "2-two_Partitions_timeCustom_series_0_idx" does not exist, skipping
NOTICE: index "3-two_Partitions_timeCustom_series_1_idx" does not exist, skipping
@ -377,11 +442,10 @@ SELECT * FROM _timescaledb_catalog.hypertable;
(0 rows)
\dt "public".*
List of relations
Schema | Name | Type | Owner
--------+---------+-------+----------
public | newname | table | postgres
(1 row)
List of relations
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dt "_timescaledb_catalog".*
List of relations
@ -403,3 +467,11 @@ SELECT * FROM _timescaledb_catalog.hypertable;
--------+------+------+-------+------+-------------
(0 rows)
-- Test that renaming ordinary table works
CREATE TABLE renametable (foo int);
ALTER TABLE "renametable" RENAME TO "newname_none_ht";
SELECT * FROM "newname_none_ht";
foo
-----
(0 rows)

@ -42,7 +42,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
111
112
(1 row)
\c postgres
@ -66,7 +66,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
111
112
(1 row)
\c single

@ -4,22 +4,32 @@
\d+ "_timescaledb_internal".*
-- Test that renaming hypertable is blocked
\set VERBOSITY default
\set ON_ERROR_STOP 0
ALTER TABLE "two_Partitions" RENAME TO "newname";
\set ON_ERROR_STOP 1
-- Test that renaming hypertable works
-- Test that renaming ordinary table works
CREATE TABLE renametable (foo int);
ALTER TABLE "renametable" RENAME TO "newname";
\d _timescaledb_internal._hyper_1_1_chunk
ALTER TABLE "two_Partitions" RENAME TO "newname";
SELECT * FROM "newname";
SELECT * FROM _timescaledb_catalog.hypertable;
CREATE SCHEMA "newschema";
ALTER TABLE "newname" SET SCHEMA "newschema";
SELECT * FROM "newschema"."newname";
SELECT * FROM _timescaledb_catalog.hypertable;
SELECT * FROM _timescaledb_catalog.hypertable_index WHERE main_schema_name <> 'newschema';
SELECT * FROM _timescaledb_catalog.chunk_index WHERE main_schema_name <> 'newschema';
SELECT * FROM _timescaledb_catalog.hypertable;
DROP TABLE "two_Partitions" CASCADE;
DROP TABLE "newschema"."newname" CASCADE;
SELECT * FROM _timescaledb_catalog.hypertable;
\dt "public".*
\dt "_timescaledb_catalog".*
\dt+ "_timescaledb_internal".*
-- Test that renaming ordinary table works
CREATE TABLE renametable (foo int);
ALTER TABLE "renametable" RENAME TO "newname_none_ht";
SELECT * FROM "newname_none_ht";