mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-26 08:41:09 +08:00
We want to get more insights about what plans are executed on data nodes. If a user runs explain with verbose option we will connect to each data node, run explain on data node and print output together with existing explain output.
858 lines
30 KiB
MySQL
858 lines
30 KiB
MySQL
-- 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';
|
|
$$);
|