-- This file and its contents are licensed under the Timescale License. -- Please see the included NOTICE for copyright information and -- LICENSE-TIMESCALE for a copy of the license. -- Need to be super user to create extension and add data nodes \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; \ir include/remote_exec.sql -- Support for execute_sql_and_filter_data_node_name_on_error() \unset ECHO \o /dev/null \ir include/filter_exec.sql \o \set ECHO all -- Cleanup from other potential tests that created these databases SET client_min_messages TO ERROR; DROP DATABASE IF EXISTS data_node_1; DROP DATABASE IF EXISTS data_node_2; DROP DATABASE IF EXISTS data_node_3; SET client_min_messages TO NOTICE; -- Add data nodes using the TimescaleDB node management API SELECT * FROM add_data_node('data_node_1', host => 'localhost', database => 'data_node_1'); SELECT * FROM add_data_node('data_node_2', host => 'localhost', database => 'data_node_2'); SELECT * FROM add_data_node('data_node_3', host => 'localhost', database => 'data_node_3'); GRANT USAGE ON FOREIGN SERVER data_node_1, data_node_2, data_node_3 TO PUBLIC; SET ROLE :ROLE_1; -- Verify lack of tables SELECT node_name, "options" FROM timescaledb_information.data_node ORDER BY node_name; \set ON_ERROR_STOP 0 -- Test that one cannot directly create TimescaleDB foreign tables CREATE FOREIGN TABLE foreign_table (time timestamptz, device int, temp float) SERVER data_node_1; \set ON_ERROR_STOP 1 -- Create distributed hypertables. Add a trigger and primary key -- constraint to test how those work CREATE TABLE disttable(time timestamptz, device int CHECK (device > 0), color int, temp float, PRIMARY KEY (time,device)); SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device', 1); -- Increase the number of partitions. Expect warning since still too low SELECT * FROM set_number_partitions('disttable', 2); -- Set number of partitions equal to the number of servers should not -- raise a warning. SELECT * FROM set_number_partitions('disttable', 3, 'device'); -- Show the number of slices SELECT h.table_name, d.column_name, d.num_slices FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'disttable'; -- This table tests both 1-dimensional tables and under-replication -- (replication_factor > num_data_nodes). CREATE TABLE underreplicated(time timestamptz, device int, temp float); SELECT * FROM create_hypertable('underreplicated', 'time', replication_factor => 4); SET ROLE :ROLE_1; CREATE OR REPLACE FUNCTION test_trigger() RETURNS TRIGGER LANGUAGE PLPGSQL AS $BODY$ DECLARE cnt INTEGER; BEGIN SELECT count(*) INTO cnt FROM hyper; RAISE WARNING 'FIRING trigger when: % level: % op: % cnt: % trigger_name %', tg_when, tg_level, tg_op, cnt, tg_name; IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; RETURN NEW; END $BODY$; CREATE TRIGGER _0_test_trigger_insert BEFORE INSERT ON disttable FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM _timescaledb_catalog.hypertable_data_node; SELECT * FROM _timescaledb_catalog.chunk_data_node; -- The constraints, indexes, and triggers on the hypertable SELECT * FROM test.show_constraints('disttable'); SELECT * FROM test.show_indexes('disttable'); SELECT * FROM test.show_triggers('disttable'); -- Drop a column. This will make the attribute numbers of the -- hypertable's root relation differ from newly created chunks. It is -- a way to test that we properly handle attributed conversion between -- the root table and chunks ALTER TABLE disttable DROP COLUMN color; -- EXPLAIN some inserts to see what plans and explain output for -- remote inserts look like EXPLAIN (COSTS FALSE) INSERT INTO disttable VALUES ('2017-01-01 06:01', 1, 1.1); EXPLAIN (VERBOSE, COSTS FALSE) INSERT INTO disttable VALUES ('2017-01-01 06:01', 1, 1.1); -- Create some chunks through insertion INSERT INTO disttable VALUES ('2017-01-01 06:01', 1, 1.1), ('2017-01-01 09:11', 3, 2.1), ('2017-01-01 08:01', 1, 1.2), ('2017-01-02 08:01', 2, 1.3), ('2018-07-02 08:01', 87, 1.6), ('2018-07-01 06:01', 13, 1.4), ('2018-07-01 09:11', 90, 2.7), ('2018-07-01 08:01', 29, 1.5); \set ON_ERROR_STOP 0 -- VACUUM currently not supported. A VACCUM cannot run in a -- transaction block so we need to distribute the command to data -- nodes using "raw" connections. VACUUM ANALYZE disttable; VACUUM FULL disttable; VACUUM disttable; \set ON_ERROR_STOP 1 -- Test ANALYZE. First show no statistics SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE oid = 'disttable'::regclass; SELECT relname, relkind, reltuples, relpages FROM pg_class cl, (SELECT show_chunks AS chunk FROM show_chunks('disttable')) ch WHERE cl.oid = ch.chunk::regclass; ANALYZE disttable; -- Show updated statistics SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE oid = 'disttable'::regclass; SELECT relname, relkind, reltuples, relpages FROM pg_class cl, (SELECT show_chunks AS chunk FROM show_chunks('disttable')) ch WHERE cl.oid = ch.chunk::regclass; -- Test prepared statement PREPARE dist_insert (timestamptz, int, float) AS INSERT INTO disttable VALUES ($1, $2, $3); EXECUTE dist_insert ('2017-01-01 06:05', 1, 1.4); -- Show chunks created SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable'); -- Show that there are assigned node_chunk_id:s in chunk data node mappings SELECT * FROM _timescaledb_catalog.chunk_data_node; -- Show that chunks are created on data nodes and that each data node -- has their own unique slice in the space (device) dimension. SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable'); $$); SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM disttable; $$); SELECT node_name, "options" FROM timescaledb_information.data_node ORDER BY node_name; SELECT * FROM hypertable_data_node_relation_size('disttable'); -- Show what some queries would look like on the frontend EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM disttable; SELECT * FROM disttable; EXPLAIN (VERBOSE, COSTS FALSE) SELECT time_bucket('3 hours', time) AS time, device, avg(temp) AS avg_temp FROM disttable GROUP BY 1, 2 ORDER BY 1; -- Execute some queries on the frontend and return the results SELECT * FROM disttable; SELECT time_bucket('3 hours', time) AS time, device, avg(temp) AS avg_temp FROM disttable GROUP BY 1, 2 ORDER BY 1; SELECT time_bucket('3 hours', time) AS time, device, avg(temp) AS avg_temp FROM disttable GROUP BY 1, 2 HAVING avg(temp) > 1.2 ORDER BY 1; SELECT time_bucket('3 hours', time) AS time, device, avg(temp) AS avg_temp FROM disttable WHERE temp > 2 GROUP BY 1, 2 HAVING avg(temp) > 1.2 ORDER BY 1; -- Test AsyncAppend when using min/max aggregates EXPLAIN (VERBOSE, COSTS FALSE) SELECT max(temp) FROM disttable; SELECT max(temp) FROM disttable; -- Test turning off async append SET timescaledb.enable_async_append = OFF; EXPLAIN (VERBOSE, COSTS FALSE) SELECT max(temp) FROM disttable; SET timescaledb.enable_async_append = ON; EXPLAIN (VERBOSE, COSTS FALSE) SELECT min(temp), max(temp) FROM disttable; SELECT min(temp), max(temp) FROM disttable; -- Test AsyncAppend when using window functions EXPLAIN (VERBOSE, COSTS FALSE) SELECT device, temp, avg(temp) OVER (PARTITION BY device) FROM disttable; SELECT device, temp, avg(temp) OVER (PARTITION BY device) FROM disttable; -- Test remote explain SET timescaledb.enable_remote_explain = ON; EXPLAIN (VERBOSE, COSTS FALSE) SELECT max(temp) FROM disttable; -- Don't remote explain if there is no VERBOSE flag EXPLAIN (COSTS FALSE) SELECT max(temp) FROM disttable; -- Test additional EXPLAIN flags EXPLAIN (ANALYZE, VERBOSE, COSTS FALSE, BUFFERS ON, TIMING OFF, SUMMARY OFF) SELECT max(temp) FROM disttable; -- The constraints, indexes, and triggers on foreign chunks. Only -- check constraints should recurse to foreign chunks (although they -- aren't enforced on a foreign table) SELECT st."Child" as chunk_relid, test.show_constraints((st)."Child") FROM test.show_subtables('disttable') st; SELECT st."Child" as chunk_relid, test.show_indexes((st)."Child") FROM test.show_subtables('disttable') st; SELECT st."Child" as chunk_relid, test.show_triggers((st)."Child") FROM test.show_subtables('disttable') st; -- Check that the chunks are assigned data nodes SELECT * FROM _timescaledb_catalog.chunk_data_node; -- Adding a new trigger should not recurse to foreign chunks CREATE TRIGGER _1_test_trigger_insert AFTER INSERT ON disttable FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT st."Child" as chunk_relid, test.show_triggers((st)."Child") FROM test.show_subtables('disttable') st; -- Check that we can create indexes on distributed hypertables and -- that they don't recurse to foreign chunks CREATE INDEX ON disttable (time, device); SELECT * FROM test.show_indexes('disttable'); SELECT st."Child" as chunk_relid, test.show_indexes((st)."Child") FROM test.show_subtables('disttable') st; -- No index mappings should exist either SELECT * FROM _timescaledb_catalog.chunk_index; -- Check that creating columns work ALTER TABLE disttable ADD COLUMN "Color" int; SELECT * FROM test.show_columns('disttable'); SELECT st."Child" as chunk_relid, test.show_columns((st)."Child") FROM test.show_subtables('disttable') st; -- Adding a new unique constraint should not recurse to foreign -- chunks, but a check constraint should ALTER TABLE disttable ADD CONSTRAINT disttable_color_unique UNIQUE (time, device, "Color"); ALTER TABLE disttable ADD CONSTRAINT disttable_temp_non_negative CHECK (temp > 0.0); SELECT st."Child" as chunk_relid, test.show_constraints((st)."Child") FROM test.show_subtables('disttable') st; SELECT cc.* FROM (SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable')) c, _timescaledb_catalog.chunk_constraint cc WHERE c.chunk_id = cc.chunk_id; -- Show contents after re-adding column SELECT * FROM disttable; -- Test INSERTS with RETURNING. Since we previously dropped a column -- on the hypertable, this also tests that we handle conversion of the -- attribute numbers in the RETURNING clause, since they now differ -- between the hypertable root relation and the chunk currently -- RETURNING from. INSERT INTO disttable (time, device, "Color", temp) VALUES ('2017-09-02 06:09', 4, 1, 9.8) RETURNING time, "Color", temp; INSERT INTO disttable (time, device, "Color", temp) VALUES ('2017-09-03 06:18', 9, 3, 8.7) RETURNING 1; -- On conflict INSERT INTO disttable (time, device, "Color", temp) VALUES ('2017-09-02 06:09', 6, 2, 10.5) ON CONFLICT DO NOTHING; SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable'); $$); -- Show new row and that conflicting row is not inserted SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM disttable; $$); \set ON_ERROR_STOP 0 -- ON CONFLICT only works with DO NOTHING INSERT INTO disttable (time, device, "Color", temp) VALUES ('2017-09-09 08:13', 7, 3, 27.5) ON CONFLICT (time) DO UPDATE SET temp = 3.2; -- Test that an INSERT that would create a chunk does not work on a -- data node SELECT * FROM test.remote_exec('{ data_node_1 }', $$ INSERT INTO disttable VALUES ('2019-01-02 12:34', 1, 2, 9.3) $$); \set ON_ERROR_STOP 1 -- However, INSERTs on a data node that does not create a chunk works. SELECT * FROM test.remote_exec('{ data_node_1 }', $$ INSERT INTO disttable VALUES ('2017-09-03 06:09', 1, 2, 9.3) $$); -- Test updates UPDATE disttable SET "Color" = 4 WHERE "Color" = 3; SELECT * FROM disttable; WITH devices AS ( SELECT DISTINCT device FROM disttable ORDER BY device ) UPDATE disttable SET "Color" = 2 WHERE device = (SELECT device FROM devices LIMIT 1); \set ON_ERROR_STOP 0 -- Updates referencing non-existing column UPDATE disttable SET device = 4 WHERE no_such_column = 2; UPDATE disttable SET no_such_column = 4 WHERE device = 2; -- Update to system column UPDATE disttable SET tableoid = 4 WHERE device = 2; \set ON_ERROR_STOP 1 -- Test deletes (no rows deleted) DELETE FROM disttable WHERE device = 3 RETURNING *; -- Test deletes (rows deleted) DELETE FROM disttable WHERE device = 4 RETURNING *; -- Query to show that rows are deleted SELECT * FROM disttable; -- Ensure rows are deleted on the data nodes SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM disttable; $$); -- Test TRUNCATE TRUNCATE disttable; -- No data should remain SELECT * FROM disttable; -- Metadata and tables cleaned up SELECT * FROM _timescaledb_catalog.chunk; SELECT * FROM show_chunks('disttable'); -- Also cleaned up remotely SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM _timescaledb_catalog.chunk; SELECT * FROM show_chunks('disttable'); SELECT * FROM disttable; $$); -- The hypertable view also shows no chunks and no data SELECT * FROM timescaledb_information.hypertable ORDER BY table_schema, table_name; -- Test underreplicated chunk warning INSERT INTO underreplicated VALUES ('2017-01-01 06:01', 1, 1.1), ('2017-01-02 07:01', 2, 3.5); SELECT * FROM _timescaledb_catalog.chunk_data_node; SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('underreplicated'); -- Show chunk data node mappings SELECT * FROM _timescaledb_catalog.chunk_data_node; -- Show that chunks are created on remote data nodes and that all -- data nodes/chunks have the same data due to replication SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('underreplicated'); $$); SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM underreplicated; $$); -- Test updates UPDATE underreplicated SET temp = 2.0 WHERE device = 2 RETURNING time, temp, device; SELECT * FROM underreplicated; -- Show that all replica chunks are updated SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM underreplicated; $$); DELETE FROM underreplicated WHERE device = 2 RETURNING *; -- Ensure deletes across all data nodes SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM underreplicated; $$); -- Test hypertable creation fails on distributed error SELECT * FROM test.remote_exec('{ data_node_3 }', $$ CREATE TABLE remotetable(time timestamptz PRIMARY KEY, id int, cost float); SELECT * FROM underreplicated; $$); \set ON_ERROR_STOP 0 CREATE TABLE remotetable(time timestamptz PRIMARY KEY, device int CHECK (device > 0), color int, temp float); SELECT * FROM create_hypertable('remotetable', 'time', replication_factor => 1); -- Test distributed_hypertable creation fails with replication factor 0 CREATE TABLE remotetable2(time timestamptz PRIMARY KEY, device int CHECK (device > 0), color int, temp float); SELECT * FROM create_distributed_hypertable('remotetable2', 'time', replication_factor => 0); \set ON_ERROR_STOP 1 SELECT * FROM timescaledb_information.hypertable ORDER BY table_schema, table_name; -- Test distributed hypertable creation with many parameters \c data_node_1 CREATE SCHEMA "T3sTSch"; CREATE SCHEMA "Table\\Schema"; CREATE SCHEMA "single'schema"; GRANT ALL ON SCHEMA "T3sTSch" TO :ROLE_1; GRANT ALL ON SCHEMA "Table\\Schema" TO :ROLE_1; GRANT ALL ON SCHEMA "single'schema" TO :ROLE_1; \c data_node_2 CREATE SCHEMA "T3sTSch"; CREATE SCHEMA "Table\\Schema"; GRANT ALL ON SCHEMA "T3sTSch" TO :ROLE_1; GRANT ALL ON SCHEMA "Table\\Schema" TO :ROLE_1; \c data_node_3 CREATE SCHEMA "T3sTSch"; CREATE SCHEMA "Table\\Schema"; GRANT ALL ON SCHEMA "T3sTSch" TO :ROLE_1; GRANT ALL ON SCHEMA "Table\\Schema" TO :ROLE_1; SET ROLE :ROLE_1; \c :TEST_DBNAME :ROLE_SUPERUSER CREATE SCHEMA "T3sTSch"; CREATE SCHEMA "Table\\Schema"; CREATE SCHEMA "single'schema"; GRANT ALL ON SCHEMA "T3sTSch" TO :ROLE_1; GRANT ALL ON SCHEMA "Table\\Schema" TO :ROLE_1; GRANT ALL ON SCHEMA "single'schema" TO :ROLE_1; SET ROLE :ROLE_1; CREATE TABLE "Table\\Schema"."Param_Table"("time Col %#^#@$#" timestamptz, __region text, reading float); SELECT * FROM create_distributed_hypertable('"Table\\Schema"."Param_Table"', 'time Col %#^#@$#', partitioning_column => '__region', associated_schema_name => 'T3sTSch', associated_table_prefix => 'test*pre_', chunk_time_interval => interval '1 week', create_default_indexes => FALSE, if_not_exists => TRUE, migrate_data => TRUE, replication_factor => 2, data_nodes => '{ "data_node_3" }'); -- Test attach_data_node. First show dimensions and currently attached -- servers. The number of slices in the space dimension should equal -- the number of servers since we didn't explicitly specify -- number_partitions SELECT h.table_name, d.column_name, d.num_slices FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'Param_Table' ORDER BY 1, 2, 3; SELECT h.table_name, hdn.node_name FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.hypertable_data_node hdn WHERE h.id = hdn.hypertable_id AND h.table_name = 'Param_Table' ORDER BY 1, 2; SELECT * FROM attach_data_node('data_node_1', '"Table\\Schema"."Param_Table"'); -- Show updated metadata after attach SELECT h.table_name, d.column_name, d.num_slices FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'Param_Table' ORDER BY 1, 2, 3; SELECT h.table_name, hdn.node_name FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.hypertable_data_node hdn WHERE h.id = hdn.hypertable_id AND h.table_name = 'Param_Table' ORDER BY 1, 2; -- Attach another data node but do not auto-repartition, i.e., -- increase the number of slices. SELECT * FROM attach_data_node('data_node_2', '"Table\\Schema"."Param_Table"', repartition => false); -- Number of slices should not be increased SELECT h.table_name, d.column_name, d.num_slices FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'Param_Table' ORDER BY 1, 2, 3; -- Manually increase the number of partitions SELECT * FROM set_number_partitions('"Table\\Schema"."Param_Table"', 4); -- Verify hypertables on all data nodes SELECT * FROM _timescaledb_catalog.hypertable; SELECT * FROM _timescaledb_catalog.dimension; SELECT * FROM test.show_triggers('"Table\\Schema"."Param_Table"'); SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM _timescaledb_catalog.hypertable; SELECT * FROM _timescaledb_catalog.dimension; SELECT t.tgname, t.tgtype, t.tgfoid::regproc FROM pg_trigger t, pg_class c WHERE c.relname = 'Param_Table' AND t.tgrelid = c.oid; $$); -- Test multi-dimensional hypertable. The add_dimension() command -- should be propagated to backends. CREATE TABLE dimented_table (time timestamptz, column1 int, column2 timestamptz, column3 int); SELECT * FROM create_distributed_hypertable('dimented_table', 'time', partitioning_column => 'column1', number_partitions => 4, replication_factor => 1, data_nodes => '{ "data_node_1" }'); SELECT * FROM add_dimension('dimented_table', 'column2', chunk_time_interval => interval '1 week'); SELECT * FROM add_dimension('dimented_table', 'column3', 4, partitioning_func => '_timescaledb_internal.get_partition_for_key'); SELECT * FROM _timescaledb_catalog.dimension; SELECT * FROM attach_data_node('data_node_2', 'dimented_table'); SELECT * FROM _timescaledb_catalog.dimension; -- Note that this didn't get the add_dimension SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM _timescaledb_catalog.dimension; $$); --test per-data node queries -- Create some chunks through insertion CREATE TABLE disttable_replicated(time timestamptz PRIMARY KEY, device int CHECK (device > 0), temp float, "Color" int); SELECT * FROM create_hypertable('disttable_replicated', 'time', replication_factor => 2); INSERT INTO disttable_replicated VALUES ('2017-01-01 06:01', 1, 1.1, 1), ('2017-01-01 08:01', 1, 1.2, 2), ('2018-01-02 08:01', 2, 1.3, 3), ('2019-01-01 09:11', 3, 2.1, 4), ('2020-01-01 06:01', 5, 1.1, 10), ('2020-01-01 08:01', 6, 1.2, 11), ('2021-01-02 08:01', 7, 1.3, 12), ('2022-01-01 09:11', 8, 2.1, 13); SELECT * FROM disttable_replicated; EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated; --guc disables the optimization SET timescaledb.enable_per_data_node_queries = FALSE; EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated; SET timescaledb.enable_per_data_node_queries = TRUE; --test WHERE clause EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated WHERE temp > 2.0; --test OR EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated WHERE temp > 2.0 or "Color" = 11; --test some chunks excluded EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated WHERE time < '2018-01-01 09:11'; --test all chunks excluded EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) SELECT * FROM disttable_replicated WHERE time < '2002-01-01 09:11'; --test cte EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) WITH cte AS ( SELECT * FROM disttable_replicated ) SELECT * FROM cte; --queries that involve updates/inserts are not optimized EXPLAIN (VERBOSE, ANALYZE, COSTS FALSE, TIMING FALSE, SUMMARY FALSE) WITH devices AS ( SELECT DISTINCT device FROM disttable_replicated ORDER BY device ) UPDATE disttable_replicated SET device = 2 WHERE device = (SELECT device FROM devices LIMIT 1); -- Test inserts with smaller batch size and more tuples to reach full -- batch SET timescaledb.max_insert_batch_size=4; CREATE TABLE twodim (time timestamptz DEFAULT '2019-02-10 10:11', "Color" int DEFAULT 11 CHECK ("Color" > 0), temp float DEFAULT 22.1); -- Create a replicated table to ensure we handle that case correctly -- with batching SELECT * FROM create_hypertable('twodim', 'time', 'Color', 3, replication_factor => 2, data_nodes => '{ "data_node_1", "data_node_2", "data_node_3" }'); SELECT * FROM twodim ORDER BY time; -- INSERT enough data to stretch across multiple batches per -- data node. Also return a system column. Although we write tuples to -- multiple data nodes, the returned tuple should only be the ones in the -- original insert statement (without the replica tuples). WITH result AS ( INSERT INTO twodim VALUES ('2017-02-01 06:01', 1, 1.1), ('2017-02-01 08:01', 1, 1.2), ('2018-02-02 08:01', 2, 1.3), ('2019-02-01 09:11', 3, 2.1), ('2019-02-02 09:11', 3, 2.1), ('2019-02-02 10:01', 5, 1.2), ('2019-02-03 11:11', 6, 3.5), ('2019-02-04 08:21', 4, 6.6), ('2019-02-04 10:11', 7, 7.4), ('2019-02-04 12:11', 8, 2.1), ('2019-02-05 13:31', 8, 6.3), ('2019-02-06 02:11', 5, 1.8), ('2019-02-06 01:13', 7, 7.9), ('2019-02-06 19:24', 9, 5.9), ('2019-02-07 18:44', 5, 9.7), ('2019-02-07 20:24', 6, NULL), ('2019-02-07 09:33', 7, 9.5), ('2019-02-08 08:54', 1, 7.3), ('2019-02-08 18:14', 4, 8.2), ('2019-02-09 19:23', 8, 9.1) RETURNING tableoid = 'twodim'::regclass AS is_tableoid, time, temp, "Color" ) SELECT * FROM result ORDER BY time; -- Test insert with default values and a batch size of 1. SET timescaledb.max_insert_batch_size=1; EXPLAIN (VERBOSE, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO twodim DEFAULT VALUES; INSERT INTO twodim DEFAULT VALUES; -- Reset the batch size SET timescaledb.max_insert_batch_size=4; -- Constraint violation error check -- -- Execute and filter mentioned data node name in the error message. \set ON_ERROR_STOP 0 SELECT test.execute_sql_and_filter_data_node_name_on_error($$ INSERT INTO twodim VALUES ('2019-02-10 17:54', 0, 10.2) $$); \set ON_ERROR_STOP 1 -- Disable batching, reverting to FDW tuple-by-tuple inserts. -- First EXPLAIN with batching turned on. EXPLAIN (VERBOSE, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO twodim VALUES ('2019-02-10 16:23', 5, 7.1), ('2019-02-10 17:11', 7, 3.2); SET timescaledb.max_insert_batch_size=0; -- Compare without batching EXPLAIN (VERBOSE, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO twodim VALUES ('2019-02-10 16:23', 5, 7.1), ('2019-02-10 17:11', 7, 3.2); -- Insert without batching INSERT INTO twodim VALUES ('2019-02-10 16:23', 5, 7.1), ('2019-02-10 17:11', 7, 3.2); -- Check results SELECT * FROM twodim ORDER BY time; SELECT count(*) FROM twodim; -- Show distribution across data nodes SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT * FROM twodim ORDER BY time; SELECT count(*) FROM twodim; $$); -- Distributed table with custom type that has no binary output CREATE TABLE disttable_with_ct(time timestamptz, txn_id rxid, val float, info text); SELECT * FROM create_hypertable('disttable_with_ct', 'time', replication_factor => 2); -- Insert data with custom type INSERT INTO disttable_with_ct VALUES ('2019-01-01 01:01', 'ts-1-10-20-30', 1.1, 'a'), ('2019-01-01 01:02', 'ts-1-11-20-30', 2.0, repeat('abc', 1000000)); -- TOAST -- Test queries on distributed table with custom type SELECT time, txn_id, val, substring(info for 20) FROM disttable_with_ct; SET timescaledb.enable_connection_binary_data=false; SELECT time, txn_id, val, substring(info for 20) FROM disttable_with_ct; -- Test DELETE with replication DELETE FROM disttable_with_ct WHERE info = 'a'; -- Check if row is gone SELECT time, txn_id, val, substring(info for 20) FROM disttable_with_ct; -- Connect to data nodes to see if data is gone SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT time, txn_id, val, substring(info for 20) FROM disttable_with_ct; $$); -- Test single quote in names SET SCHEMA 'single''schema'; CREATE TABLE "disttable'quote"(time timestamptz, "device'quote" int, val float, info text); SELECT public.create_distributed_hypertable( 'disttable''quote', 'time', 'device''quote', data_nodes => '{ "data_node_1" }' ); SET SCHEMA 'public'; CREATE TABLE disttable_drop_chunks(time timestamptz, device int CHECK (device > 0), color int, PRIMARY KEY (time,device)); SELECT * FROM create_distributed_hypertable('disttable_drop_chunks', 'time', 'device', number_partitions => 3, replication_factor => 2); INSERT INTO disttable_drop_chunks VALUES ('2017-01-01 06:01', 1, 1.1), ('2017-01-01 09:11', 3, 2.1), ('2017-01-01 08:01', 1, 1.2), ('2017-01-02 08:01', 2, 1.3), ('2018-07-02 08:01', 87, 1.6), ('2018-07-01 06:01', 13, 1.4), ('2018-07-01 09:11', 90, 2.7), ('2018-07-01 08:01', 29, 1.5); -- Show chunks on access node SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable_drop_chunks'); -- Show chunks on data nodes SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable_drop_chunks'); $$); -- test passing older_than and schema name SELECT * FROM drop_chunks(older_than => '2018-01-01'::timestamptz, table_name => 'disttable_drop_chunks', schema_name => 'public'); SELECT * FROM disttable_drop_chunks; SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable_drop_chunks'); SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT (_timescaledb_internal.show_chunk(show_chunks)).* FROM show_chunks('disttable_drop_chunks'); $$); -- test passing newer_than as interval and cascade SELECT * FROM drop_chunks(newer_than => interval '10 years', table_name => 'disttable_drop_chunks', cascade => true); SELECT * FROM disttable_drop_chunks; CREATE TABLE "weird nAme\\#^."(time bigint, device int CHECK (device > 0), color int, PRIMARY KEY (time,device)); SELECT * FROM create_distributed_hypertable('"weird nAme\\#^."', 'time', 'device', 3, chunk_time_interval => 100, replication_factor => 2); INSERT INTO "weird nAme\\#^." VALUES (300, 1, 1.1), (400, 3, 2.1), (350, 1, 1.2); SELECT * FROM "weird nAme\\#^."; -- drop chunks using bigint as time SELECT * FROM drop_chunks(older_than => 1000, table_name => 'weird nAme\\#^.'); SELECT * FROM "weird nAme\\#^."; ----------------------------------------------------------------------------------------- -- Test that settings on hypertables are distributed to data nodes ----------------------------------------------------------------------------------------- DROP TABLE disttable CASCADE; CREATE TABLE disttable (time bigint, device int, temp float); SELECT create_distributed_hypertable('disttable', 'time', chunk_time_interval => 1000000::bigint); -- Show the dimension configuration on data nodes SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT d.* FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'disttable'; $$); -- Test adding a space dimension. Should apply to data nodes as -- well. We're setting num_partitions lower than the number of servers -- and expect a warning. SELECT * FROM add_dimension('disttable', 'device', 1, partitioning_func => '_timescaledb_internal.get_partition_hash'); CREATE INDEX disttable_device_time_idx ON disttable (device, time); SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT d.* FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'disttable'; $$); -- Show that changing dimension settings apply to data nodes SELECT * FROM set_chunk_time_interval('disttable', 2000000000::bigint); SELECT * FROM set_number_partitions('disttable', 3); CREATE OR REPLACE FUNCTION dummy_now() RETURNS BIGINT LANGUAGE SQL IMMUTABLE as 'SELECT 2::BIGINT'; SELECT * FROM distributed_exec($$ CREATE OR REPLACE FUNCTION dummy_now() RETURNS BIGINT LANGUAGE SQL IMMUTABLE as 'SELECT 2::BIGINT' $$); SELECT * FROM set_integer_now_func('disttable', 'dummy_now'); -- Show changes to dimensions SELECT * FROM test.remote_exec('{ data_node_1, data_node_2, data_node_3 }', $$ SELECT d.* FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.dimension d WHERE h.id = d.hypertable_id AND h.table_name = 'disttable'; $$);