mirror of
https://github.com/timescale/timescaledb.git
synced 2025-04-20 13:53:19 +08:00
Fix dump and restore for tables with triggers and constraints
During a database restore, the creation of triggers and constraints on hypertables should not recurse to chunks since the chunks will be restored with existing triggers and constraints. Failing to block recursing during restore risks duplicating these objects on chunks. Further, operations on chunks (e.g., ALTER TABLE) should be allowed during the restore process since those operations are needed to recreate the constraints and triggers. To fix this, the "restoring" GUC is used to bypass custom handling of events in the ProcessUtility hook.
This commit is contained in:
parent
8cf8d3c377
commit
a13039fd58
@ -5,6 +5,7 @@
|
||||
extern bool guc_disable_optimizations;
|
||||
extern bool guc_optimize_non_hypertables;
|
||||
extern bool guc_constraint_aware_append;
|
||||
extern bool guc_restoring;
|
||||
|
||||
void _guc_init(void);
|
||||
void _guc_fini(void);
|
||||
|
@ -808,7 +808,11 @@ timescaledb_ProcessUtility(Node *parsetree,
|
||||
DestReceiver *dest,
|
||||
char *completion_tag)
|
||||
{
|
||||
if (!extension_is_loaded())
|
||||
/*
|
||||
* If we are restoring, we don't want to recurse to chunks or block
|
||||
* operations on chunks. If we do, the restore will fail.
|
||||
*/
|
||||
if (!extension_is_loaded() || guc_restoring)
|
||||
{
|
||||
prev_ProcessUtility(parsetree, query_string, context, params, dest, completion_tag);
|
||||
return;
|
||||
|
@ -37,6 +37,20 @@ INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1) VALUES
|
||||
(1257894000000000000, 'dev2', 1.5, 2);
|
||||
\set QUIET on
|
||||
\o
|
||||
-- Test that we can restore constraints
|
||||
ALTER TABLE PUBLIC."two_Partitions"
|
||||
ADD CONSTRAINT timeCustom_device_id_series_2_key
|
||||
UNIQUE ("timeCustom", device_id, series_2);
|
||||
-- Test that we can restore triggers
|
||||
CREATE OR REPLACE FUNCTION test_trigger()
|
||||
RETURNS TRIGGER LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$;
|
||||
CREATE TRIGGER restore_trigger BEFORE INSERT ON PUBLIC."two_Partitions"
|
||||
FOR EACH ROW EXECUTE PROCEDURE test_trigger();
|
||||
SELECT count(*)
|
||||
FROM pg_depend
|
||||
WHERE refclassid = 'pg_extension'::regclass
|
||||
@ -46,6 +60,67 @@ SELECT count(*)
|
||||
132
|
||||
(1 row)
|
||||
|
||||
\d+ _timescaledb_internal._hyper_1_1_chunk
|
||||
Table "_timescaledb_internal._hyper_1_1_chunk"
|
||||
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:
|
||||
"1_1_timecustom_device_id_series_2_key" UNIQUE CONSTRAINT, btree ("timeCustom", device_id, series_2)
|
||||
"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)
|
||||
Triggers:
|
||||
restore_trigger BEFORE INSERT ON _timescaledb_internal._hyper_1_1_chunk FOR EACH ROW EXECUTE PROCEDURE test_trigger()
|
||||
Inherits: "two_Partitions"
|
||||
|
||||
SELECT * FROM "two_Partitions" ORDER BY "timeCustom", device_id;
|
||||
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
|
||||
---------------------+-----------+----------+----------+----------+-------------
|
||||
1257894000000000000 | dev1 | 1.5 | 1 | 2 | t
|
||||
1257894000000000000 | dev1 | 1.5 | 2 | |
|
||||
1257894000000000000 | dev2 | 1.5 | 2 | |
|
||||
1257894000000000000 | dev2 | 1.5 | 1 | |
|
||||
1257894000000001000 | dev1 | 2.5 | 3 | |
|
||||
1257894001000000000 | dev1 | 3.5 | 4 | |
|
||||
1257894002000000000 | dev1 | 2.5 | 3 | |
|
||||
1257894002000000000 | dev1 | 5.5 | 6 | | t
|
||||
1257894002000000000 | dev1 | 5.5 | 7 | | f
|
||||
1257897600000000000 | dev1 | 4.5 | 5 | | f
|
||||
1257987600000000000 | dev1 | 1.5 | 2 | |
|
||||
1257987600000000000 | dev1 | 1.5 | 1 | |
|
||||
(12 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY "timeCustom", device_id;
|
||||
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 | |
|
||||
(7 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY "timeCustom", device_id;
|
||||
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
|
||||
---------------------+-----------+----------+----------+----------+-------------
|
||||
1257897600000000000 | dev1 | 4.5 | 5 | | f
|
||||
(1 row)
|
||||
|
||||
\c postgres
|
||||
\! pg_dump -h localhost -U postgres -Fc single > dump/single.sql
|
||||
\! dropdb -h localhost -U postgres single
|
||||
@ -70,29 +145,73 @@ SELECT count(*)
|
||||
132
|
||||
(1 row)
|
||||
|
||||
\c single
|
||||
--chunk schema should be the same
|
||||
\d+ _timescaledb_internal._hyper_1_1_chunk
|
||||
Table "_timescaledb_internal._hyper_1_1_chunk"
|
||||
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:
|
||||
"1_1_timecustom_device_id_series_2_key" UNIQUE CONSTRAINT, btree ("timeCustom", device_id, series_2)
|
||||
"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)
|
||||
Triggers:
|
||||
restore_trigger BEFORE INSERT ON _timescaledb_internal._hyper_1_1_chunk FOR EACH ROW EXECUTE PROCEDURE test_trigger()
|
||||
Inherits: "two_Partitions"
|
||||
|
||||
--data should be the same
|
||||
SELECT * FROM "two_Partitions" ORDER BY "timeCustom", device_id;
|
||||
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
|
||||
---------------------+-----------+----------+----------+----------+-------------
|
||||
1257894000000000000 | dev1 | 1.5 | 1 | 2 | t
|
||||
1257894000000000000 | dev1 | 1.5 | 2 | |
|
||||
1257894000000000000 | dev2 | 1.5 | 2 | |
|
||||
1257894000000000000 | dev2 | 1.5 | 1 | |
|
||||
1257894000000001000 | dev1 | 2.5 | 3 | |
|
||||
1257894001000000000 | dev1 | 3.5 | 4 | |
|
||||
1257894002000000000 | dev1 | 2.5 | 3 | |
|
||||
1257894002000000000 | dev1 | 5.5 | 6 | | t
|
||||
1257894002000000000 | dev1 | 5.5 | 7 | | f
|
||||
1257897600000000000 | dev1 | 4.5 | 5 | | f
|
||||
1257987600000000000 | dev1 | 1.5 | 2 | |
|
||||
1257987600000000000 | dev1 | 1.5 | 1 | |
|
||||
(12 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY "timeCustom", device_id;
|
||||
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 | |
|
||||
(7 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY "timeCustom", device_id;
|
||||
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool
|
||||
---------------------+-----------+----------+----------+----------+-------------
|
||||
1257897600000000000 | dev1 | 4.5 | 5 | | f
|
||||
(1 row)
|
||||
|
||||
--check simple ddl still works
|
||||
ALTER TABLE "two_Partitions" ADD COLUMN series_3 integer;
|
||||
INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1, series_3) VALUES
|
||||
(1357894000000000000, 'dev5', 1.5, 2, 4);
|
||||
SELECT * FROM "two_Partitions" order by "timeCustom", device_id;
|
||||
timeCustom | device_id | series_0 | series_1 | series_2 | series_bool | series_3
|
||||
---------------------+-----------+----------+----------+----------+-------------+----------
|
||||
1257894000000000000 | dev1 | 1.5 | 1 | 2 | t |
|
||||
1257894000000000000 | dev1 | 1.5 | 2 | | |
|
||||
1257894000000000000 | dev2 | 1.5 | 2 | | |
|
||||
1257894000000000000 | dev2 | 1.5 | 1 | | |
|
||||
1257894000000001000 | dev1 | 2.5 | 3 | | |
|
||||
1257894001000000000 | dev1 | 3.5 | 4 | | |
|
||||
1257894002000000000 | dev1 | 2.5 | 3 | | |
|
||||
1257894002000000000 | dev1 | 5.5 | 6 | | t |
|
||||
1257894002000000000 | dev1 | 5.5 | 7 | | f |
|
||||
1257897600000000000 | dev1 | 4.5 | 5 | | f |
|
||||
1257987600000000000 | dev1 | 1.5 | 2 | | |
|
||||
1257987600000000000 | dev1 | 1.5 | 1 | | |
|
||||
1357894000000000000 | dev5 | 1.5 | 2 | | | 4
|
||||
(13 rows)
|
||||
|
||||
--query for the extension tables/sequences that will not be dumped by pg_dump (should be empty except for views)
|
||||
SELECT objid::regclass
|
||||
FROM pg_catalog.pg_depend
|
||||
|
@ -2,11 +2,33 @@
|
||||
\ir include/insert_two_partitions.sql
|
||||
\o
|
||||
|
||||
-- Test that we can restore constraints
|
||||
ALTER TABLE PUBLIC."two_Partitions"
|
||||
ADD CONSTRAINT timeCustom_device_id_series_2_key
|
||||
UNIQUE ("timeCustom", device_id, series_2);
|
||||
|
||||
-- Test that we can restore triggers
|
||||
CREATE OR REPLACE FUNCTION test_trigger()
|
||||
RETURNS TRIGGER LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE TRIGGER restore_trigger BEFORE INSERT ON PUBLIC."two_Partitions"
|
||||
FOR EACH ROW EXECUTE PROCEDURE test_trigger();
|
||||
|
||||
SELECT count(*)
|
||||
FROM pg_depend
|
||||
WHERE refclassid = 'pg_extension'::regclass
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
|
||||
\d+ _timescaledb_internal._hyper_1_1_chunk
|
||||
SELECT * FROM "two_Partitions" ORDER BY "timeCustom", device_id;
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY "timeCustom", device_id;
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY "timeCustom", device_id;
|
||||
|
||||
\c postgres
|
||||
|
||||
\! pg_dump -h localhost -U postgres -Fc single > dump/single.sql
|
||||
@ -24,15 +46,18 @@ SELECT count(*)
|
||||
WHERE refclassid = 'pg_extension'::regclass
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
|
||||
--chunk schema should be the same
|
||||
\d+ _timescaledb_internal._hyper_1_1_chunk
|
||||
--data should be the same
|
||||
SELECT * FROM "two_Partitions" ORDER BY "timeCustom", device_id;
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY "timeCustom", device_id;
|
||||
SELECT * FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY "timeCustom", device_id;
|
||||
|
||||
\c single
|
||||
--check simple ddl still works
|
||||
ALTER TABLE "two_Partitions" ADD COLUMN series_3 integer;
|
||||
INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1, series_3) VALUES
|
||||
(1357894000000000000, 'dev5', 1.5, 2, 4);
|
||||
|
||||
SELECT * FROM "two_Partitions" order by "timeCustom", device_id;
|
||||
|
||||
--query for the extension tables/sequences that will not be dumped by pg_dump (should be empty except for views)
|
||||
SELECT objid::regclass
|
||||
FROM pg_catalog.pg_depend
|
||||
|
Loading…
x
Reference in New Issue
Block a user