diff --git a/sql/updates/pre-update.sql b/sql/updates/pre-update.sql index 018556fe546..90d464bbe64 100644 --- a/sql/updates/pre-update.sql +++ b/sql/updates/pre-update.sql @@ -51,3 +51,35 @@ WHERE ) ; +-- ERROR if trying to update the extension on PG16 using Multi-Node +DO $$ +DECLARE + data_nodes TEXT; + dist_hypertables TEXT; +BEGIN + IF current_setting('server_version_num')::int >= 160000 THEN + SELECT string_agg(format('%I.%I', hypertable_schema, hypertable_name), ', ') + INTO dist_hypertables + FROM timescaledb_information.hypertables + WHERE is_distributed IS TRUE; + + IF dist_hypertables IS NOT NULL THEN + RAISE USING + ERRCODE = 'feature_not_supported', + MESSAGE = 'cannot upgrade because multi-node is not supported on PostgreSQL >= 16', + DETAIL = 'The following distributed hypertables should be migrated to regular: '||dist_hypertables; + END IF; + + SELECT string_agg(format('%I', node_name), ', ') + INTO data_nodes + FROM timescaledb_information.data_nodes; + + IF data_nodes IS NOT NULL THEN + RAISE USING + ERRCODE = 'feature_not_supported', + MESSAGE = 'cannot upgrade because multi-node is not supported on PostgreSQL >= 16', + DETAIL = 'The following data nodes should be removed: '||data_nodes; + END IF; + END IF; +END $$; + diff --git a/src/hypertable.c b/src/hypertable.c index 08ab3b87940..aecc1e1d2c5 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -68,6 +68,7 @@ #include "debug_assert.h" #include "osm_callbacks.h" #include "error_utils.h" +#include "compat/compat.h" Oid ts_rel_get_owner(Oid relid) @@ -2046,6 +2047,13 @@ ts_hypertable_create(PG_FUNCTION_ARGS) Datum ts_hypertable_distributed_create(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("distributed hypertable is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."), + errdetail("Use the \"create_hypertable\" to create a regular hypertable instead."))); +#endif return ts_hypertable_create_time_prev(fcinfo, true); } diff --git a/tsl/src/data_node.c b/tsl/src/data_node.c index 45349590b77..e702894d969 100644 --- a/tsl/src/data_node.c +++ b/tsl/src/data_node.c @@ -857,18 +857,37 @@ data_node_add_internal(PG_FUNCTION_ARGS, bool set_distid) Datum data_node_add(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("adding data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif return data_node_add_internal(fcinfo, true); } Datum data_node_add_without_dist_id(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("adding data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif return data_node_add_internal(fcinfo, false); } Datum data_node_attach(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("attaching data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *node_name = PG_ARGISNULL(0) ? NULL : PG_GETARG_CSTRING(0); Oid table_id = PG_GETARG_OID(1); bool if_not_attached = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); @@ -1404,6 +1423,13 @@ data_node_block_new_chunks(PG_FUNCTION_ARGS) Datum data_node_detach(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("detaching data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *node_name = PG_ARGISNULL(0) ? NULL : NameStr(*PG_GETARG_NAME(0)); Oid table_id = PG_ARGISNULL(1) ? InvalidOid : PG_GETARG_OID(1); bool all_hypertables = PG_ARGISNULL(1); @@ -1601,6 +1627,13 @@ append_data_node_option(List *new_options, List **current_options, const char *n Datum data_node_alter(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("alter data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *node_name = PG_ARGISNULL(0) ? NULL : NameStr(*PG_GETARG_NAME(0)); const char *host = PG_ARGISNULL(1) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(1)); const char *database = PG_ARGISNULL(2) ? NULL : NameStr(*PG_GETARG_NAME(2)); @@ -1826,6 +1859,13 @@ drop_data_node_database(const ForeignServer *server) Datum data_node_delete(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("deleting data node is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *node_name = PG_ARGISNULL(0) ? NULL : PG_GETARG_CSTRING(0); bool if_exists = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1); bool force = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); diff --git a/tsl/src/dist_backup.c b/tsl/src/dist_backup.c index 7d90d42db3b..bc0c87995ed 100644 --- a/tsl/src/dist_backup.c +++ b/tsl/src/dist_backup.c @@ -65,6 +65,13 @@ create_restore_point_datum(TupleDesc tupdesc, const char *node_name, XLogRecPtr Datum create_distributed_restore_point(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("creating distributed restore point is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *name = TextDatumGetCString(PG_GETARG_DATUM(0)); DistCmdResult *result_cmd; FuncCallContext *funcctx; diff --git a/tsl/src/remote/dist_commands.c b/tsl/src/remote/dist_commands.c index 6f335f0587b..2a296021a81 100644 --- a/tsl/src/remote/dist_commands.c +++ b/tsl/src/remote/dist_commands.c @@ -523,6 +523,13 @@ ts_dist_cmd_close_prepared_command(PreparedDistCmd *command) Datum ts_dist_cmd_exec(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("distributed command execution is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + const char *query = PG_ARGISNULL(0) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(0)); ArrayType *data_nodes = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1); bool transactional = PG_ARGISNULL(2) ? true : PG_GETARG_BOOL(2); diff --git a/tsl/test/expected/cagg_ddl_dist_ht-13.out b/tsl/test/expected/cagg_ddl_dist_ht-13.out deleted file mode 100644 index 9f782583746..00000000000 --- a/tsl/test/expected/cagg_ddl_dist_ht-13.out +++ /dev/null @@ -1,2207 +0,0 @@ --- 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. ------------------------------------- --- Set up a distributed environment ------------------------------------- -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created ------------------------+-----------------------+--------------+------------------+------------------- - db_cagg_ddl_dist_ht_1 | db_cagg_ddl_dist_ht_1 | t | t | t - db_cagg_ddl_dist_ht_2 | db_cagg_ddl_dist_ht_2 | t | t | t - db_cagg_ddl_dist_ht_3 | db_cagg_ddl_dist_ht_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -\set IS_DISTRIBUTED TRUE -\ir include/cagg_ddl_common.sql --- 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. --- Set this variable to avoid using a hard-coded path each time query --- results are compared -\set QUERY_RESULT_TEST_EQUAL_RELPATH '../../../../test/sql/include/query_result_test_equal.sql' -\if :IS_DISTRIBUTED -\echo 'Running distributed hypertable tests' -Running distributed hypertable tests -\else -\echo 'Running local hypertable tests' -\endif -SET ROLE :ROLE_DEFAULT_PERM_USER; ---DDL commands on continuous aggregates -CREATE TABLE conditions ( - timec TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature integer NULL, - humidity DOUBLE PRECISION NULL, - timemeasure TIMESTAMPTZ, - timeinterval INTERVAL -); -\if :IS_DISTRIBUTED -SELECT table_name FROM create_distributed_hypertable('conditions', 'timec', replication_factor => 2); - table_name ------------- - conditions -(1 row) - -\else -SELECT table_name FROM create_hypertable('conditions', 'timec'); -\endif --- schema tests -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; -CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH; -CREATE SCHEMA rename_schema; -GRANT ALL ON SCHEMA rename_schema TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE foo(time TIMESTAMPTZ NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('foo', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (2,public,foo,t) -(1 row) - -\else -SELECT create_hypertable('foo', 'time'); -\endif -CREATE MATERIALIZED VIEW rename_test - WITH ( timescaledb.continuous, timescaledb.materialized_only=true) -AS SELECT time_bucket('1week', time), COUNT(data) - FROM foo - GROUP BY 1 WITH NO DATA; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - public | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test SET SCHEMA rename_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - rename_schema | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -SELECT ca.raw_hypertable_id as "RAW_HYPERTABLE_ID", - h.schema_name AS "MAT_SCHEMA_NAME", - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema as "PART_VIEW_SCHEMA", - direct_view_name as "DIR_VIEW_NAME", - direct_view_schema as "DIR_VIEW_SCHEMA" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'rename_test' -\gset -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"PART_VIEW_SCHEMA".:"PART_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - rename_schema | rename_test | public | _partial_view_3 -(1 row) - ---alter direct view schema -SELECT user_view_schema, user_view_name, direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | direct_view_schema | direct_view_name -------------------+----------------+-----------------------+------------------ - rename_schema | rename_test | _timescaledb_internal | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"DIR_VIEW_SCHEMA".:"DIR_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - rename_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA rename_schema RENAME TO new_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -ALTER VIEW :"PART_VIEW_NAME" SET SCHEMA new_name_schema; -ALTER VIEW :"DIR_VIEW_NAME" SET SCHEMA new_name_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | new_name_schema | _partial_view_3 | new_name_schema | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA new_name_schema RENAME TO foo_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - foo_name_schema | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW foo_name_schema.rename_test SET SCHEMA public; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA foo_name_schema RENAME TO rename_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | rename_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test RENAME TO rename_c_aggregate; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+--------------------+---------------------+------------------- - public | rename_c_aggregate | rename_schema | _partial_view_3 -(1 row) - -SELECT * FROM rename_c_aggregate; - time_bucket | count --------------+------- -(0 rows) - -ALTER VIEW rename_schema.:"PART_VIEW_NAME" RENAME TO partial_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | _direct_view_3 -(1 row) - ---rename direct view -ALTER VIEW rename_schema.:"DIR_VIEW_NAME" RENAME TO direct_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | direct_view -(1 row) - --- drop_chunks tests -DROP TABLE conditions CASCADE; -DROP TABLE foo CASCADE; -psql:include/cagg_ddl_common.sql:161: NOTICE: drop cascades to 2 other objects -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_id - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_id - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), COUNT(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 29) AS i; --- Only refresh up to bucket 15 initially. Matches the old refresh --- behavior that didn't materialize everything -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- cannot drop directly from the materialization table without specifying --- cont. aggregate view name explicitly -\set ON_ERROR_STOP 0 -SELECT drop_chunks(:'drop_chunks_mat_table', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:213: ERROR: operation not supported on materialized hypertable -\set ON_ERROR_STOP 1 -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- drop chunks when the chunksize and time_bucket aren't aligned -DROP TABLE drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to table _timescaledb_internal._hyper_5_4_chunk -CREATE TABLE drop_chunks_table_u(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_distributed_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table_u', 'integer_now_test1'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('3', time), COUNT(data) - FROM drop_chunks_table_u - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table_u, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_u_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_u_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table_u SELECT i, i FROM generate_series(0, 21) AS i; --- Refresh up to bucket 15 to match old materializer behavior -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table_u') AS c; - count -------- - 4 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - --- TRUNCATE test --- Can truncate regular hypertables that have caggs -TRUNCATE drop_chunks_table_u; -\set ON_ERROR_STOP 0 --- Can't truncate materialized hypertables directly -TRUNCATE :drop_chunks_mat_table_u; -psql:include/cagg_ddl_common.sql:271: ERROR: cannot TRUNCATE a hypertable underlying a continuous aggregate -\set ON_ERROR_STOP 1 --- Check that we don't interfere with TRUNCATE of normal table and --- partitioned table -CREATE TABLE truncate (value int); -INSERT INTO truncate VALUES (1), (2); -TRUNCATE truncate; -SELECT * FROM truncate; - value -------- -(0 rows) - -CREATE TABLE truncate_partitioned (value int) - PARTITION BY RANGE(value); -CREATE TABLE truncate_p1 PARTITION OF truncate_partitioned - FOR VALUES FROM (1) TO (3); -INSERT INTO truncate_partitioned VALUES (1), (2); -TRUNCATE truncate_partitioned; -SELECT * FROM truncate_partitioned; - value -------- -(0 rows) - --- ALTER TABLE tests -\set ON_ERROR_STOP 0 --- test a variety of ALTER TABLE statements -ALTER TABLE :drop_chunks_mat_table_u RENAME time_bucket TO bad_name; -psql:include/cagg_ddl_common.sql:291: ERROR: renaming columns on materialization tables is not supported -ALTER TABLE :drop_chunks_mat_table_u ADD UNIQUE(time_bucket); -psql:include/cagg_ddl_common.sql:292: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET UNLOGGED; -psql:include/cagg_ddl_common.sql:293: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ENABLE ROW LEVEL SECURITY; -psql:include/cagg_ddl_common.sql:294: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ADD COLUMN fizzle INTEGER; -psql:include/cagg_ddl_common.sql:295: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DROP COLUMN time_bucket; -psql:include/cagg_ddl_common.sql:296: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket DROP NOT NULL; -psql:include/cagg_ddl_common.sql:297: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET DEFAULT 1; -psql:include/cagg_ddl_common.sql:298: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET STORAGE EXTERNAL; -psql:include/cagg_ddl_common.sql:299: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DISABLE TRIGGER ALL; -psql:include/cagg_ddl_common.sql:300: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET TABLESPACE foo; -psql:include/cagg_ddl_common.sql:301: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u NOT OF; -psql:include/cagg_ddl_common.sql:302: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u OWNER TO CURRENT_USER; -psql:include/cagg_ddl_common.sql:303: ERROR: operation not supported on materialization tables -\set ON_ERROR_STOP 1 -ALTER TABLE :drop_chunks_mat_table_u SET SCHEMA public; -ALTER TABLE :drop_chunks_mat_table_u_name RENAME TO new_name; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT * FROM new_name; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -\set ON_ERROR_STOP 0 --- no continuous aggregates on a continuous aggregate materialization table -CREATE MATERIALIZED VIEW new_name_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('6', time_bucket), COUNT("count") - FROM new_name - GROUP BY 1 WITH NO DATA; -psql:include/cagg_ddl_common.sql:326: ERROR: hypertable is a continuous aggregate materialization table -\set ON_ERROR_STOP 1 -CREATE TABLE metrics(time timestamptz NOT NULL, device_id int, v1 float, v2 float); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('metrics', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (8,public,metrics,t) -(1 row) - -\else -SELECT create_hypertable('metrics','time'); -\endif -INSERT INTO metrics SELECT generate_series('2000-01-01'::timestamptz,'2000-01-10','1m'),1,0.25,0.75; --- check expressions in view definition -CREATE MATERIALIZED VIEW cagg_expr - WITH (timescaledb.continuous, timescaledb.materialized_only=true) -AS -SELECT - time_bucket('1d', time) AS time, - 'Const'::text AS Const, - 4.3::numeric AS "numeric", - first(metrics,time), - CASE WHEN true THEN 'foo' ELSE 'bar' END, - COALESCE(NULL,'coalesce'), - avg(v1) + avg(v2) AS avg1, - avg(v1+v2) AS avg2 -FROM metrics -GROUP BY 1 WITH NO DATA; -CALL refresh_continuous_aggregate('cagg_expr', NULL, NULL); -SELECT * FROM cagg_expr ORDER BY time LIMIT 5; - time | const | numeric | first | case | coalesce | avg1 | avg2 -------------------------------+-------+---------+----------------------------------------------+------+----------+------+------ - Fri Dec 31 16:00:00 1999 PST | Const | 4.3 | ("Sat Jan 01 00:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sat Jan 01 16:00:00 2000 PST | Const | 4.3 | ("Sat Jan 01 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sun Jan 02 16:00:00 2000 PST | Const | 4.3 | ("Sun Jan 02 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Mon Jan 03 16:00:00 2000 PST | Const | 4.3 | ("Mon Jan 03 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Tue Jan 04 16:00:00 2000 PST | Const | 4.3 | ("Tue Jan 04 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 -(5 rows) - ---test materialization of invalidation before drop -DROP TABLE IF EXISTS drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:358: NOTICE: table "drop_chunks_table" does not exist, skipping -DROP TABLE IF EXISTS drop_chunks_table_u CASCADE; -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to table _timescaledb_internal._hyper_7_9_chunk -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test2'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), max(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---dropping chunks will process the invalidations -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk -(1 row) - -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 10 | 10 -(1 row) - -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(20, 35) AS i; -CALL refresh_continuous_aggregate('drop_chunks_view', 10, 40); ---this will be seen after the drop its within the invalidation window and will be dropped -INSERT INTO drop_chunks_table VALUES (26, 100); ---this will not be processed by the drop since chunk 30-39 is not dropped but will be seen after refresh ---shows that the drop doesn't do more work than necessary -INSERT INTO drop_chunks_table VALUES (31, 200); ---move the time up to 39 -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(35, 39) AS i; ---the chunks and ranges we have thus far -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table'; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(3 rows) - ---the invalidation on 25 not yet seen -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 29 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---refresh to process the invalidations and then drop -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, (integer_now_test2()-9)); -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_14_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - ---new values on 25 now seen in view -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---earliest datapoint now in table -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 30 | 30 -(1 row) - ---we see the chunks row with the dropped flags set; -SELECT id, hypertable_id, schema_name, table_name, compressed_chunk_id, dropped, status, osm_chunk FROM _timescaledb_catalog.chunk where dropped; - id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped | status | osm_chunk -----+---------------+-----------------------+-------------------------+---------------------+---------+--------+----------- - 13 | 10 | _timescaledb_internal | _dist_hyper_10_13_chunk | | t | 0 | f - 14 | 10 | _timescaledb_internal | _dist_hyper_10_14_chunk | | t | 0 | f - 15 | 10 | _timescaledb_internal | _dist_hyper_10_15_chunk | | t | 0 | f -(3 rows) - ---still see data in the view -SELECT * FROM drop_chunks_view WHERE time_bucket < (integer_now_test2()-9) ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(4 rows) - ---no data but covers dropped chunks -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ -(0 rows) - ---recreate the dropped chunk -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---see data from recreated region -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ - 20 | 20 - 19 | 19 - 18 | 18 - 17 | 17 - 16 | 16 - 15 | 15 - 14 | 14 - 13 | 13 - 12 | 12 - 11 | 11 - 10 | 10 - 9 | 9 - 8 | 8 - 7 | 7 - 6 | 6 - 5 | 5 - 4 | 4 - 3 | 3 - 2 | 2 - 1 | 1 - 0 | 0 -(21 rows) - ---should show chunk with old name and old ranges -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(4 rows) - ---We dropped everything up to the bucket starting at 30 and then ---inserted new data up to and including time 20. Therefore, the ---dropped data should stay the same as long as we only refresh ---buckets that have non-dropped data. -CALL refresh_continuous_aggregate('drop_chunks_view', 30, 40); -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 39 - 30 | 200 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_tablen, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_nid - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- TEST drop chunks from continuous aggregates by specifying view name -SELECT drop_chunks('drop_chunks_view', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:454: INFO: dropping chunk _timescaledb_internal._hyper_11_17_chunk - drop_chunks ------------------------------------------- - _timescaledb_internal._hyper_11_17_chunk -(1 row) - --- Test that we cannot drop chunks when specifying materialized --- hypertable -INSERT INTO drop_chunks_table SELECT generate_series(45, 55), 500; -CALL refresh_continuous_aggregate('drop_chunks_view', 45, 55); -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = :'drop_chunks_mat_table_name' ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer ---------------------+---------------------+------------------- - _hyper_11_20_chunk | 0 | 100 -(1 row) - -\set ON_ERROR_STOP 0 -\set VERBOSITY default -SELECT drop_chunks(:'drop_chunks_mat_tablen', older_than => 60); -psql:include/cagg_ddl_common.sql:466: ERROR: operation not supported on materialized hypertable -DETAIL: Hypertable "_materialized_hypertable_11" is a materialized hypertable. -HINT: Try the operation on the continuous aggregate instead. -\set VERBOSITY terse -\set ON_ERROR_STOP 1 ------------------------------------------------------------------ --- Test that refresh_continuous_aggregate on chunk will refresh, --- but only in the regions covered by the show chunks. ------------------------------------------------------------------ -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(6 rows) - --- Pick the second chunk as the one to drop -WITH numbered_chunks AS ( - SELECT row_number() OVER (ORDER BY range_start_integer), chunk_schema, chunk_name, range_start_integer, range_end_integer - FROM timescaledb_information.chunks - WHERE hypertable_name = 'drop_chunks_table' - ORDER BY 1 -) -SELECT format('%I.%I', chunk_schema, chunk_name) AS chunk_to_drop, range_start_integer, range_end_integer -FROM numbered_chunks -WHERE row_number = 2 \gset --- There's data in the table for the chunk/range we will drop -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 -(10 rows) - --- Make sure there is also data in the continuous aggregate --- CARE: --- Note that this behaviour of dropping the materialization table chunks and expecting a refresh --- that overlaps that time range to NOT update those chunks is undefined. Since CAGGs over --- distributed hypertables merge the invalidations the refresh region is updated in the distributed --- case, which may be different than what happens in the normal hypertable case. The command was: --- SELECT drop_chunks('drop_chunks_view', newer_than => -20, verbose => true); -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 50); -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 9 - 10 | 14 - 15 | 19 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(9 rows) - --- Drop the second chunk, to leave a gap in the data -\if :IS_DISTRIBUTED -CALL distributed_exec(format('DROP TABLE IF EXISTS %s', :'chunk_to_drop')); -DROP FOREIGN TABLE :chunk_to_drop; -\else -DROP TABLE :chunk_to_drop; -\endif --- Verify that the second chunk is dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(5 rows) - --- Data is no longer in the table but still in the view -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ -(0 rows) - -SELECT * FROM drop_chunks_view -WHERE time_bucket >= :range_start_integer -AND time_bucket < :range_end_integer -ORDER BY 1; - time_bucket | max --------------+----- - 10 | 14 - 15 | 19 -(2 rows) - --- Insert a large value in one of the chunks that will be dropped -INSERT INTO drop_chunks_table VALUES (:range_start_integer-1, 100); --- Now refresh and drop the two adjecent chunks -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, 30); -SELECT drop_chunks('drop_chunks_table', older_than=>30); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - --- Verify that the chunks are dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(3 rows) - --- The continuous aggregate should be refreshed in the regions covered --- by the dropped chunks, but not in the "gap" region, i.e., the --- region of the chunk that was dropped via DROP TABLE. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 100 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(7 rows) - --- Now refresh in the region of the first two dropped chunks -CALL refresh_continuous_aggregate('drop_chunks_view', 0, :range_end_integer); --- Aggregate data in the refreshed range should no longer exist since --- the underlying data was dropped. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(5 rows) - --------------------------------------------------------------------- --- Check that we can create a materialized table in a tablespace. We --- create one with tablespace and one without and compare them. -CREATE VIEW cagg_info AS -WITH - caggs AS ( - SELECT format('%I.%I', user_view_schema, user_view_name)::regclass AS user_view, - format('%I.%I', direct_view_schema, direct_view_name)::regclass AS direct_view, - format('%I.%I', partial_view_schema, partial_view_name)::regclass AS partial_view, - format('%I.%I', ht.schema_name, ht.table_name)::regclass AS mat_relid - FROM _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.continuous_agg cagg - WHERE ht.id = cagg.mat_hypertable_id - ) -SELECT user_view, - pg_get_userbyid(relowner) AS user_view_owner, - relname AS mat_table, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = mat_relid) AS mat_table_owner, - direct_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = direct_view) AS direct_view_owner, - partial_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = partial_view) AS partial_view_owner, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class JOIN caggs ON pg_class.oid = caggs.mat_relid; -GRANT SELECT ON cagg_info TO PUBLIC; -CREATE VIEW chunk_info AS -SELECT ht.schema_name, ht.table_name, relname AS chunk_name, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class c, - _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.chunk ch - WHERE ch.table_name = c.relname AND ht.id = ch.hypertable_id; -CREATE TABLE whatever(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS whatever_nid - FROM create_distributed_hypertable('whatever', 'time', chunk_time_interval => 10, replication_factor => 2) -\gset -\else -SELECT hypertable_id AS whatever_nid - FROM create_hypertable('whatever', 'time', chunk_time_interval => 10) -\gset -\endif -SELECT set_integer_now_func('whatever', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW whatever_view_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -CREATE MATERIALIZED VIEW whatever_view_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) -TABLESPACE tablespace1 AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -INSERT INTO whatever SELECT i, i FROM generate_series(0, 29) AS i; -CALL refresh_continuous_aggregate('whatever_view_1', NULL, NULL); -CALL refresh_continuous_aggregate('whatever_view_2', NULL, NULL); -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | | _hyper_13_24_chunk | - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -ALTER MATERIALIZED VIEW whatever_view_1 SET TABLESPACE tablespace2; -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | tablespace2 | _hyper_13_24_chunk | tablespace2 - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -DROP MATERIALIZED VIEW whatever_view_1; -psql:include/cagg_ddl_common.sql:644: NOTICE: drop cascades to table _timescaledb_internal._hyper_13_24_chunk -DROP MATERIALIZED VIEW whatever_view_2; -psql:include/cagg_ddl_common.sql:645: NOTICE: drop cascades to table _timescaledb_internal._hyper_14_25_chunk --- test bucket width expressions on integer hypertables -CREATE TABLE metrics_int2 ( - time int2 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int4 ( - time int4 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int8 ( - time int8 NOT NULL, - device_id int, - v1 float, - v2 float -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10, replication_factor => 2) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - create_distributed_hypertable -------------------------------- - (15,public,metrics_int2,t) - (16,public,metrics_int4,t) - (17,public,metrics_int8,t) -(3 rows) - -\else -SELECT create_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); -\endif -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -$DIST$); -\endif -SELECT set_integer_now_func (('metrics_' || dt)::regclass, (dt || '_now')::regproc) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - set_integer_now_func ----------------------- - - - -(3 rows) - --- width expression for int2 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:750: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint + 2::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:757: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int4 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:765: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:772: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int8 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:780: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:787: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -\set ON_ERROR_STOP 0 --- non-immutable expresions should be rejected -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:796: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:801: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:806: ERROR: only immutable expressions allowed in time bucket function -\set ON_ERROR_STOP 1 --- Test various ALTER MATERIALIZED VIEW statements. -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE MATERIALIZED VIEW owner_check WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1 -WITH NO DATA; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | default_perm_user -mat_table | _materialized_hypertable_24 -mat_table_owner | default_perm_user -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | default_perm_user -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | default_perm_user -tablespace | - -\x off --- This should not work since the target user has the wrong role, but --- we test that the normal checks are done when changing the owner. -\set ON_ERROR_STOP 0 -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -psql:include/cagg_ddl_common.sql:826: ERROR: must be member of role "test_role_1" -\set ON_ERROR_STOP 1 --- Superuser can always change owner -SET ROLE :ROLE_CLUSTER_SUPERUSER; -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | test_role_1 -mat_table | _materialized_hypertable_24 -mat_table_owner | test_role_1 -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | test_role_1 -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | test_role_1 -tablespace | - -\x off --- --- Test drop continuous aggregate cases --- --- Issue: #2608 --- -CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS -$BODY$ - SELECT 50; -$BODY$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ - CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS - $BODY$ - SELECT 50; - $BODY$; -$DIST$); -\endif -CREATE TABLE conditionsnm(time_int INT NOT NULL, device INT, value FLOAT); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10, replication_factor => 2); - create_distributed_hypertable -------------------------------- - (25,public,conditionsnm,t) -(1 row) - -\else -SELECT create_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10); -\endif -SELECT set_integer_now_func('conditionsnm', 'test_int_now'); - set_integer_now_func ----------------------- - -(1 row) - -INSERT INTO conditionsnm -SELECT time_val, time_val % 4, 3.14 FROM generate_series(0,100,1) AS time_val; --- Case 1: DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:874: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4; -psql:include/cagg_ddl_common.sql:876: NOTICE: drop cascades to table _timescaledb_internal._hyper_26_37_chunk --- Case 2: DROP CASCADE should have similar behaviour as DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:884: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:886: NOTICE: drop cascades to table _timescaledb_internal._hyper_27_38_chunk --- Case 3: require CASCADE in case of dependent object -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:894: NOTICE: refreshing continuous aggregate "conditionsnm_4" -CREATE VIEW see_cagg as select * from conditionsnm_4; -\set ON_ERROR_STOP 0 -DROP MATERIALIZED VIEW conditionsnm_4; -psql:include/cagg_ddl_common.sql:898: ERROR: cannot drop view conditionsnm_4 because other objects depend on it -\set ON_ERROR_STOP 1 --- Case 4: DROP CASCADE with dependency -DROP MATERIALIZED VIEW conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to view see_cagg -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to table _timescaledb_internal._hyper_28_39_chunk --- Test DROP SCHEMA CASCADE with continuous aggregates --- --- Issue: #2350 --- --- Case 1: DROP SCHEMA CASCADE -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (29,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.telemetry_1s - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'telemetry_1s'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME | PART_VIEW_NAME | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 29 | _timescaledb_internal | _materialized_hypertable_30 | _partial_view_30 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:941: NOTICE: drop cascades to 4 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'telemetry_1s'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - --- Case 2: DROP SCHEMA CASCADE with multiple caggs -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (31,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.cagg1 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -CREATE MATERIALIZED VIEW test_schema.cagg2 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME1", - partial_view_name as "PART_VIEW_NAME1", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg1'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME1 | PART_VIEW_NAME1 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_32 | _partial_view_32 | _timescaledb_internal -(1 row) - -\gset -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME2", - partial_view_name as "PART_VIEW_NAME2", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg2'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME2 | PART_VIEW_NAME2 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_33 | _partial_view_33 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:998: NOTICE: drop cascades to 7 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - -DROP TABLESPACE tablespace1; -DROP TABLESPACE tablespace2; --- Check that we can rename a column of a materialized view and still --- rebuild it after (#3051, #3405) -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (34,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -SELECT format('%I.%I', '_timescaledb_internal', h.table_name) AS "MAT_TABLE_NAME", - format('%I.%I', '_timescaledb_internal', partial_view_name) AS "PART_VIEW_NAME", - format('%I.%I', '_timescaledb_internal', direct_view_name) AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'conditions_daily' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns('conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | t - avg | double precision | f -(3 rows) - -ALTER MATERIALIZED VIEW conditions_daily RENAME COLUMN bucket to "time"; --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns(' conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | t - avg | double precision | f -(3 rows) - --- This will rebuild the materialized view and should succeed. -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only = false); --- Refresh the continuous aggregate to check that it works after the --- rename. -\set VERBOSITY verbose -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -\set VERBOSITY terse --- --- Indexes on continuous aggregate --- -\set ON_ERROR_STOP 0 --- unique indexes are not supported -CREATE UNIQUE INDEX index_unique_error ON conditions_daily ("time", location); -psql:include/cagg_ddl_common.sql:1084: ERROR: continuous aggregates do not support UNIQUE indexes --- concurrently index creation not supported -CREATE INDEX CONCURRENTLY index_concurrently_avg ON conditions_daily (avg); -psql:include/cagg_ddl_common.sql:1086: ERROR: hypertables do not support concurrent index creation -\set ON_ERROR_STOP 1 -CREATE INDEX index_avg ON conditions_daily (avg); -CREATE INDEX index_avg_only ON ONLY conditions_daily (avg); -CREATE INDEX index_avg_include ON conditions_daily (avg) INCLUDE (location); -CREATE INDEX index_avg_expr ON conditions_daily ((avg + 1)); -CREATE INDEX index_avg_location_sfo ON conditions_daily (avg) WHERE location = 'SFO'; -CREATE INDEX index_avg_expr_location_sfo ON conditions_daily ((avg + 2)) WHERE location = 'SFO'; -SELECT * FROM test.show_indexespred(:'MAT_TABLE_NAME'); - Index | Columns | Expr | Pred | Unique | Primary | Exclusion | Tablespace ------------------------------------------------------------------------+-------------------+---------------------------+------------------------+--------+---------+-----------+------------ - _timescaledb_internal._materialized_hypertable_35_bucket_idx | {bucket} | | | f | f | f | - _timescaledb_internal._materialized_hypertable_35_location_bucket_idx | {location,bucket} | | | f | f | f | - _timescaledb_internal.index_avg | {avg} | | | f | f | f | - _timescaledb_internal.index_avg_expr | {expr} | avg + 1::double precision | | f | f | f | - _timescaledb_internal.index_avg_expr_location_sfo | {expr} | avg + 2::double precision | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_include | {avg,location} | | | f | f | f | - _timescaledb_internal.index_avg_location_sfo | {avg} | | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_only | {avg} | | | f | f | f | -(8 rows) - --- #3696 assertion failure when referencing columns not present in result -CREATE TABLE i3696(time timestamptz NOT NULL, search_query text, cnt integer, cnt2 integer); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('i3696', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (36,public,i3696,t) -(1 row) - -\else -SELECT table_name FROM create_hypertable('i3696','time'); -\endif -CREATE MATERIALIZED VIEW i3696_cagg1 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt +cnt2 , bucket, search_query; -psql:include/cagg_ddl_common.sql:1108: NOTICE: continuous aggregate "i3696_cagg1" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg1 SET (timescaledb.materialized_only = 'true'); -CREATE MATERIALIZED VIEW i3696_cagg2 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt + cnt2, bucket, search_query - HAVING cnt + cnt2 + sum(cnt) > 2 or count(cnt2) > 10; -psql:include/cagg_ddl_common.sql:1116: NOTICE: continuous aggregate "i3696_cagg2" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg2 SET (timescaledb.materialized_only = 'true'); ---TEST test with multiple settings on continuous aggregates -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -CREATE TABLE test_setting(time timestamptz not null, val numeric); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_setting', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (39,public,test_setting,t) -(1 row) - -\else -SELECT create_hypertable('test_setting', 'time'); -\endif -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only=false) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1130: NOTICE: continuous aggregate "test_setting_cagg" is already up-to-date -INSERT INTO test_setting -SELECT generate_series( '2020-01-10 8:00'::timestamp, '2020-01-30 10:00+00'::timestamptz, '1 day'::interval), 10.0; -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1141: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1149: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -DELETE FROM test_setting WHERE val = 20; ---TEST test with multiple settings on continuous aggregates with real time aggregates turned off initially -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -DROP MATERIALIZED VIEW test_setting_cagg; -psql:include/cagg_ddl_common.sql:1174: NOTICE: drop cascades to table _timescaledb_internal._hyper_40_47_chunk -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only = true) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1177: NOTICE: refreshing continuous aggregate "test_setting_cagg" -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1185: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1193: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - --- END TEST with multiple settings --- Test View Target Entries that contain both aggrefs and Vars in the same expression -CREATE TABLE transactions -( - "time" timestamp with time zone NOT NULL, - dummy1 integer, - dummy2 integer, - dummy3 integer, - dummy4 integer, - dummy5 integer, - amount integer, - fiat_value integer -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('transactions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (46,public,transactions,t) -(1 row) - -\else -SELECT create_hypertable('transactions', 'time'); -\endif -INSERT INTO transactions VALUES ( '2018-01-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:30:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:20:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 10:40:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 11:50:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 12:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 13:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 10:30:00-08', 0, 0, 0, 0, 0, -1, 10); -CREATE materialized view cashflows( - bucket, - amount, - cashflow, - cashflow2 -) WITH ( - timescaledb.continuous, - timescaledb.materialized_only = true -) AS -SELECT time_bucket ('1 day', time) AS bucket, - amount, - CASE - WHEN amount < 0 THEN (0 - sum(fiat_value)) - ELSE sum(fiat_value) - END AS cashflow, - amount + sum(fiat_value) -FROM transactions -GROUP BY bucket, amount; -psql:include/cagg_ddl_common.sql:1267: NOTICE: refreshing continuous aggregate "cashflows" -SELECT h.table_name AS "MAT_TABLE_NAME", - partial_view_name AS "PART_VIEW_NAME", - direct_view_name AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cashflows' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -\d+ "_timescaledb_internal".:"DIRECT_VIEW_NAME" - View "_timescaledb_internal._direct_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"PART_VIEW_NAME" - View "_timescaledb_internal._partial_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"MAT_TABLE_NAME" - Table "_timescaledb_internal._materialized_hypertable_47" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ------------+--------------------------+-----------+----------+---------+---------+--------------+------------- - bucket | timestamp with time zone | | not null | | plain | | - amount | integer | | | | plain | | - cashflow | bigint | | | | plain | | - cashflow2 | bigint | | | | plain | | -Indexes: - "_materialized_hypertable_47_amount_bucket_idx" btree (amount, bucket DESC) - "_materialized_hypertable_47_bucket_idx" btree (bucket DESC) -Triggers: - ts_insert_blocker BEFORE INSERT ON _timescaledb_internal._materialized_hypertable_47 FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker() -Child tables: _timescaledb_internal._hyper_47_52_chunk, - _timescaledb_internal._hyper_47_53_chunk - -\d+ 'cashflows' - View "public.cashflows" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT _materialized_hypertable_47.bucket, - _materialized_hypertable_47.amount, - _materialized_hypertable_47.cashflow, - _materialized_hypertable_47.cashflow2 - FROM _timescaledb_internal._materialized_hypertable_47; - -SELECT * FROM cashflows; - bucket | amount | cashflow | cashflow2 -------------------------------+--------+----------+----------- - Sun Dec 31 16:00:00 2017 PST | 1 | 10 | 11 - Mon Jan 01 16:00:00 2018 PST | -1 | -30 | 29 - Wed Oct 31 17:00:00 2018 PDT | -1 | -20 | 19 - Wed Oct 31 17:00:00 2018 PDT | 1 | 30 | 31 - Thu Nov 01 17:00:00 2018 PDT | -1 | -10 | 9 - Thu Nov 01 17:00:00 2018 PDT | 1 | 10 | 11 -(6 rows) - --- test cagg creation with named arguments in time_bucket --- note that positional arguments cannot follow named arguments --- 1. test named origin --- 2. test named timezone --- 3. test named ts --- 4. test named bucket width --- named origin -CREATE MATERIALIZED VIEW cagg_named_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named timezone -CREATE MATERIALIZED VIEW cagg_named_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named ts -CREATE MATERIALIZED VIEW cagg_named_ts_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named bucket width -CREATE MATERIALIZED VIEW cagg_named_all WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(bucket_width => '1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- Refreshing from the beginning (NULL) of a CAGG with variable time bucket and --- using an INTERVAL for the end timestamp (issue #5534) -CREATE MATERIALIZED VIEW transactions_montly -WITH (timescaledb.continuous, timescaledb.materialized_only = true) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) - FROM transactions -GROUP BY 1 -WITH NO DATA; --- No rows -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min ---------+-----+-----+----- -(0 rows) - --- Refresh from beginning of the CAGG for 1 month -CALL refresh_continuous_aggregate('transactions_montly', NULL, INTERVAL '1 month'); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - -TRUNCATE transactions_montly; --- Partial refresh the CAGG from beginning to an specific timestamp -CALL refresh_continuous_aggregate('transactions_montly', NULL, '2018-11-01 11:50:00-08'::timestamptz); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 -(1 row) - --- Full refresh the CAGG -CALL refresh_continuous_aggregate('transactions_montly', NULL, NULL); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - --- Check set_chunk_time_interval on continuous aggregate -CREATE MATERIALIZED VIEW cagg_set_chunk_time_interval -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) -FROM transactions -GROUP BY 1 -WITH NO DATA; -SELECT set_chunk_time_interval('cagg_set_chunk_time_interval', chunk_time_interval => interval '1 month'); - set_chunk_time_interval -------------------------- - -(1 row) - -CALL refresh_continuous_aggregate('cagg_set_chunk_time_interval', NULL, NULL); -SELECT _timescaledb_functions.to_interval(d.interval_length) = interval '1 month' -FROM _timescaledb_catalog.dimension d - RIGHT JOIN _timescaledb_catalog.continuous_agg ca ON ca.user_view_name = 'cagg_set_chunk_time_interval' -WHERE d.hypertable_id = ca.mat_hypertable_id; - ?column? ----------- - t -(1 row) - --- Since #6077 CAggs are materialized only by default -DROP TABLE conditions CASCADE; -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 3 other objects -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 2 other objects -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (54,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - --- Should return NO ROWS -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+--------+----- -(0 rows) - -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=false); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55 - WHERE _materialized_hypertable_55.bucket < COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) -UNION ALL - SELECT conditions.location, - time_bucket('@ 1 day'::interval, conditions."time") AS bucket, - avg(conditions.temperature) AS avg - FROM conditions - WHERE conditions."time" >= COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) - GROUP BY conditions.location, (time_bucket('@ 1 day'::interval, conditions."time")); - --- Should return ROWS because now it is realtime -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- Should return ROWS because we refreshed it -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=true); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- cleanup -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); diff --git a/tsl/test/expected/cagg_ddl_dist_ht-14.out b/tsl/test/expected/cagg_ddl_dist_ht-14.out deleted file mode 100644 index 8c4e1394a03..00000000000 --- a/tsl/test/expected/cagg_ddl_dist_ht-14.out +++ /dev/null @@ -1,2207 +0,0 @@ --- 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. ------------------------------------- --- Set up a distributed environment ------------------------------------- -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created ------------------------+-----------------------+--------------+------------------+------------------- - db_cagg_ddl_dist_ht_1 | db_cagg_ddl_dist_ht_1 | t | t | t - db_cagg_ddl_dist_ht_2 | db_cagg_ddl_dist_ht_2 | t | t | t - db_cagg_ddl_dist_ht_3 | db_cagg_ddl_dist_ht_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -\set IS_DISTRIBUTED TRUE -\ir include/cagg_ddl_common.sql --- 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. --- Set this variable to avoid using a hard-coded path each time query --- results are compared -\set QUERY_RESULT_TEST_EQUAL_RELPATH '../../../../test/sql/include/query_result_test_equal.sql' -\if :IS_DISTRIBUTED -\echo 'Running distributed hypertable tests' -Running distributed hypertable tests -\else -\echo 'Running local hypertable tests' -\endif -SET ROLE :ROLE_DEFAULT_PERM_USER; ---DDL commands on continuous aggregates -CREATE TABLE conditions ( - timec TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature integer NULL, - humidity DOUBLE PRECISION NULL, - timemeasure TIMESTAMPTZ, - timeinterval INTERVAL -); -\if :IS_DISTRIBUTED -SELECT table_name FROM create_distributed_hypertable('conditions', 'timec', replication_factor => 2); - table_name ------------- - conditions -(1 row) - -\else -SELECT table_name FROM create_hypertable('conditions', 'timec'); -\endif --- schema tests -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; -CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH; -CREATE SCHEMA rename_schema; -GRANT ALL ON SCHEMA rename_schema TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE foo(time TIMESTAMPTZ NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('foo', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (2,public,foo,t) -(1 row) - -\else -SELECT create_hypertable('foo', 'time'); -\endif -CREATE MATERIALIZED VIEW rename_test - WITH ( timescaledb.continuous, timescaledb.materialized_only=true) -AS SELECT time_bucket('1week', time), COUNT(data) - FROM foo - GROUP BY 1 WITH NO DATA; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - public | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test SET SCHEMA rename_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - rename_schema | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -SELECT ca.raw_hypertable_id as "RAW_HYPERTABLE_ID", - h.schema_name AS "MAT_SCHEMA_NAME", - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema as "PART_VIEW_SCHEMA", - direct_view_name as "DIR_VIEW_NAME", - direct_view_schema as "DIR_VIEW_SCHEMA" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'rename_test' -\gset -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"PART_VIEW_SCHEMA".:"PART_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - rename_schema | rename_test | public | _partial_view_3 -(1 row) - ---alter direct view schema -SELECT user_view_schema, user_view_name, direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | direct_view_schema | direct_view_name -------------------+----------------+-----------------------+------------------ - rename_schema | rename_test | _timescaledb_internal | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"DIR_VIEW_SCHEMA".:"DIR_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - rename_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA rename_schema RENAME TO new_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -ALTER VIEW :"PART_VIEW_NAME" SET SCHEMA new_name_schema; -ALTER VIEW :"DIR_VIEW_NAME" SET SCHEMA new_name_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | new_name_schema | _partial_view_3 | new_name_schema | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA new_name_schema RENAME TO foo_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - foo_name_schema | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW foo_name_schema.rename_test SET SCHEMA public; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA foo_name_schema RENAME TO rename_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | rename_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test RENAME TO rename_c_aggregate; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+--------------------+---------------------+------------------- - public | rename_c_aggregate | rename_schema | _partial_view_3 -(1 row) - -SELECT * FROM rename_c_aggregate; - time_bucket | count --------------+------- -(0 rows) - -ALTER VIEW rename_schema.:"PART_VIEW_NAME" RENAME TO partial_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | _direct_view_3 -(1 row) - ---rename direct view -ALTER VIEW rename_schema.:"DIR_VIEW_NAME" RENAME TO direct_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | direct_view -(1 row) - --- drop_chunks tests -DROP TABLE conditions CASCADE; -DROP TABLE foo CASCADE; -psql:include/cagg_ddl_common.sql:161: NOTICE: drop cascades to 2 other objects -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_id - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_id - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), COUNT(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 29) AS i; --- Only refresh up to bucket 15 initially. Matches the old refresh --- behavior that didn't materialize everything -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- cannot drop directly from the materialization table without specifying --- cont. aggregate view name explicitly -\set ON_ERROR_STOP 0 -SELECT drop_chunks(:'drop_chunks_mat_table', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:213: ERROR: operation not supported on materialized hypertable -\set ON_ERROR_STOP 1 -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- drop chunks when the chunksize and time_bucket aren't aligned -DROP TABLE drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to table _timescaledb_internal._hyper_5_4_chunk -CREATE TABLE drop_chunks_table_u(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_distributed_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table_u', 'integer_now_test1'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('3', time), COUNT(data) - FROM drop_chunks_table_u - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table_u, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_u_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_u_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table_u SELECT i, i FROM generate_series(0, 21) AS i; --- Refresh up to bucket 15 to match old materializer behavior -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table_u') AS c; - count -------- - 4 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - --- TRUNCATE test --- Can truncate regular hypertables that have caggs -TRUNCATE drop_chunks_table_u; -\set ON_ERROR_STOP 0 --- Can't truncate materialized hypertables directly -TRUNCATE :drop_chunks_mat_table_u; -psql:include/cagg_ddl_common.sql:271: ERROR: cannot TRUNCATE a hypertable underlying a continuous aggregate -\set ON_ERROR_STOP 1 --- Check that we don't interfere with TRUNCATE of normal table and --- partitioned table -CREATE TABLE truncate (value int); -INSERT INTO truncate VALUES (1), (2); -TRUNCATE truncate; -SELECT * FROM truncate; - value -------- -(0 rows) - -CREATE TABLE truncate_partitioned (value int) - PARTITION BY RANGE(value); -CREATE TABLE truncate_p1 PARTITION OF truncate_partitioned - FOR VALUES FROM (1) TO (3); -INSERT INTO truncate_partitioned VALUES (1), (2); -TRUNCATE truncate_partitioned; -SELECT * FROM truncate_partitioned; - value -------- -(0 rows) - --- ALTER TABLE tests -\set ON_ERROR_STOP 0 --- test a variety of ALTER TABLE statements -ALTER TABLE :drop_chunks_mat_table_u RENAME time_bucket TO bad_name; -psql:include/cagg_ddl_common.sql:291: ERROR: renaming columns on materialization tables is not supported -ALTER TABLE :drop_chunks_mat_table_u ADD UNIQUE(time_bucket); -psql:include/cagg_ddl_common.sql:292: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET UNLOGGED; -psql:include/cagg_ddl_common.sql:293: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ENABLE ROW LEVEL SECURITY; -psql:include/cagg_ddl_common.sql:294: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ADD COLUMN fizzle INTEGER; -psql:include/cagg_ddl_common.sql:295: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DROP COLUMN time_bucket; -psql:include/cagg_ddl_common.sql:296: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket DROP NOT NULL; -psql:include/cagg_ddl_common.sql:297: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET DEFAULT 1; -psql:include/cagg_ddl_common.sql:298: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET STORAGE EXTERNAL; -psql:include/cagg_ddl_common.sql:299: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DISABLE TRIGGER ALL; -psql:include/cagg_ddl_common.sql:300: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET TABLESPACE foo; -psql:include/cagg_ddl_common.sql:301: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u NOT OF; -psql:include/cagg_ddl_common.sql:302: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u OWNER TO CURRENT_USER; -psql:include/cagg_ddl_common.sql:303: ERROR: operation not supported on materialization tables -\set ON_ERROR_STOP 1 -ALTER TABLE :drop_chunks_mat_table_u SET SCHEMA public; -ALTER TABLE :drop_chunks_mat_table_u_name RENAME TO new_name; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT * FROM new_name; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -\set ON_ERROR_STOP 0 --- no continuous aggregates on a continuous aggregate materialization table -CREATE MATERIALIZED VIEW new_name_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('6', time_bucket), COUNT("count") - FROM new_name - GROUP BY 1 WITH NO DATA; -psql:include/cagg_ddl_common.sql:326: ERROR: hypertable is a continuous aggregate materialization table -\set ON_ERROR_STOP 1 -CREATE TABLE metrics(time timestamptz NOT NULL, device_id int, v1 float, v2 float); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('metrics', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (8,public,metrics,t) -(1 row) - -\else -SELECT create_hypertable('metrics','time'); -\endif -INSERT INTO metrics SELECT generate_series('2000-01-01'::timestamptz,'2000-01-10','1m'),1,0.25,0.75; --- check expressions in view definition -CREATE MATERIALIZED VIEW cagg_expr - WITH (timescaledb.continuous, timescaledb.materialized_only=true) -AS -SELECT - time_bucket('1d', time) AS time, - 'Const'::text AS Const, - 4.3::numeric AS "numeric", - first(metrics,time), - CASE WHEN true THEN 'foo' ELSE 'bar' END, - COALESCE(NULL,'coalesce'), - avg(v1) + avg(v2) AS avg1, - avg(v1+v2) AS avg2 -FROM metrics -GROUP BY 1 WITH NO DATA; -CALL refresh_continuous_aggregate('cagg_expr', NULL, NULL); -SELECT * FROM cagg_expr ORDER BY time LIMIT 5; - time | const | numeric | first | case | coalesce | avg1 | avg2 -------------------------------+-------+---------+----------------------------------------------+------+----------+------+------ - Fri Dec 31 16:00:00 1999 PST | Const | 4.3 | ("Sat Jan 01 00:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sat Jan 01 16:00:00 2000 PST | Const | 4.3 | ("Sat Jan 01 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sun Jan 02 16:00:00 2000 PST | Const | 4.3 | ("Sun Jan 02 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Mon Jan 03 16:00:00 2000 PST | Const | 4.3 | ("Mon Jan 03 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Tue Jan 04 16:00:00 2000 PST | Const | 4.3 | ("Tue Jan 04 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 -(5 rows) - ---test materialization of invalidation before drop -DROP TABLE IF EXISTS drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:358: NOTICE: table "drop_chunks_table" does not exist, skipping -DROP TABLE IF EXISTS drop_chunks_table_u CASCADE; -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to table _timescaledb_internal._hyper_7_9_chunk -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test2'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), max(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---dropping chunks will process the invalidations -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk -(1 row) - -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 10 | 10 -(1 row) - -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(20, 35) AS i; -CALL refresh_continuous_aggregate('drop_chunks_view', 10, 40); ---this will be seen after the drop its within the invalidation window and will be dropped -INSERT INTO drop_chunks_table VALUES (26, 100); ---this will not be processed by the drop since chunk 30-39 is not dropped but will be seen after refresh ---shows that the drop doesn't do more work than necessary -INSERT INTO drop_chunks_table VALUES (31, 200); ---move the time up to 39 -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(35, 39) AS i; ---the chunks and ranges we have thus far -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table'; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(3 rows) - ---the invalidation on 25 not yet seen -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 29 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---refresh to process the invalidations and then drop -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, (integer_now_test2()-9)); -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_14_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - ---new values on 25 now seen in view -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---earliest datapoint now in table -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 30 | 30 -(1 row) - ---we see the chunks row with the dropped flags set; -SELECT * FROM _timescaledb_catalog.chunk where dropped; - id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped | status | osm_chunk -----+---------------+-----------------------+-------------------------+---------------------+---------+--------+----------- - 13 | 10 | _timescaledb_internal | _dist_hyper_10_13_chunk | | t | 0 | f - 14 | 10 | _timescaledb_internal | _dist_hyper_10_14_chunk | | t | 0 | f - 15 | 10 | _timescaledb_internal | _dist_hyper_10_15_chunk | | t | 0 | f -(3 rows) - ---still see data in the view -SELECT * FROM drop_chunks_view WHERE time_bucket < (integer_now_test2()-9) ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(4 rows) - ---no data but covers dropped chunks -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ -(0 rows) - ---recreate the dropped chunk -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---see data from recreated region -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ - 20 | 20 - 19 | 19 - 18 | 18 - 17 | 17 - 16 | 16 - 15 | 15 - 14 | 14 - 13 | 13 - 12 | 12 - 11 | 11 - 10 | 10 - 9 | 9 - 8 | 8 - 7 | 7 - 6 | 6 - 5 | 5 - 4 | 4 - 3 | 3 - 2 | 2 - 1 | 1 - 0 | 0 -(21 rows) - ---should show chunk with old name and old ranges -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(4 rows) - ---We dropped everything up to the bucket starting at 30 and then ---inserted new data up to and including time 20. Therefore, the ---dropped data should stay the same as long as we only refresh ---buckets that have non-dropped data. -CALL refresh_continuous_aggregate('drop_chunks_view', 30, 40); -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 39 - 30 | 200 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_tablen, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_nid - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- TEST drop chunks from continuous aggregates by specifying view name -SELECT drop_chunks('drop_chunks_view', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:454: INFO: dropping chunk _timescaledb_internal._hyper_11_17_chunk - drop_chunks ------------------------------------------- - _timescaledb_internal._hyper_11_17_chunk -(1 row) - --- Test that we cannot drop chunks when specifying materialized --- hypertable -INSERT INTO drop_chunks_table SELECT generate_series(45, 55), 500; -CALL refresh_continuous_aggregate('drop_chunks_view', 45, 55); -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = :'drop_chunks_mat_table_name' ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer ---------------------+---------------------+------------------- - _hyper_11_20_chunk | 0 | 100 -(1 row) - -\set ON_ERROR_STOP 0 -\set VERBOSITY default -SELECT drop_chunks(:'drop_chunks_mat_tablen', older_than => 60); -psql:include/cagg_ddl_common.sql:466: ERROR: operation not supported on materialized hypertable -DETAIL: Hypertable "_materialized_hypertable_11" is a materialized hypertable. -HINT: Try the operation on the continuous aggregate instead. -\set VERBOSITY terse -\set ON_ERROR_STOP 1 ------------------------------------------------------------------ --- Test that refresh_continuous_aggregate on chunk will refresh, --- but only in the regions covered by the show chunks. ------------------------------------------------------------------ -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(6 rows) - --- Pick the second chunk as the one to drop -WITH numbered_chunks AS ( - SELECT row_number() OVER (ORDER BY range_start_integer), chunk_schema, chunk_name, range_start_integer, range_end_integer - FROM timescaledb_information.chunks - WHERE hypertable_name = 'drop_chunks_table' - ORDER BY 1 -) -SELECT format('%I.%I', chunk_schema, chunk_name) AS chunk_to_drop, range_start_integer, range_end_integer -FROM numbered_chunks -WHERE row_number = 2 \gset --- There's data in the table for the chunk/range we will drop -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 -(10 rows) - --- Make sure there is also data in the continuous aggregate --- CARE: --- Note that this behaviour of dropping the materialization table chunks and expecting a refresh --- that overlaps that time range to NOT update those chunks is undefined. Since CAGGs over --- distributed hypertables merge the invalidations the refresh region is updated in the distributed --- case, which may be different than what happens in the normal hypertable case. The command was: --- SELECT drop_chunks('drop_chunks_view', newer_than => -20, verbose => true); -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 50); -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 9 - 10 | 14 - 15 | 19 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(9 rows) - --- Drop the second chunk, to leave a gap in the data -\if :IS_DISTRIBUTED -CALL distributed_exec(format('DROP TABLE IF EXISTS %s', :'chunk_to_drop')); -DROP FOREIGN TABLE :chunk_to_drop; -\else -DROP TABLE :chunk_to_drop; -\endif --- Verify that the second chunk is dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(5 rows) - --- Data is no longer in the table but still in the view -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ -(0 rows) - -SELECT * FROM drop_chunks_view -WHERE time_bucket >= :range_start_integer -AND time_bucket < :range_end_integer -ORDER BY 1; - time_bucket | max --------------+----- - 10 | 14 - 15 | 19 -(2 rows) - --- Insert a large value in one of the chunks that will be dropped -INSERT INTO drop_chunks_table VALUES (:range_start_integer-1, 100); --- Now refresh and drop the two adjecent chunks -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, 30); -SELECT drop_chunks('drop_chunks_table', older_than=>30); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - --- Verify that the chunks are dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(3 rows) - --- The continuous aggregate should be refreshed in the regions covered --- by the dropped chunks, but not in the "gap" region, i.e., the --- region of the chunk that was dropped via DROP TABLE. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 100 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(7 rows) - --- Now refresh in the region of the first two dropped chunks -CALL refresh_continuous_aggregate('drop_chunks_view', 0, :range_end_integer); --- Aggregate data in the refreshed range should no longer exist since --- the underlying data was dropped. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(5 rows) - --------------------------------------------------------------------- --- Check that we can create a materialized table in a tablespace. We --- create one with tablespace and one without and compare them. -CREATE VIEW cagg_info AS -WITH - caggs AS ( - SELECT format('%I.%I', user_view_schema, user_view_name)::regclass AS user_view, - format('%I.%I', direct_view_schema, direct_view_name)::regclass AS direct_view, - format('%I.%I', partial_view_schema, partial_view_name)::regclass AS partial_view, - format('%I.%I', ht.schema_name, ht.table_name)::regclass AS mat_relid - FROM _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.continuous_agg cagg - WHERE ht.id = cagg.mat_hypertable_id - ) -SELECT user_view, - pg_get_userbyid(relowner) AS user_view_owner, - relname AS mat_table, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = mat_relid) AS mat_table_owner, - direct_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = direct_view) AS direct_view_owner, - partial_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = partial_view) AS partial_view_owner, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class JOIN caggs ON pg_class.oid = caggs.mat_relid; -GRANT SELECT ON cagg_info TO PUBLIC; -CREATE VIEW chunk_info AS -SELECT ht.schema_name, ht.table_name, relname AS chunk_name, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class c, - _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.chunk ch - WHERE ch.table_name = c.relname AND ht.id = ch.hypertable_id; -CREATE TABLE whatever(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS whatever_nid - FROM create_distributed_hypertable('whatever', 'time', chunk_time_interval => 10, replication_factor => 2) -\gset -\else -SELECT hypertable_id AS whatever_nid - FROM create_hypertable('whatever', 'time', chunk_time_interval => 10) -\gset -\endif -SELECT set_integer_now_func('whatever', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW whatever_view_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -CREATE MATERIALIZED VIEW whatever_view_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) -TABLESPACE tablespace1 AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -INSERT INTO whatever SELECT i, i FROM generate_series(0, 29) AS i; -CALL refresh_continuous_aggregate('whatever_view_1', NULL, NULL); -CALL refresh_continuous_aggregate('whatever_view_2', NULL, NULL); -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | | _hyper_13_24_chunk | - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -ALTER MATERIALIZED VIEW whatever_view_1 SET TABLESPACE tablespace2; -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | tablespace2 | _hyper_13_24_chunk | tablespace2 - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -DROP MATERIALIZED VIEW whatever_view_1; -psql:include/cagg_ddl_common.sql:644: NOTICE: drop cascades to table _timescaledb_internal._hyper_13_24_chunk -DROP MATERIALIZED VIEW whatever_view_2; -psql:include/cagg_ddl_common.sql:645: NOTICE: drop cascades to table _timescaledb_internal._hyper_14_25_chunk --- test bucket width expressions on integer hypertables -CREATE TABLE metrics_int2 ( - time int2 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int4 ( - time int4 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int8 ( - time int8 NOT NULL, - device_id int, - v1 float, - v2 float -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10, replication_factor => 2) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - create_distributed_hypertable -------------------------------- - (15,public,metrics_int2,t) - (16,public,metrics_int4,t) - (17,public,metrics_int8,t) -(3 rows) - -\else -SELECT create_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); -\endif -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -$DIST$); -\endif -SELECT set_integer_now_func (('metrics_' || dt)::regclass, (dt || '_now')::regproc) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - set_integer_now_func ----------------------- - - - -(3 rows) - --- width expression for int2 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:750: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint + 2::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:757: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int4 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:765: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:772: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int8 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:780: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:787: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -\set ON_ERROR_STOP 0 --- non-immutable expresions should be rejected -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:796: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:801: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:806: ERROR: only immutable expressions allowed in time bucket function -\set ON_ERROR_STOP 1 --- Test various ALTER MATERIALIZED VIEW statements. -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE MATERIALIZED VIEW owner_check WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1 -WITH NO DATA; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | default_perm_user -mat_table | _materialized_hypertable_24 -mat_table_owner | default_perm_user -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | default_perm_user -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | default_perm_user -tablespace | - -\x off --- This should not work since the target user has the wrong role, but --- we test that the normal checks are done when changing the owner. -\set ON_ERROR_STOP 0 -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -psql:include/cagg_ddl_common.sql:826: ERROR: must be member of role "test_role_1" -\set ON_ERROR_STOP 1 --- Superuser can always change owner -SET ROLE :ROLE_CLUSTER_SUPERUSER; -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | test_role_1 -mat_table | _materialized_hypertable_24 -mat_table_owner | test_role_1 -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | test_role_1 -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | test_role_1 -tablespace | - -\x off --- --- Test drop continuous aggregate cases --- --- Issue: #2608 --- -CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS -$BODY$ - SELECT 50; -$BODY$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ - CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS - $BODY$ - SELECT 50; - $BODY$; -$DIST$); -\endif -CREATE TABLE conditionsnm(time_int INT NOT NULL, device INT, value FLOAT); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10, replication_factor => 2); - create_distributed_hypertable -------------------------------- - (25,public,conditionsnm,t) -(1 row) - -\else -SELECT create_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10); -\endif -SELECT set_integer_now_func('conditionsnm', 'test_int_now'); - set_integer_now_func ----------------------- - -(1 row) - -INSERT INTO conditionsnm -SELECT time_val, time_val % 4, 3.14 FROM generate_series(0,100,1) AS time_val; --- Case 1: DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:874: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4; -psql:include/cagg_ddl_common.sql:876: NOTICE: drop cascades to table _timescaledb_internal._hyper_26_37_chunk --- Case 2: DROP CASCADE should have similar behaviour as DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:884: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:886: NOTICE: drop cascades to table _timescaledb_internal._hyper_27_38_chunk --- Case 3: require CASCADE in case of dependent object -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:894: NOTICE: refreshing continuous aggregate "conditionsnm_4" -CREATE VIEW see_cagg as select * from conditionsnm_4; -\set ON_ERROR_STOP 0 -DROP MATERIALIZED VIEW conditionsnm_4; -psql:include/cagg_ddl_common.sql:898: ERROR: cannot drop view conditionsnm_4 because other objects depend on it -\set ON_ERROR_STOP 1 --- Case 4: DROP CASCADE with dependency -DROP MATERIALIZED VIEW conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to view see_cagg -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to table _timescaledb_internal._hyper_28_39_chunk --- Test DROP SCHEMA CASCADE with continuous aggregates --- --- Issue: #2350 --- --- Case 1: DROP SCHEMA CASCADE -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (29,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.telemetry_1s - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'telemetry_1s'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME | PART_VIEW_NAME | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 29 | _timescaledb_internal | _materialized_hypertable_30 | _partial_view_30 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:941: NOTICE: drop cascades to 4 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'telemetry_1s'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - --- Case 2: DROP SCHEMA CASCADE with multiple caggs -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (31,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.cagg1 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -CREATE MATERIALIZED VIEW test_schema.cagg2 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME1", - partial_view_name as "PART_VIEW_NAME1", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg1'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME1 | PART_VIEW_NAME1 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_32 | _partial_view_32 | _timescaledb_internal -(1 row) - -\gset -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME2", - partial_view_name as "PART_VIEW_NAME2", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg2'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME2 | PART_VIEW_NAME2 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_33 | _partial_view_33 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:998: NOTICE: drop cascades to 7 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - -DROP TABLESPACE tablespace1; -DROP TABLESPACE tablespace2; --- Check that we can rename a column of a materialized view and still --- rebuild it after (#3051, #3405) -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (34,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -SELECT format('%I.%I', '_timescaledb_internal', h.table_name) AS "MAT_TABLE_NAME", - format('%I.%I', '_timescaledb_internal', partial_view_name) AS "PART_VIEW_NAME", - format('%I.%I', '_timescaledb_internal', direct_view_name) AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'conditions_daily' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns('conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | t - avg | double precision | f -(3 rows) - -ALTER MATERIALIZED VIEW conditions_daily RENAME COLUMN bucket to "time"; --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns(' conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | t - avg | double precision | f -(3 rows) - --- This will rebuild the materialized view and should succeed. -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only = false); --- Refresh the continuous aggregate to check that it works after the --- rename. -\set VERBOSITY verbose -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -\set VERBOSITY terse --- --- Indexes on continuous aggregate --- -\set ON_ERROR_STOP 0 --- unique indexes are not supported -CREATE UNIQUE INDEX index_unique_error ON conditions_daily ("time", location); -psql:include/cagg_ddl_common.sql:1084: ERROR: continuous aggregates do not support UNIQUE indexes --- concurrently index creation not supported -CREATE INDEX CONCURRENTLY index_concurrently_avg ON conditions_daily (avg); -psql:include/cagg_ddl_common.sql:1086: ERROR: hypertables do not support concurrent index creation -\set ON_ERROR_STOP 1 -CREATE INDEX index_avg ON conditions_daily (avg); -CREATE INDEX index_avg_only ON ONLY conditions_daily (avg); -CREATE INDEX index_avg_include ON conditions_daily (avg) INCLUDE (location); -CREATE INDEX index_avg_expr ON conditions_daily ((avg + 1)); -CREATE INDEX index_avg_location_sfo ON conditions_daily (avg) WHERE location = 'SFO'; -CREATE INDEX index_avg_expr_location_sfo ON conditions_daily ((avg + 2)) WHERE location = 'SFO'; -SELECT * FROM test.show_indexespred(:'MAT_TABLE_NAME'); - Index | Columns | Expr | Pred | Unique | Primary | Exclusion | Tablespace ------------------------------------------------------------------------+-------------------+---------------------------+------------------------+--------+---------+-----------+------------ - _timescaledb_internal._materialized_hypertable_35_bucket_idx | {bucket} | | | f | f | f | - _timescaledb_internal._materialized_hypertable_35_location_bucket_idx | {location,bucket} | | | f | f | f | - _timescaledb_internal.index_avg | {avg} | | | f | f | f | - _timescaledb_internal.index_avg_expr | {expr} | avg + 1::double precision | | f | f | f | - _timescaledb_internal.index_avg_expr_location_sfo | {expr} | avg + 2::double precision | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_include | {avg,location} | | | f | f | f | - _timescaledb_internal.index_avg_location_sfo | {avg} | | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_only | {avg} | | | f | f | f | -(8 rows) - --- #3696 assertion failure when referencing columns not present in result -CREATE TABLE i3696(time timestamptz NOT NULL, search_query text, cnt integer, cnt2 integer); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('i3696', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (36,public,i3696,t) -(1 row) - -\else -SELECT table_name FROM create_hypertable('i3696','time'); -\endif -CREATE MATERIALIZED VIEW i3696_cagg1 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt +cnt2 , bucket, search_query; -psql:include/cagg_ddl_common.sql:1108: NOTICE: continuous aggregate "i3696_cagg1" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg1 SET (timescaledb.materialized_only = 'true'); -CREATE MATERIALIZED VIEW i3696_cagg2 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt + cnt2, bucket, search_query - HAVING cnt + cnt2 + sum(cnt) > 2 or count(cnt2) > 10; -psql:include/cagg_ddl_common.sql:1116: NOTICE: continuous aggregate "i3696_cagg2" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg2 SET (timescaledb.materialized_only = 'true'); ---TEST test with multiple settings on continuous aggregates -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -CREATE TABLE test_setting(time timestamptz not null, val numeric); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_setting', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (39,public,test_setting,t) -(1 row) - -\else -SELECT create_hypertable('test_setting', 'time'); -\endif -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only=false) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1130: NOTICE: continuous aggregate "test_setting_cagg" is already up-to-date -INSERT INTO test_setting -SELECT generate_series( '2020-01-10 8:00'::timestamp, '2020-01-30 10:00+00'::timestamptz, '1 day'::interval), 10.0; -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1141: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1149: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -DELETE FROM test_setting WHERE val = 20; ---TEST test with multiple settings on continuous aggregates with real time aggregates turned off initially -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -DROP MATERIALIZED VIEW test_setting_cagg; -psql:include/cagg_ddl_common.sql:1174: NOTICE: drop cascades to table _timescaledb_internal._hyper_40_47_chunk -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only = true) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1177: NOTICE: refreshing continuous aggregate "test_setting_cagg" -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1185: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1193: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - --- END TEST with multiple settings --- Test View Target Entries that contain both aggrefs and Vars in the same expression -CREATE TABLE transactions -( - "time" timestamp with time zone NOT NULL, - dummy1 integer, - dummy2 integer, - dummy3 integer, - dummy4 integer, - dummy5 integer, - amount integer, - fiat_value integer -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('transactions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (46,public,transactions,t) -(1 row) - -\else -SELECT create_hypertable('transactions', 'time'); -\endif -INSERT INTO transactions VALUES ( '2018-01-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:30:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:20:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 10:40:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 11:50:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 12:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 13:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 10:30:00-08', 0, 0, 0, 0, 0, -1, 10); -CREATE materialized view cashflows( - bucket, - amount, - cashflow, - cashflow2 -) WITH ( - timescaledb.continuous, - timescaledb.materialized_only = true -) AS -SELECT time_bucket ('1 day', time) AS bucket, - amount, - CASE - WHEN amount < 0 THEN (0 - sum(fiat_value)) - ELSE sum(fiat_value) - END AS cashflow, - amount + sum(fiat_value) -FROM transactions -GROUP BY bucket, amount; -psql:include/cagg_ddl_common.sql:1267: NOTICE: refreshing continuous aggregate "cashflows" -SELECT h.table_name AS "MAT_TABLE_NAME", - partial_view_name AS "PART_VIEW_NAME", - direct_view_name AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cashflows' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -\d+ "_timescaledb_internal".:"DIRECT_VIEW_NAME" - View "_timescaledb_internal._direct_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"PART_VIEW_NAME" - View "_timescaledb_internal._partial_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"MAT_TABLE_NAME" - Table "_timescaledb_internal._materialized_hypertable_47" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ------------+--------------------------+-----------+----------+---------+---------+--------------+------------- - bucket | timestamp with time zone | | not null | | plain | | - amount | integer | | | | plain | | - cashflow | bigint | | | | plain | | - cashflow2 | bigint | | | | plain | | -Indexes: - "_materialized_hypertable_47_amount_bucket_idx" btree (amount, bucket DESC) - "_materialized_hypertable_47_bucket_idx" btree (bucket DESC) -Triggers: - ts_insert_blocker BEFORE INSERT ON _timescaledb_internal._materialized_hypertable_47 FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker() -Child tables: _timescaledb_internal._hyper_47_52_chunk, - _timescaledb_internal._hyper_47_53_chunk - -\d+ 'cashflows' - View "public.cashflows" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT _materialized_hypertable_47.bucket, - _materialized_hypertable_47.amount, - _materialized_hypertable_47.cashflow, - _materialized_hypertable_47.cashflow2 - FROM _timescaledb_internal._materialized_hypertable_47; - -SELECT * FROM cashflows; - bucket | amount | cashflow | cashflow2 -------------------------------+--------+----------+----------- - Sun Dec 31 16:00:00 2017 PST | 1 | 10 | 11 - Mon Jan 01 16:00:00 2018 PST | -1 | -30 | 29 - Wed Oct 31 17:00:00 2018 PDT | -1 | -20 | 19 - Wed Oct 31 17:00:00 2018 PDT | 1 | 30 | 31 - Thu Nov 01 17:00:00 2018 PDT | -1 | -10 | 9 - Thu Nov 01 17:00:00 2018 PDT | 1 | 10 | 11 -(6 rows) - --- test cagg creation with named arguments in time_bucket --- note that positional arguments cannot follow named arguments --- 1. test named origin --- 2. test named timezone --- 3. test named ts --- 4. test named bucket width --- named origin -CREATE MATERIALIZED VIEW cagg_named_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named timezone -CREATE MATERIALIZED VIEW cagg_named_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named ts -CREATE MATERIALIZED VIEW cagg_named_ts_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named bucket width -CREATE MATERIALIZED VIEW cagg_named_all WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(bucket_width => '1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- Refreshing from the beginning (NULL) of a CAGG with variable time bucket and --- using an INTERVAL for the end timestamp (issue #5534) -CREATE MATERIALIZED VIEW transactions_montly -WITH (timescaledb.continuous, timescaledb.materialized_only = true) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) - FROM transactions -GROUP BY 1 -WITH NO DATA; --- No rows -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min ---------+-----+-----+----- -(0 rows) - --- Refresh from beginning of the CAGG for 1 month -CALL refresh_continuous_aggregate('transactions_montly', NULL, INTERVAL '1 month'); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - -TRUNCATE transactions_montly; --- Partial refresh the CAGG from beginning to an specific timestamp -CALL refresh_continuous_aggregate('transactions_montly', NULL, '2018-11-01 11:50:00-08'::timestamptz); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 -(1 row) - --- Full refresh the CAGG -CALL refresh_continuous_aggregate('transactions_montly', NULL, NULL); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - --- Check set_chunk_time_interval on continuous aggregate -CREATE MATERIALIZED VIEW cagg_set_chunk_time_interval -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) -FROM transactions -GROUP BY 1 -WITH NO DATA; -SELECT set_chunk_time_interval('cagg_set_chunk_time_interval', chunk_time_interval => interval '1 month'); - set_chunk_time_interval -------------------------- - -(1 row) - -CALL refresh_continuous_aggregate('cagg_set_chunk_time_interval', NULL, NULL); -SELECT _timescaledb_functions.to_interval(d.interval_length) = interval '1 month' -FROM _timescaledb_catalog.dimension d - RIGHT JOIN _timescaledb_catalog.continuous_agg ca ON ca.user_view_name = 'cagg_set_chunk_time_interval' -WHERE d.hypertable_id = ca.mat_hypertable_id; - ?column? ----------- - t -(1 row) - --- Since #6077 CAggs are materialized only by default -DROP TABLE conditions CASCADE; -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 3 other objects -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 2 other objects -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (54,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - --- Should return NO ROWS -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+--------+----- -(0 rows) - -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=false); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55 - WHERE _materialized_hypertable_55.bucket < COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) -UNION ALL - SELECT conditions.location, - time_bucket('@ 1 day'::interval, conditions."time") AS bucket, - avg(conditions.temperature) AS avg - FROM conditions - WHERE conditions."time" >= COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) - GROUP BY conditions.location, (time_bucket('@ 1 day'::interval, conditions."time")); - --- Should return ROWS because now it is realtime -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- Should return ROWS because we refreshed it -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=true); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- cleanup -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); diff --git a/tsl/test/expected/cagg_ddl_dist_ht-15.out b/tsl/test/expected/cagg_ddl_dist_ht-15.out deleted file mode 100644 index 8c4e1394a03..00000000000 --- a/tsl/test/expected/cagg_ddl_dist_ht-15.out +++ /dev/null @@ -1,2207 +0,0 @@ --- 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. ------------------------------------- --- Set up a distributed environment ------------------------------------- -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created ------------------------+-----------------------+--------------+------------------+------------------- - db_cagg_ddl_dist_ht_1 | db_cagg_ddl_dist_ht_1 | t | t | t - db_cagg_ddl_dist_ht_2 | db_cagg_ddl_dist_ht_2 | t | t | t - db_cagg_ddl_dist_ht_3 | db_cagg_ddl_dist_ht_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -\set IS_DISTRIBUTED TRUE -\ir include/cagg_ddl_common.sql --- 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. --- Set this variable to avoid using a hard-coded path each time query --- results are compared -\set QUERY_RESULT_TEST_EQUAL_RELPATH '../../../../test/sql/include/query_result_test_equal.sql' -\if :IS_DISTRIBUTED -\echo 'Running distributed hypertable tests' -Running distributed hypertable tests -\else -\echo 'Running local hypertable tests' -\endif -SET ROLE :ROLE_DEFAULT_PERM_USER; ---DDL commands on continuous aggregates -CREATE TABLE conditions ( - timec TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature integer NULL, - humidity DOUBLE PRECISION NULL, - timemeasure TIMESTAMPTZ, - timeinterval INTERVAL -); -\if :IS_DISTRIBUTED -SELECT table_name FROM create_distributed_hypertable('conditions', 'timec', replication_factor => 2); - table_name ------------- - conditions -(1 row) - -\else -SELECT table_name FROM create_hypertable('conditions', 'timec'); -\endif --- schema tests -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; -CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH; -CREATE SCHEMA rename_schema; -GRANT ALL ON SCHEMA rename_schema TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE foo(time TIMESTAMPTZ NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('foo', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (2,public,foo,t) -(1 row) - -\else -SELECT create_hypertable('foo', 'time'); -\endif -CREATE MATERIALIZED VIEW rename_test - WITH ( timescaledb.continuous, timescaledb.materialized_only=true) -AS SELECT time_bucket('1week', time), COUNT(data) - FROM foo - GROUP BY 1 WITH NO DATA; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - public | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test SET SCHEMA rename_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+-----------------------+------------------- - rename_schema | rename_test | _timescaledb_internal | _partial_view_3 -(1 row) - -SELECT ca.raw_hypertable_id as "RAW_HYPERTABLE_ID", - h.schema_name AS "MAT_SCHEMA_NAME", - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema as "PART_VIEW_SCHEMA", - direct_view_name as "DIR_VIEW_NAME", - direct_view_schema as "DIR_VIEW_SCHEMA" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'rename_test' -\gset -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"PART_VIEW_SCHEMA".:"PART_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - rename_schema | rename_test | public | _partial_view_3 -(1 row) - ---alter direct view schema -SELECT user_view_schema, user_view_name, direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | direct_view_schema | direct_view_name -------------------+----------------+-----------------------+------------------ - rename_schema | rename_test | _timescaledb_internal | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER VIEW :"DIR_VIEW_SCHEMA".:"DIR_VIEW_NAME" SET SCHEMA public; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - rename_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA rename_schema RENAME TO new_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | public | _partial_view_3 | public | _direct_view_3 -(1 row) - -ALTER VIEW :"PART_VIEW_NAME" SET SCHEMA new_name_schema; -ALTER VIEW :"DIR_VIEW_NAME" SET SCHEMA new_name_schema; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+----------------+---------------------+-------------------+--------------------+------------------ - new_name_schema | rename_test | new_name_schema | _partial_view_3 | new_name_schema | _direct_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA new_name_schema RENAME TO foo_name_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - foo_name_schema | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW foo_name_schema.rename_test SET SCHEMA public; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | foo_name_schema | _partial_view_3 -(1 row) - -RESET ROLE; -SELECT current_user; - current_user --------------------- - cluster_super_user -(1 row) - -ALTER SCHEMA foo_name_schema RENAME TO rename_schema; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+----------------+---------------------+------------------- - public | rename_test | rename_schema | _partial_view_3 -(1 row) - -ALTER MATERIALIZED VIEW rename_test RENAME TO rename_c_aggregate; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name -------------------+--------------------+---------------------+------------------- - public | rename_c_aggregate | rename_schema | _partial_view_3 -(1 row) - -SELECT * FROM rename_c_aggregate; - time_bucket | count --------------+------- -(0 rows) - -ALTER VIEW rename_schema.:"PART_VIEW_NAME" RENAME TO partial_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | _direct_view_3 -(1 row) - ---rename direct view -ALTER VIEW rename_schema.:"DIR_VIEW_NAME" RENAME TO direct_view; -SELECT user_view_schema, user_view_name, partial_view_schema, partial_view_name, - direct_view_schema, direct_view_name - FROM _timescaledb_catalog.continuous_agg; - user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name -------------------+--------------------+---------------------+-------------------+--------------------+------------------ - public | rename_c_aggregate | rename_schema | partial_view | rename_schema | direct_view -(1 row) - --- drop_chunks tests -DROP TABLE conditions CASCADE; -DROP TABLE foo CASCADE; -psql:include/cagg_ddl_common.sql:161: NOTICE: drop cascades to 2 other objects -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_id - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_id - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), COUNT(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 29) AS i; --- Only refresh up to bucket 15 initially. Matches the old refresh --- behavior that didn't materialize everything -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- cannot drop directly from the materialization table without specifying --- cont. aggregate view name explicitly -\set ON_ERROR_STOP 0 -SELECT drop_chunks(:'drop_chunks_mat_table', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:213: ERROR: operation not supported on materialized hypertable -\set ON_ERROR_STOP 1 -SELECT count(c) FROM show_chunks('drop_chunks_table') AS c; - count -------- - 3 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 5 - 5 | 5 - 10 | 5 -(3 rows) - --- drop chunks when the chunksize and time_bucket aren't aligned -DROP TABLE drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:222: NOTICE: drop cascades to table _timescaledb_internal._hyper_5_4_chunk -CREATE TABLE drop_chunks_table_u(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_distributed_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_u_id - FROM create_hypertable('drop_chunks_table_u', 'time', chunk_time_interval => 7) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test1() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table_u $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table_u', 'integer_now_test1'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('3', time), COUNT(data) - FROM drop_chunks_table_u - GROUP BY 1 WITH NO DATA; -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_table_u, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_u_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_u_id - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- create 3 chunks, with 3 time bucket -INSERT INTO drop_chunks_table_u SELECT i, i FROM generate_series(0, 21) AS i; --- Refresh up to bucket 15 to match old materializer behavior -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 15); -SELECT count(c) FROM show_chunks('drop_chunks_table_u') AS c; - count -------- - 4 -(1 row) - -SELECT count(c) FROM show_chunks('drop_chunks_view') AS c; - count -------- - 1 -(1 row) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - --- TRUNCATE test --- Can truncate regular hypertables that have caggs -TRUNCATE drop_chunks_table_u; -\set ON_ERROR_STOP 0 --- Can't truncate materialized hypertables directly -TRUNCATE :drop_chunks_mat_table_u; -psql:include/cagg_ddl_common.sql:271: ERROR: cannot TRUNCATE a hypertable underlying a continuous aggregate -\set ON_ERROR_STOP 1 --- Check that we don't interfere with TRUNCATE of normal table and --- partitioned table -CREATE TABLE truncate (value int); -INSERT INTO truncate VALUES (1), (2); -TRUNCATE truncate; -SELECT * FROM truncate; - value -------- -(0 rows) - -CREATE TABLE truncate_partitioned (value int) - PARTITION BY RANGE(value); -CREATE TABLE truncate_p1 PARTITION OF truncate_partitioned - FOR VALUES FROM (1) TO (3); -INSERT INTO truncate_partitioned VALUES (1), (2); -TRUNCATE truncate_partitioned; -SELECT * FROM truncate_partitioned; - value -------- -(0 rows) - --- ALTER TABLE tests -\set ON_ERROR_STOP 0 --- test a variety of ALTER TABLE statements -ALTER TABLE :drop_chunks_mat_table_u RENAME time_bucket TO bad_name; -psql:include/cagg_ddl_common.sql:291: ERROR: renaming columns on materialization tables is not supported -ALTER TABLE :drop_chunks_mat_table_u ADD UNIQUE(time_bucket); -psql:include/cagg_ddl_common.sql:292: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET UNLOGGED; -psql:include/cagg_ddl_common.sql:293: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ENABLE ROW LEVEL SECURITY; -psql:include/cagg_ddl_common.sql:294: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ADD COLUMN fizzle INTEGER; -psql:include/cagg_ddl_common.sql:295: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DROP COLUMN time_bucket; -psql:include/cagg_ddl_common.sql:296: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket DROP NOT NULL; -psql:include/cagg_ddl_common.sql:297: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET DEFAULT 1; -psql:include/cagg_ddl_common.sql:298: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u ALTER COLUMN time_bucket SET STORAGE EXTERNAL; -psql:include/cagg_ddl_common.sql:299: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u DISABLE TRIGGER ALL; -psql:include/cagg_ddl_common.sql:300: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u SET TABLESPACE foo; -psql:include/cagg_ddl_common.sql:301: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u NOT OF; -psql:include/cagg_ddl_common.sql:302: ERROR: operation not supported on materialization tables -ALTER TABLE :drop_chunks_mat_table_u OWNER TO CURRENT_USER; -psql:include/cagg_ddl_common.sql:303: ERROR: operation not supported on materialization tables -\set ON_ERROR_STOP 1 -ALTER TABLE :drop_chunks_mat_table_u SET SCHEMA public; -ALTER TABLE :drop_chunks_mat_table_u_name RENAME TO new_name; -SET ROLE :ROLE_DEFAULT_PERM_USER; -SET client_min_messages TO NOTICE; -SELECT * FROM new_name; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -SELECT * FROM drop_chunks_view ORDER BY 1; - time_bucket | count --------------+------- - 0 | 3 - 3 | 3 - 6 | 3 - 9 | 3 - 12 | 3 -(5 rows) - -\set ON_ERROR_STOP 0 --- no continuous aggregates on a continuous aggregate materialization table -CREATE MATERIALIZED VIEW new_name_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('6', time_bucket), COUNT("count") - FROM new_name - GROUP BY 1 WITH NO DATA; -psql:include/cagg_ddl_common.sql:326: ERROR: hypertable is a continuous aggregate materialization table -\set ON_ERROR_STOP 1 -CREATE TABLE metrics(time timestamptz NOT NULL, device_id int, v1 float, v2 float); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('metrics', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (8,public,metrics,t) -(1 row) - -\else -SELECT create_hypertable('metrics','time'); -\endif -INSERT INTO metrics SELECT generate_series('2000-01-01'::timestamptz,'2000-01-10','1m'),1,0.25,0.75; --- check expressions in view definition -CREATE MATERIALIZED VIEW cagg_expr - WITH (timescaledb.continuous, timescaledb.materialized_only=true) -AS -SELECT - time_bucket('1d', time) AS time, - 'Const'::text AS Const, - 4.3::numeric AS "numeric", - first(metrics,time), - CASE WHEN true THEN 'foo' ELSE 'bar' END, - COALESCE(NULL,'coalesce'), - avg(v1) + avg(v2) AS avg1, - avg(v1+v2) AS avg2 -FROM metrics -GROUP BY 1 WITH NO DATA; -CALL refresh_continuous_aggregate('cagg_expr', NULL, NULL); -SELECT * FROM cagg_expr ORDER BY time LIMIT 5; - time | const | numeric | first | case | coalesce | avg1 | avg2 -------------------------------+-------+---------+----------------------------------------------+------+----------+------+------ - Fri Dec 31 16:00:00 1999 PST | Const | 4.3 | ("Sat Jan 01 00:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sat Jan 01 16:00:00 2000 PST | Const | 4.3 | ("Sat Jan 01 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Sun Jan 02 16:00:00 2000 PST | Const | 4.3 | ("Sun Jan 02 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Mon Jan 03 16:00:00 2000 PST | Const | 4.3 | ("Mon Jan 03 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 - Tue Jan 04 16:00:00 2000 PST | Const | 4.3 | ("Tue Jan 04 16:00:00 2000 PST",1,0.25,0.75) | foo | coalesce | 1 | 1 -(5 rows) - ---test materialization of invalidation before drop -DROP TABLE IF EXISTS drop_chunks_table CASCADE; -psql:include/cagg_ddl_common.sql:358: NOTICE: table "drop_chunks_table" does not exist, skipping -DROP TABLE IF EXISTS drop_chunks_table_u CASCADE; -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to 2 other objects -psql:include/cagg_ddl_common.sql:359: NOTICE: drop cascades to table _timescaledb_internal._hyper_7_9_chunk -CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_distributed_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10, replication_factor => 2) \gset -\else -SELECT hypertable_id AS drop_chunks_table_nid - FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset -\endif -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION integer_now_test2() returns bigint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), bigint '0') FROM drop_chunks_table $$; -$DIST$); -\endif -SELECT set_integer_now_func('drop_chunks_table', 'integer_now_test2'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW drop_chunks_view - WITH ( - timescaledb.continuous, - timescaledb.materialized_only=true - ) -AS SELECT time_bucket('5', time), max(data) - FROM drop_chunks_table - GROUP BY 1 WITH NO DATA; -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---dropping chunks will process the invalidations -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk -(1 row) - -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 10 | 10 -(1 row) - -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(20, 35) AS i; -CALL refresh_continuous_aggregate('drop_chunks_view', 10, 40); ---this will be seen after the drop its within the invalidation window and will be dropped -INSERT INTO drop_chunks_table VALUES (26, 100); ---this will not be processed by the drop since chunk 30-39 is not dropped but will be seen after refresh ---shows that the drop doesn't do more work than necessary -INSERT INTO drop_chunks_table VALUES (31, 200); ---move the time up to 39 -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(35, 39) AS i; ---the chunks and ranges we have thus far -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table'; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(3 rows) - ---the invalidation on 25 not yet seen -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 29 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---refresh to process the invalidations and then drop -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, (integer_now_test2()-9)); -SELECT drop_chunks('drop_chunks_table', older_than => (integer_now_test2()-9)); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_14_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - ---new values on 25 now seen in view -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 35 - 30 | 34 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - ---earliest datapoint now in table -SELECT * FROM drop_chunks_table ORDER BY time ASC limit 1; - time | data -------+------ - 30 | 30 -(1 row) - ---we see the chunks row with the dropped flags set; -SELECT * FROM _timescaledb_catalog.chunk where dropped; - id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped | status | osm_chunk -----+---------------+-----------------------+-------------------------+---------------------+---------+--------+----------- - 13 | 10 | _timescaledb_internal | _dist_hyper_10_13_chunk | | t | 0 | f - 14 | 10 | _timescaledb_internal | _dist_hyper_10_14_chunk | | t | 0 | f - 15 | 10 | _timescaledb_internal | _dist_hyper_10_15_chunk | | t | 0 | f -(3 rows) - ---still see data in the view -SELECT * FROM drop_chunks_view WHERE time_bucket < (integer_now_test2()-9) ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(4 rows) - ---no data but covers dropped chunks -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ -(0 rows) - ---recreate the dropped chunk -INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 20) AS i; ---see data from recreated region -SELECT * FROM drop_chunks_table WHERE time < (integer_now_test2()-9) ORDER BY time DESC; - time | data -------+------ - 20 | 20 - 19 | 19 - 18 | 18 - 17 | 17 - 16 | 16 - 15 | 15 - 14 | 14 - 13 | 13 - 12 | 12 - 11 | 11 - 10 | 10 - 9 | 9 - 8 | 8 - 7 | 7 - 6 | 6 - 5 | 5 - 4 | 4 - 3 | 3 - 2 | 2 - 1 | 1 - 0 | 0 -(21 rows) - ---should show chunk with old name and old ranges -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 -(4 rows) - ---We dropped everything up to the bucket starting at 30 and then ---inserted new data up to and including time 20. Therefore, the ---dropped data should stay the same as long as we only refresh ---buckets that have non-dropped data. -CALL refresh_continuous_aggregate('drop_chunks_view', 30, 40); -SELECT * FROM drop_chunks_view ORDER BY time_bucket DESC; - time_bucket | max --------------+----- - 35 | 39 - 30 | 200 - 25 | 100 - 20 | 24 - 15 | 19 - 10 | 14 -(6 rows) - -SELECT format('%I.%I', schema_name, table_name) AS drop_chunks_mat_tablen, - schema_name AS drop_chunks_mat_schema, - table_name AS drop_chunks_mat_table_name - FROM _timescaledb_catalog.hypertable, _timescaledb_catalog.continuous_agg - WHERE _timescaledb_catalog.continuous_agg.raw_hypertable_id = :drop_chunks_table_nid - AND _timescaledb_catalog.hypertable.id = _timescaledb_catalog.continuous_agg.mat_hypertable_id \gset --- TEST drop chunks from continuous aggregates by specifying view name -SELECT drop_chunks('drop_chunks_view', - newer_than => -20, - verbose => true); -psql:include/cagg_ddl_common.sql:454: INFO: dropping chunk _timescaledb_internal._hyper_11_17_chunk - drop_chunks ------------------------------------------- - _timescaledb_internal._hyper_11_17_chunk -(1 row) - --- Test that we cannot drop chunks when specifying materialized --- hypertable -INSERT INTO drop_chunks_table SELECT generate_series(45, 55), 500; -CALL refresh_continuous_aggregate('drop_chunks_view', 45, 55); -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = :'drop_chunks_mat_table_name' ORDER BY range_start_integer; - chunk_name | range_start_integer | range_end_integer ---------------------+---------------------+------------------- - _hyper_11_20_chunk | 0 | 100 -(1 row) - -\set ON_ERROR_STOP 0 -\set VERBOSITY default -SELECT drop_chunks(:'drop_chunks_mat_tablen', older_than => 60); -psql:include/cagg_ddl_common.sql:466: ERROR: operation not supported on materialized hypertable -DETAIL: Hypertable "_materialized_hypertable_11" is a materialized hypertable. -HINT: Try the operation on the continuous aggregate instead. -\set VERBOSITY terse -\set ON_ERROR_STOP 1 ------------------------------------------------------------------ --- Test that refresh_continuous_aggregate on chunk will refresh, --- but only in the regions covered by the show chunks. ------------------------------------------------------------------ -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_14_chunk | 10 | 20 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(6 rows) - --- Pick the second chunk as the one to drop -WITH numbered_chunks AS ( - SELECT row_number() OVER (ORDER BY range_start_integer), chunk_schema, chunk_name, range_start_integer, range_end_integer - FROM timescaledb_information.chunks - WHERE hypertable_name = 'drop_chunks_table' - ORDER BY 1 -) -SELECT format('%I.%I', chunk_schema, chunk_name) AS chunk_to_drop, range_start_integer, range_end_integer -FROM numbered_chunks -WHERE row_number = 2 \gset --- There's data in the table for the chunk/range we will drop -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ - 10 | 10 - 11 | 11 - 12 | 12 - 13 | 13 - 14 | 14 - 15 | 15 - 16 | 16 - 17 | 17 - 18 | 18 - 19 | 19 -(10 rows) - --- Make sure there is also data in the continuous aggregate --- CARE: --- Note that this behaviour of dropping the materialization table chunks and expecting a refresh --- that overlaps that time range to NOT update those chunks is undefined. Since CAGGs over --- distributed hypertables merge the invalidations the refresh region is updated in the distributed --- case, which may be different than what happens in the normal hypertable case. The command was: --- SELECT drop_chunks('drop_chunks_view', newer_than => -20, verbose => true); -CALL refresh_continuous_aggregate('drop_chunks_view', 0, 50); -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 9 - 10 | 14 - 15 | 19 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(9 rows) - --- Drop the second chunk, to leave a gap in the data -\if :IS_DISTRIBUTED -CALL distributed_exec(format('DROP TABLE IF EXISTS %s', :'chunk_to_drop')); -DROP FOREIGN TABLE :chunk_to_drop; -\else -DROP TABLE :chunk_to_drop; -\endif --- Verify that the second chunk is dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_13_chunk | 0 | 10 - _dist_hyper_10_15_chunk | 20 | 30 - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(5 rows) - --- Data is no longer in the table but still in the view -SELECT * FROM drop_chunks_table -WHERE time >= :range_start_integer -AND time < :range_end_integer -ORDER BY 1; - time | data -------+------ -(0 rows) - -SELECT * FROM drop_chunks_view -WHERE time_bucket >= :range_start_integer -AND time_bucket < :range_end_integer -ORDER BY 1; - time_bucket | max --------------+----- - 10 | 14 - 15 | 19 -(2 rows) - --- Insert a large value in one of the chunks that will be dropped -INSERT INTO drop_chunks_table VALUES (:range_start_integer-1, 100); --- Now refresh and drop the two adjecent chunks -CALL refresh_continuous_aggregate('drop_chunks_view', NULL, 30); -SELECT drop_chunks('drop_chunks_table', older_than=>30); - drop_chunks ------------------------------------------------ - _timescaledb_internal._dist_hyper_10_13_chunk - _timescaledb_internal._dist_hyper_10_15_chunk -(2 rows) - --- Verify that the chunks are dropped -SELECT chunk_name, range_start_integer, range_end_integer -FROM timescaledb_information.chunks -WHERE hypertable_name = 'drop_chunks_table' -ORDER BY 2,3; - chunk_name | range_start_integer | range_end_integer --------------------------+---------------------+------------------- - _dist_hyper_10_16_chunk | 30 | 40 - _dist_hyper_10_18_chunk | 40 | 50 - _dist_hyper_10_19_chunk | 50 | 60 -(3 rows) - --- The continuous aggregate should be refreshed in the regions covered --- by the dropped chunks, but not in the "gap" region, i.e., the --- region of the chunk that was dropped via DROP TABLE. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 0 | 4 - 5 | 100 - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(7 rows) - --- Now refresh in the region of the first two dropped chunks -CALL refresh_continuous_aggregate('drop_chunks_view', 0, :range_end_integer); --- Aggregate data in the refreshed range should no longer exist since --- the underlying data was dropped. -SELECT * FROM drop_chunks_view -ORDER BY 1; - time_bucket | max --------------+----- - 20 | 20 - 30 | 200 - 35 | 39 - 45 | 500 - 50 | 500 -(5 rows) - --------------------------------------------------------------------- --- Check that we can create a materialized table in a tablespace. We --- create one with tablespace and one without and compare them. -CREATE VIEW cagg_info AS -WITH - caggs AS ( - SELECT format('%I.%I', user_view_schema, user_view_name)::regclass AS user_view, - format('%I.%I', direct_view_schema, direct_view_name)::regclass AS direct_view, - format('%I.%I', partial_view_schema, partial_view_name)::regclass AS partial_view, - format('%I.%I', ht.schema_name, ht.table_name)::regclass AS mat_relid - FROM _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.continuous_agg cagg - WHERE ht.id = cagg.mat_hypertable_id - ) -SELECT user_view, - pg_get_userbyid(relowner) AS user_view_owner, - relname AS mat_table, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = mat_relid) AS mat_table_owner, - direct_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = direct_view) AS direct_view_owner, - partial_view, - (SELECT pg_get_userbyid(relowner) FROM pg_class WHERE oid = partial_view) AS partial_view_owner, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class JOIN caggs ON pg_class.oid = caggs.mat_relid; -GRANT SELECT ON cagg_info TO PUBLIC; -CREATE VIEW chunk_info AS -SELECT ht.schema_name, ht.table_name, relname AS chunk_name, - (SELECT spcname FROM pg_tablespace WHERE oid = reltablespace) AS tablespace - FROM pg_class c, - _timescaledb_catalog.hypertable ht, - _timescaledb_catalog.chunk ch - WHERE ch.table_name = c.relname AND ht.id = ch.hypertable_id; -CREATE TABLE whatever(time BIGINT NOT NULL, data INTEGER); -\if :IS_DISTRIBUTED -SELECT hypertable_id AS whatever_nid - FROM create_distributed_hypertable('whatever', 'time', chunk_time_interval => 10, replication_factor => 2) -\gset -\else -SELECT hypertable_id AS whatever_nid - FROM create_hypertable('whatever', 'time', chunk_time_interval => 10) -\gset -\endif -SELECT set_integer_now_func('whatever', 'integer_now_test'); - set_integer_now_func ----------------------- - -(1 row) - -CREATE MATERIALIZED VIEW whatever_view_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -CREATE MATERIALIZED VIEW whatever_view_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) -TABLESPACE tablespace1 AS -SELECT time_bucket('5', time), COUNT(data) - FROM whatever GROUP BY 1 WITH NO DATA; -INSERT INTO whatever SELECT i, i FROM generate_series(0, 29) AS i; -CALL refresh_continuous_aggregate('whatever_view_1', NULL, NULL); -CALL refresh_continuous_aggregate('whatever_view_2', NULL, NULL); -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | | _hyper_13_24_chunk | - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -ALTER MATERIALIZED VIEW whatever_view_1 SET TABLESPACE tablespace2; -SELECT user_view, - mat_table, - cagg_info.tablespace AS mat_tablespace, - chunk_name, - chunk_info.tablespace AS chunk_tablespace - FROM cagg_info, chunk_info - WHERE mat_table::text = table_name - AND user_view::text LIKE 'whatever_view%'; - user_view | mat_table | mat_tablespace | chunk_name | chunk_tablespace ------------------+-----------------------------+----------------+--------------------+------------------ - whatever_view_1 | _materialized_hypertable_13 | tablespace2 | _hyper_13_24_chunk | tablespace2 - whatever_view_2 | _materialized_hypertable_14 | tablespace1 | _hyper_14_25_chunk | tablespace1 -(2 rows) - -DROP MATERIALIZED VIEW whatever_view_1; -psql:include/cagg_ddl_common.sql:644: NOTICE: drop cascades to table _timescaledb_internal._hyper_13_24_chunk -DROP MATERIALIZED VIEW whatever_view_2; -psql:include/cagg_ddl_common.sql:645: NOTICE: drop cascades to table _timescaledb_internal._hyper_14_25_chunk --- test bucket width expressions on integer hypertables -CREATE TABLE metrics_int2 ( - time int2 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int4 ( - time int4 NOT NULL, - device_id int, - v1 float, - v2 float -); -CREATE TABLE metrics_int8 ( - time int8 NOT NULL, - device_id int, - v1 float, - v2 float -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10, replication_factor => 2) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - create_distributed_hypertable -------------------------------- - (15,public,metrics_int2,t) - (16,public,metrics_int4,t) - (17,public,metrics_int8,t) -(3 rows) - -\else -SELECT create_hypertable (('metrics_' || dt)::regclass, 'time', chunk_time_interval => 10) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); -\endif -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int2_now () - RETURNS int2 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int2 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int4_now () - RETURNS int4 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int4 -$$; -$DIST$); -\endif -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ -CREATE OR REPLACE FUNCTION int8_now () - RETURNS int8 - LANGUAGE SQL - STABLE - AS $$ - SELECT 10::int8 -$$; -$DIST$); -\endif -SELECT set_integer_now_func (('metrics_' || dt)::regclass, (dt || '_now')::regproc) -FROM ( - VALUES ('int2'), - ('int4'), - ('int8')) v (dt); - set_integer_now_func ----------------------- - - - -(3 rows) - --- width expression for int2 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:750: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1::smallint + 2::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:757: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int4 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:765: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:772: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; --- width expression for int8 hypertables -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:780: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:787: NOTICE: continuous aggregate "width_expr" is already up-to-date -DROP MATERIALIZED VIEW width_expr; -\set ON_ERROR_STOP 0 --- non-immutable expresions should be rejected -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::smallint, time) -FROM metrics_int2 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:796: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int4 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:801: ERROR: only immutable expressions allowed in time bucket function -CREATE MATERIALIZED VIEW width_expr WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(extract(year FROM now())::int, time) -FROM metrics_int8 -GROUP BY 1; -psql:include/cagg_ddl_common.sql:806: ERROR: only immutable expressions allowed in time bucket function -\set ON_ERROR_STOP 1 --- Test various ALTER MATERIALIZED VIEW statements. -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE MATERIALIZED VIEW owner_check WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(1 + 2, time) -FROM metrics_int8 -GROUP BY 1 -WITH NO DATA; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | default_perm_user -mat_table | _materialized_hypertable_24 -mat_table_owner | default_perm_user -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | default_perm_user -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | default_perm_user -tablespace | - -\x off --- This should not work since the target user has the wrong role, but --- we test that the normal checks are done when changing the owner. -\set ON_ERROR_STOP 0 -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -psql:include/cagg_ddl_common.sql:826: ERROR: must be member of role "test_role_1" -\set ON_ERROR_STOP 1 --- Superuser can always change owner -SET ROLE :ROLE_CLUSTER_SUPERUSER; -ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -\x on -SELECT * FROM cagg_info WHERE user_view::text = 'owner_check'; --[ RECORD 1 ]------+--------------------------------------- -user_view | owner_check -user_view_owner | test_role_1 -mat_table | _materialized_hypertable_24 -mat_table_owner | test_role_1 -direct_view | _timescaledb_internal._direct_view_24 -direct_view_owner | test_role_1 -partial_view | _timescaledb_internal._partial_view_24 -partial_view_owner | test_role_1 -tablespace | - -\x off --- --- Test drop continuous aggregate cases --- --- Issue: #2608 --- -CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS -$BODY$ - SELECT 50; -$BODY$; -\if :IS_DISTRIBUTED -CALL distributed_exec($DIST$ - CREATE OR REPLACE FUNCTION test_int_now() - RETURNS INT LANGUAGE SQL STABLE AS - $BODY$ - SELECT 50; - $BODY$; -$DIST$); -\endif -CREATE TABLE conditionsnm(time_int INT NOT NULL, device INT, value FLOAT); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10, replication_factor => 2); - create_distributed_hypertable -------------------------------- - (25,public,conditionsnm,t) -(1 row) - -\else -SELECT create_hypertable('conditionsnm', 'time_int', chunk_time_interval => 10); -\endif -SELECT set_integer_now_func('conditionsnm', 'test_int_now'); - set_integer_now_func ----------------------- - -(1 row) - -INSERT INTO conditionsnm -SELECT time_val, time_val % 4, 3.14 FROM generate_series(0,100,1) AS time_val; --- Case 1: DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:874: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4; -psql:include/cagg_ddl_common.sql:876: NOTICE: drop cascades to table _timescaledb_internal._hyper_26_37_chunk --- Case 2: DROP CASCADE should have similar behaviour as DROP -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:884: NOTICE: refreshing continuous aggregate "conditionsnm_4" -DROP materialized view conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:886: NOTICE: drop cascades to table _timescaledb_internal._hyper_27_38_chunk --- Case 3: require CASCADE in case of dependent object -CREATE MATERIALIZED VIEW conditionsnm_4 -WITH (timescaledb.continuous, timescaledb.materialized_only = TRUE) -AS -SELECT time_bucket(7, time_int) as bucket, -SUM(value), COUNT(value) -FROM conditionsnm GROUP BY bucket WITH DATA; -psql:include/cagg_ddl_common.sql:894: NOTICE: refreshing continuous aggregate "conditionsnm_4" -CREATE VIEW see_cagg as select * from conditionsnm_4; -\set ON_ERROR_STOP 0 -DROP MATERIALIZED VIEW conditionsnm_4; -psql:include/cagg_ddl_common.sql:898: ERROR: cannot drop view conditionsnm_4 because other objects depend on it -\set ON_ERROR_STOP 1 --- Case 4: DROP CASCADE with dependency -DROP MATERIALIZED VIEW conditionsnm_4 CASCADE; -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to view see_cagg -psql:include/cagg_ddl_common.sql:902: NOTICE: drop cascades to table _timescaledb_internal._hyper_28_39_chunk --- Test DROP SCHEMA CASCADE with continuous aggregates --- --- Issue: #2350 --- --- Case 1: DROP SCHEMA CASCADE -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (29,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.telemetry_1s - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME", - partial_view_name as "PART_VIEW_NAME", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'telemetry_1s'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME | PART_VIEW_NAME | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 29 | _timescaledb_internal | _materialized_hypertable_30 | _partial_view_30 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:941: NOTICE: drop cascades to 4 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'telemetry_1s'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - --- Case 2: DROP SCHEMA CASCADE with multiple caggs -CREATE SCHEMA test_schema; -CREATE TABLE test_schema.telemetry_raw ( - ts TIMESTAMP WITH TIME ZONE NOT NULL, - value DOUBLE PRECISION -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_schema.telemetry_raw', 'ts', replication_factor => 2); - create_distributed_hypertable ----------------------------------- - (31,test_schema,telemetry_raw,t) -(1 row) - -\else -SELECT create_hypertable('test_schema.telemetry_raw', 'ts'); -\endif -CREATE MATERIALIZED VIEW test_schema.cagg1 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -CREATE MATERIALIZED VIEW test_schema.cagg2 - WITH (timescaledb.continuous, timescaledb.materialized_only=false) - AS -SELECT time_bucket(INTERVAL '1s', ts) AS ts_1s, - avg(value) - FROM test_schema.telemetry_raw - GROUP BY ts_1s WITH NO DATA; -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME1", - partial_view_name as "PART_VIEW_NAME1", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg1'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME1 | PART_VIEW_NAME1 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_32 | _partial_view_32 | _timescaledb_internal -(1 row) - -\gset -SELECT ca.raw_hypertable_id, - h.schema_name, - h.table_name AS "MAT_TABLE_NAME2", - partial_view_name as "PART_VIEW_NAME2", - partial_view_schema -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cagg2'; - raw_hypertable_id | schema_name | MAT_TABLE_NAME2 | PART_VIEW_NAME2 | partial_view_schema --------------------+-----------------------+-----------------------------+------------------+----------------------- - 31 | _timescaledb_internal | _materialized_hypertable_33 | _partial_view_33 | _timescaledb_internal -(1 row) - -\gset -DROP SCHEMA test_schema CASCADE; -psql:include/cagg_ddl_common.sql:998: NOTICE: drop cascades to 7 other objects -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg1'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'MAT_TABLE_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = :'PART_VIEW_NAME2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_class WHERE relname = 'cagg2'; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_namespace WHERE nspname = 'test_schema'; - count -------- - 0 -(1 row) - -DROP TABLESPACE tablespace1; -DROP TABLESPACE tablespace2; --- Check that we can rename a column of a materialized view and still --- rebuild it after (#3051, #3405) -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (34,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -SELECT format('%I.%I', '_timescaledb_internal', h.table_name) AS "MAT_TABLE_NAME", - format('%I.%I', '_timescaledb_internal', partial_view_name) AS "PART_VIEW_NAME", - format('%I.%I', '_timescaledb_internal', direct_view_name) AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'conditions_daily' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns('conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - bucket | timestamp with time zone | t - avg | double precision | f -(3 rows) - -ALTER MATERIALIZED VIEW conditions_daily RENAME COLUMN bucket to "time"; --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -SELECT * FROM test.show_columns(' conditions_daily'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'DIRECT_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'PART_VIEW_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | f - avg | double precision | f -(3 rows) - -SELECT * FROM test.show_columns(:'MAT_TABLE_NAME'); - Column | Type | NotNull -----------+--------------------------+--------- - location | text | f - time | timestamp with time zone | t - avg | double precision | f -(3 rows) - --- This will rebuild the materialized view and should succeed. -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only = false); --- Refresh the continuous aggregate to check that it works after the --- rename. -\set VERBOSITY verbose -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -\set VERBOSITY terse --- --- Indexes on continuous aggregate --- -\set ON_ERROR_STOP 0 --- unique indexes are not supported -CREATE UNIQUE INDEX index_unique_error ON conditions_daily ("time", location); -psql:include/cagg_ddl_common.sql:1084: ERROR: continuous aggregates do not support UNIQUE indexes --- concurrently index creation not supported -CREATE INDEX CONCURRENTLY index_concurrently_avg ON conditions_daily (avg); -psql:include/cagg_ddl_common.sql:1086: ERROR: hypertables do not support concurrent index creation -\set ON_ERROR_STOP 1 -CREATE INDEX index_avg ON conditions_daily (avg); -CREATE INDEX index_avg_only ON ONLY conditions_daily (avg); -CREATE INDEX index_avg_include ON conditions_daily (avg) INCLUDE (location); -CREATE INDEX index_avg_expr ON conditions_daily ((avg + 1)); -CREATE INDEX index_avg_location_sfo ON conditions_daily (avg) WHERE location = 'SFO'; -CREATE INDEX index_avg_expr_location_sfo ON conditions_daily ((avg + 2)) WHERE location = 'SFO'; -SELECT * FROM test.show_indexespred(:'MAT_TABLE_NAME'); - Index | Columns | Expr | Pred | Unique | Primary | Exclusion | Tablespace ------------------------------------------------------------------------+-------------------+---------------------------+------------------------+--------+---------+-----------+------------ - _timescaledb_internal._materialized_hypertable_35_bucket_idx | {bucket} | | | f | f | f | - _timescaledb_internal._materialized_hypertable_35_location_bucket_idx | {location,bucket} | | | f | f | f | - _timescaledb_internal.index_avg | {avg} | | | f | f | f | - _timescaledb_internal.index_avg_expr | {expr} | avg + 1::double precision | | f | f | f | - _timescaledb_internal.index_avg_expr_location_sfo | {expr} | avg + 2::double precision | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_include | {avg,location} | | | f | f | f | - _timescaledb_internal.index_avg_location_sfo | {avg} | | location = 'SFO'::text | f | f | f | - _timescaledb_internal.index_avg_only | {avg} | | | f | f | f | -(8 rows) - --- #3696 assertion failure when referencing columns not present in result -CREATE TABLE i3696(time timestamptz NOT NULL, search_query text, cnt integer, cnt2 integer); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('i3696', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (36,public,i3696,t) -(1 row) - -\else -SELECT table_name FROM create_hypertable('i3696','time'); -\endif -CREATE MATERIALIZED VIEW i3696_cagg1 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt +cnt2 , bucket, search_query; -psql:include/cagg_ddl_common.sql:1108: NOTICE: continuous aggregate "i3696_cagg1" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg1 SET (timescaledb.materialized_only = 'true'); -CREATE MATERIALIZED VIEW i3696_cagg2 WITH (timescaledb.continuous, timescaledb.materialized_only=false) -AS - SELECT search_query,count(search_query) as count, sum(cnt), time_bucket(INTERVAL '1 minute', time) AS bucket - FROM i3696 GROUP BY cnt + cnt2, bucket, search_query - HAVING cnt + cnt2 + sum(cnt) > 2 or count(cnt2) > 10; -psql:include/cagg_ddl_common.sql:1116: NOTICE: continuous aggregate "i3696_cagg2" is already up-to-date -ALTER MATERIALIZED VIEW i3696_cagg2 SET (timescaledb.materialized_only = 'true'); ---TEST test with multiple settings on continuous aggregates -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -CREATE TABLE test_setting(time timestamptz not null, val numeric); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('test_setting', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (39,public,test_setting,t) -(1 row) - -\else -SELECT create_hypertable('test_setting', 'time'); -\endif -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only=false) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1130: NOTICE: continuous aggregate "test_setting_cagg" is already up-to-date -INSERT INTO test_setting -SELECT generate_series( '2020-01-10 8:00'::timestamp, '2020-01-30 10:00+00'::timestamptz, '1 day'::interval), 10.0; -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1141: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1149: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -DELETE FROM test_setting WHERE val = 20; ---TEST test with multiple settings on continuous aggregates with real time aggregates turned off initially -- --- test for materialized_only + compress combinations (real time aggs enabled initially) -DROP MATERIALIZED VIEW test_setting_cagg; -psql:include/cagg_ddl_common.sql:1174: NOTICE: drop cascades to table _timescaledb_internal._hyper_40_47_chunk -CREATE MATERIALIZED VIEW test_setting_cagg with (timescaledb.continuous, timescaledb.materialized_only = true) -AS SELECT time_bucket('1h',time), avg(val), count(*) FROM test_setting GROUP BY 1; -psql:include/cagg_ddl_common.sql:1177: NOTICE: refreshing continuous aggregate "test_setting_cagg" -CALL refresh_continuous_aggregate('test_setting_cagg', NULL, '2020-05-30 10:00+00'::timestamptz); -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - ---this row is not in the materialized result --- -INSERT INTO test_setting VALUES( '2020-11-01', 20); ---try out 2 settings here -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1185: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - ---now set it back to false -- -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='true'); -psql:include/cagg_ddl_common.sql:1193: NOTICE: defaulting compress_orderby to time_bucket -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | t | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'false', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | f -(1 row) - ---count should return additional data since we have real time aggs on -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 21 -(1 row) - -ALTER MATERIALIZED VIEW test_setting_cagg SET (timescaledb.materialized_only = 'true', timescaledb.compress='false'); -SELECT view_name, compression_enabled, materialized_only -FROM timescaledb_information.continuous_aggregates -where view_name = 'test_setting_cagg'; - view_name | compression_enabled | materialized_only --------------------+---------------------+------------------- - test_setting_cagg | f | t -(1 row) - ---real time aggs is off now , should return 20 -- -SELECT count(*) from test_setting_cagg ORDER BY 1; - count -------- - 20 -(1 row) - --- END TEST with multiple settings --- Test View Target Entries that contain both aggrefs and Vars in the same expression -CREATE TABLE transactions -( - "time" timestamp with time zone NOT NULL, - dummy1 integer, - dummy2 integer, - dummy3 integer, - dummy4 integer, - dummy5 integer, - amount integer, - fiat_value integer -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('transactions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (46,public,transactions,t) -(1 row) - -\else -SELECT create_hypertable('transactions', 'time'); -\endif -INSERT INTO transactions VALUES ( '2018-01-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:30:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:20:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-01-02 09:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 10:40:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 11:50:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 12:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-01 13:10:00-08', 0, 0, 0, 0, 0, -1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 09:20:00-08', 0, 0, 0, 0, 0, 1, 10); -INSERT INTO transactions VALUES ( '2018-11-02 10:30:00-08', 0, 0, 0, 0, 0, -1, 10); -CREATE materialized view cashflows( - bucket, - amount, - cashflow, - cashflow2 -) WITH ( - timescaledb.continuous, - timescaledb.materialized_only = true -) AS -SELECT time_bucket ('1 day', time) AS bucket, - amount, - CASE - WHEN amount < 0 THEN (0 - sum(fiat_value)) - ELSE sum(fiat_value) - END AS cashflow, - amount + sum(fiat_value) -FROM transactions -GROUP BY bucket, amount; -psql:include/cagg_ddl_common.sql:1267: NOTICE: refreshing continuous aggregate "cashflows" -SELECT h.table_name AS "MAT_TABLE_NAME", - partial_view_name AS "PART_VIEW_NAME", - direct_view_name AS "DIRECT_VIEW_NAME" -FROM _timescaledb_catalog.continuous_agg ca -INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = ca.mat_hypertable_id) -WHERE user_view_name = 'cashflows' -\gset --- Show both the columns and the view definitions to see that --- references are correct in the view as well. -\d+ "_timescaledb_internal".:"DIRECT_VIEW_NAME" - View "_timescaledb_internal._direct_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"PART_VIEW_NAME" - View "_timescaledb_internal._partial_view_47" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, - transactions.amount, - CASE - WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) - ELSE sum(transactions.fiat_value) - END AS cashflow, - transactions.amount + sum(transactions.fiat_value) AS cashflow2 - FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; - -\d+ "_timescaledb_internal".:"MAT_TABLE_NAME" - Table "_timescaledb_internal._materialized_hypertable_47" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ------------+--------------------------+-----------+----------+---------+---------+--------------+------------- - bucket | timestamp with time zone | | not null | | plain | | - amount | integer | | | | plain | | - cashflow | bigint | | | | plain | | - cashflow2 | bigint | | | | plain | | -Indexes: - "_materialized_hypertable_47_amount_bucket_idx" btree (amount, bucket DESC) - "_materialized_hypertable_47_bucket_idx" btree (bucket DESC) -Triggers: - ts_insert_blocker BEFORE INSERT ON _timescaledb_internal._materialized_hypertable_47 FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker() -Child tables: _timescaledb_internal._hyper_47_52_chunk, - _timescaledb_internal._hyper_47_53_chunk - -\d+ 'cashflows' - View "public.cashflows" - Column | Type | Collation | Nullable | Default | Storage | Description ------------+--------------------------+-----------+----------+---------+---------+------------- - bucket | timestamp with time zone | | | | plain | - amount | integer | | | | plain | - cashflow | bigint | | | | plain | - cashflow2 | bigint | | | | plain | -View definition: - SELECT _materialized_hypertable_47.bucket, - _materialized_hypertable_47.amount, - _materialized_hypertable_47.cashflow, - _materialized_hypertable_47.cashflow2 - FROM _timescaledb_internal._materialized_hypertable_47; - -SELECT * FROM cashflows; - bucket | amount | cashflow | cashflow2 -------------------------------+--------+----------+----------- - Sun Dec 31 16:00:00 2017 PST | 1 | 10 | 11 - Mon Jan 01 16:00:00 2018 PST | -1 | -30 | 29 - Wed Oct 31 17:00:00 2018 PDT | -1 | -20 | 19 - Wed Oct 31 17:00:00 2018 PDT | 1 | 30 | 31 - Thu Nov 01 17:00:00 2018 PDT | -1 | -10 | 9 - Thu Nov 01 17:00:00 2018 PDT | 1 | 10 | 11 -(6 rows) - --- test cagg creation with named arguments in time_bucket --- note that positional arguments cannot follow named arguments --- 1. test named origin --- 2. test named timezone --- 3. test named ts --- 4. test named bucket width --- named origin -CREATE MATERIALIZED VIEW cagg_named_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named timezone -CREATE MATERIALIZED VIEW cagg_named_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named ts -CREATE MATERIALIZED VIEW cagg_named_ts_tz_origin WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket('1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- named bucket width -CREATE MATERIALIZED VIEW cagg_named_all WITH -(timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(bucket_width => '1h', ts => time, timezone => 'UTC', origin => '2001-01-03 01:23:45') AS bucket, -avg(amount) as avg_amount -FROM transactions GROUP BY 1 WITH NO DATA; --- Refreshing from the beginning (NULL) of a CAGG with variable time bucket and --- using an INTERVAL for the end timestamp (issue #5534) -CREATE MATERIALIZED VIEW transactions_montly -WITH (timescaledb.continuous, timescaledb.materialized_only = true) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) - FROM transactions -GROUP BY 1 -WITH NO DATA; --- No rows -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min ---------+-----+-----+----- -(0 rows) - --- Refresh from beginning of the CAGG for 1 month -CALL refresh_continuous_aggregate('transactions_montly', NULL, INTERVAL '1 month'); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - -TRUNCATE transactions_montly; --- Partial refresh the CAGG from beginning to an specific timestamp -CALL refresh_continuous_aggregate('transactions_montly', NULL, '2018-11-01 11:50:00-08'::timestamptz); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 -(1 row) - --- Full refresh the CAGG -CALL refresh_continuous_aggregate('transactions_montly', NULL, NULL); -SELECT * FROM transactions_montly ORDER BY bucket; - bucket | sum | max | min -------------------------------+-----+-----+----- - Sun Dec 31 16:00:00 2017 PST | 40 | 10 | 10 - Wed Oct 31 17:00:00 2018 PDT | 70 | 10 | 10 -(2 rows) - --- Check set_chunk_time_interval on continuous aggregate -CREATE MATERIALIZED VIEW cagg_set_chunk_time_interval -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT time_bucket(INTERVAL '1 month', time) AS bucket, - SUM(fiat_value), - MAX(fiat_value), - MIN(fiat_value) -FROM transactions -GROUP BY 1 -WITH NO DATA; -SELECT set_chunk_time_interval('cagg_set_chunk_time_interval', chunk_time_interval => interval '1 month'); - set_chunk_time_interval -------------------------- - -(1 row) - -CALL refresh_continuous_aggregate('cagg_set_chunk_time_interval', NULL, NULL); -SELECT _timescaledb_functions.to_interval(d.interval_length) = interval '1 month' -FROM _timescaledb_catalog.dimension d - RIGHT JOIN _timescaledb_catalog.continuous_agg ca ON ca.user_view_name = 'cagg_set_chunk_time_interval' -WHERE d.hypertable_id = ca.mat_hypertable_id; - ?column? ----------- - t -(1 row) - --- Since #6077 CAggs are materialized only by default -DROP TABLE conditions CASCADE; -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 3 other objects -psql:include/cagg_ddl_common.sql:1365: NOTICE: drop cascades to 2 other objects -CREATE TABLE conditions ( - time TIMESTAMPTZ NOT NULL, - location TEXT NOT NULL, - temperature DOUBLE PRECISION NULL -); -\if :IS_DISTRIBUTED -SELECT create_distributed_hypertable('conditions', 'time', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (54,public,conditions,t) -(1 row) - -\else -SELECT create_hypertable('conditions', 'time'); -\endif -INSERT INTO conditions VALUES ( '2018-01-01 09:20:00-08', 'SFO', 55); -INSERT INTO conditions VALUES ( '2018-01-02 09:30:00-08', 'por', 100); -INSERT INTO conditions VALUES ( '2018-01-02 09:20:00-08', 'SFO', 65); -INSERT INTO conditions VALUES ( '2018-01-02 09:10:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 09:20:00-08', 'NYC', 45); -INSERT INTO conditions VALUES ( '2018-11-01 10:40:00-08', 'NYC', 55); -INSERT INTO conditions VALUES ( '2018-11-01 11:50:00-08', 'NYC', 65); -INSERT INTO conditions VALUES ( '2018-11-01 12:10:00-08', 'NYC', 75); -INSERT INTO conditions VALUES ( '2018-11-01 13:10:00-08', 'NYC', 85); -INSERT INTO conditions VALUES ( '2018-11-02 09:20:00-08', 'NYC', 10); -INSERT INTO conditions VALUES ( '2018-11-02 10:30:00-08', 'NYC', 20); -CREATE MATERIALIZED VIEW conditions_daily -WITH (timescaledb.continuous) AS -SELECT location, - time_bucket(INTERVAL '1 day', time) AS bucket, - AVG(temperature) - FROM conditions -GROUP BY location, bucket -WITH NO DATA; -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - --- Should return NO ROWS -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+--------+----- -(0 rows) - -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=false); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55 - WHERE _materialized_hypertable_55.bucket < COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) -UNION ALL - SELECT conditions.location, - time_bucket('@ 1 day'::interval, conditions."time") AS bucket, - avg(conditions.temperature) AS avg - FROM conditions - WHERE conditions."time" >= COALESCE(_timescaledb_functions.to_timestamp(_timescaledb_functions.cagg_watermark(55)), '-infinity'::timestamp with time zone) - GROUP BY conditions.location, (time_bucket('@ 1 day'::interval, conditions."time")); - --- Should return ROWS because now it is realtime -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- Should return ROWS because we refreshed it -ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=true); -\d+ conditions_daily - View "public.conditions_daily" - Column | Type | Collation | Nullable | Default | Storage | Description -----------+--------------------------+-----------+----------+---------+----------+------------- - location | text | | | | extended | - bucket | timestamp with time zone | | | | plain | - avg | double precision | | | | plain | -View definition: - SELECT _materialized_hypertable_55.location, - _materialized_hypertable_55.bucket, - _materialized_hypertable_55.avg - FROM _timescaledb_internal._materialized_hypertable_55; - -CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); -SELECT * FROM conditions_daily ORDER BY bucket, avg; - location | bucket | avg -----------+------------------------------+----- - SFO | Sun Dec 31 16:00:00 2017 PST | 55 - SFO | Mon Jan 01 16:00:00 2018 PST | 65 - NYC | Mon Jan 01 16:00:00 2018 PST | 65 - por | Mon Jan 01 16:00:00 2018 PST | 100 - NYC | Wed Oct 31 17:00:00 2018 PDT | 65 - NYC | Thu Nov 01 17:00:00 2018 PDT | 15 -(6 rows) - --- cleanup -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); diff --git a/tsl/test/expected/cagg_ddl_dist_ht-16.out b/tsl/test/expected/cagg_ddl_dist_ht.out similarity index 98% rename from tsl/test/expected/cagg_ddl_dist_ht-16.out rename to tsl/test/expected/cagg_ddl_dist_ht.out index b4d7ef8aca8..d2ea010eda9 100644 --- a/tsl/test/expected/cagg_ddl_dist_ht-16.out +++ b/tsl/test/expected/cagg_ddl_dist_ht.out @@ -1226,7 +1226,7 @@ tablespace | -- we test that the normal checks are done when changing the owner. \set ON_ERROR_STOP 0 ALTER MATERIALIZED VIEW owner_check OWNER TO :ROLE_1; -psql:include/cagg_ddl_common.sql:831: ERROR: must be able to SET ROLE "test_role_1" +psql:include/cagg_ddl_common.sql:831: ERROR: must be member of role "test_role_1" \set ON_ERROR_STOP 1 -- Superuser can always change owner SET ROLE :ROLE_CLUSTER_SUPERUSER; @@ -1916,15 +1916,15 @@ WHERE user_view_name = 'cashflows' cashflow | bigint | | | | plain | cashflow2 | bigint | | | | plain | View definition: - SELECT time_bucket('@ 1 day'::interval, "time") AS bucket, - amount, + SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, + transactions.amount, CASE - WHEN amount < 0 THEN 0 - sum(fiat_value) - ELSE sum(fiat_value) + WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) + ELSE sum(transactions.fiat_value) END AS cashflow, - amount + sum(fiat_value) AS cashflow2 + transactions.amount + sum(transactions.fiat_value) AS cashflow2 FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, "time")), amount; + GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; \d+ "_timescaledb_internal".:"PART_VIEW_NAME" View "_timescaledb_internal._partial_view_47" @@ -1935,15 +1935,15 @@ View definition: cashflow | bigint | | | | plain | cashflow2 | bigint | | | | plain | View definition: - SELECT time_bucket('@ 1 day'::interval, "time") AS bucket, - amount, + SELECT time_bucket('@ 1 day'::interval, transactions."time") AS bucket, + transactions.amount, CASE - WHEN amount < 0 THEN 0 - sum(fiat_value) - ELSE sum(fiat_value) + WHEN transactions.amount < 0 THEN 0 - sum(transactions.fiat_value) + ELSE sum(transactions.fiat_value) END AS cashflow, - amount + sum(fiat_value) AS cashflow2 + transactions.amount + sum(transactions.fiat_value) AS cashflow2 FROM transactions - GROUP BY (time_bucket('@ 1 day'::interval, "time")), amount; + GROUP BY (time_bucket('@ 1 day'::interval, transactions."time")), transactions.amount; \d+ "_timescaledb_internal".:"MAT_TABLE_NAME" Table "_timescaledb_internal._materialized_hypertable_47" @@ -1970,10 +1970,10 @@ Child tables: _timescaledb_internal._hyper_47_52_chunk, cashflow | bigint | | | | plain | cashflow2 | bigint | | | | plain | View definition: - SELECT bucket, - amount, - cashflow, - cashflow2 + SELECT _materialized_hypertable_47.bucket, + _materialized_hypertable_47.amount, + _materialized_hypertable_47.cashflow, + _materialized_hypertable_47.cashflow2 FROM _timescaledb_internal._materialized_hypertable_47; SELECT * FROM cashflows; @@ -2133,9 +2133,9 @@ WITH NO DATA; bucket | timestamp with time zone | | | | plain | avg | double precision | | | | plain | View definition: - SELECT location, - bucket, - avg + SELECT _materialized_hypertable_55.location, + _materialized_hypertable_55.bucket, + _materialized_hypertable_55.avg FROM _timescaledb_internal._materialized_hypertable_55; -- Should return NO ROWS @@ -2188,9 +2188,9 @@ ALTER MATERIALIZED VIEW conditions_daily SET (timescaledb.materialized_only=true bucket | timestamp with time zone | | | | plain | avg | double precision | | | | plain | View definition: - SELECT location, - bucket, - avg + SELECT _materialized_hypertable_55.location, + _materialized_hypertable_55.bucket, + _materialized_hypertable_55.avg FROM _timescaledb_internal._materialized_hypertable_55; CALL refresh_continuous_aggregate('conditions_daily', NULL, NULL); diff --git a/tsl/test/expected/chunk_api.out b/tsl/test/expected/chunk_api.out index 5e07f28e5cb..1051f479e22 100644 --- a/tsl/test/expected/chunk_api.out +++ b/tsl/test/expected/chunk_api.out @@ -2,23 +2,6 @@ -- Please see the included NOTICE for copyright information and -- LICENSE-TIMESCALE for a copy of the license. \c :TEST_DBNAME :ROLE_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; GRANT CREATE ON DATABASE :"TEST_DBNAME" TO :ROLE_DEFAULT_PERM_USER; SET ROLE :ROLE_DEFAULT_PERM_USER; CREATE SCHEMA "ChunkSchema"; @@ -270,301 +253,6 @@ ORDER BY tablename, attname; _hyper_1_1_chunk | time | f | 0 | 8 | -1 (4 rows) --- Test getting chunk stats on a distribute hypertable -SET ROLE :ROLE_CLUSTER_SUPERUSER; -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2')) v(name) -) a; - node_name | database | node_created | database_created | extension_created -----------------+----------------+--------------+------------------+------------------- - db_chunk_api_1 | db_chunk_api_1 | t | t | t - db_chunk_api_2 | db_chunk_api_2 | t | t | t -(2 rows) - -GRANT USAGE - ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2 - TO :ROLE_1, :ROLE_DEFAULT_PERM_USER; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_1; -SET ROLE :ROLE_1; -CREATE TABLE disttable (time timestamptz, device int, temp float, color text); -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device'); -NOTICE: adding not-null constraint to column "time" - hypertable_id | schema_name | table_name | created ----------------+-------------+------------+--------- - 2 | public | disttable | t -(1 row) - -INSERT INTO disttable VALUES ('2018-01-01 05:00:00-8', 1, 23.4, 'green'), - ('2018-01-01 06:00:00-8', 4, 22.3, NULL), - ('2018-01-01 06:00:00-8', 1, 21.1, 'green'); --- Make sure we get deterministic behavior across all nodes -CALL distributed_exec($$ SELECT setseed(1); $$); --- No stats on the local table -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); - chunk_id | hypertable_id | num_pages | num_tuples | num_allvisible -----------+---------------+-----------+------------+---------------- - 3 | 2 | 0 | 0 | 0 - 4 | 2 | 0 | 0 | 0 -(2 rows) - -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - chunk_id | hypertable_id | att_num | nullfrac | width | distinctval | slotkind | slotopstrings | slotcollations | slot1numbers | slot2numbers | slot3numbers | slot4numbers | slot5numbers | slotvaluetypetrings | slot1values | slot2values | slot3values | slot4values | slot5values -----------+---------------+---------+----------+-------+-------------+----------+---------------+----------------+--------------+--------------+--------------+--------------+--------------+---------------------+-------------+-------------+-------------+-------------+------------- -(0 rows) - -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; - relname | reltuples | relpages | relallvisible ------------------------+-----------+----------+--------------- - _dist_hyper_2_3_chunk | 0 | 0 | 0 - _dist_hyper_2_4_chunk | 0 | 0 | 0 -(2 rows) - -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram -------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+---------------------- -(0 rows) - --- Run ANALYZE on data node 1 -CALL distributed_exec('ANALYZE disttable', ARRAY[:'DATA_NODE_1']); --- Stats should now be refreshed after running get_chunk_{col,rel}stats -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; - relname | reltuples | relpages | relallvisible ------------------------+-----------+----------+--------------- - _dist_hyper_2_3_chunk | 0 | 0 | 0 - _dist_hyper_2_4_chunk | 0 | 0 | 0 -(2 rows) - -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram -------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+---------------------- -(0 rows) - -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); - chunk_id | hypertable_id | num_pages | num_tuples | num_allvisible -----------+---------------+-----------+------------+---------------- - 3 | 2 | 1 | 2 | 0 - 4 | 2 | 0 | 0 | 0 -(2 rows) - -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - chunk_id | hypertable_id | att_num | nullfrac | width | distinctval | slotkind | slotopstrings | slotcollations | slot1numbers | slot2numbers | slot3numbers | slot4numbers | slot5numbers | slotvaluetypetrings | slot1values | slot2values | slot3values | slot4values | slot5values -----------+---------------+---------+----------+-------+-------------+-------------+-------------------------------------------------------------------------------------------------------------------------+-----------------+--------------+--------------+--------------+--------------+--------------+--------------------------+-----------------------------------------------------------------+-------------+-------------+-------------+------------- - 3 | 2 | 1 | 0 | 8 | -1 | {2,3,0,0,0} | {<,pg_catalog,timestamptz,pg_catalog,timestamptz,pg_catalog,<,pg_catalog,timestamptz,pg_catalog,timestamptz,pg_catalog} | {0,0,0,0,0} | | {1} | | | | {timestamptz,pg_catalog} | {"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"} | | | | - 3 | 2 | 2 | 0 | 4 | -0.5 | {1,3,0,0,0} | {=,pg_catalog,int4,pg_catalog,int4,pg_catalog,<,pg_catalog,int4,pg_catalog,int4,pg_catalog} | {0,0,0,0,0} | {1} | {1} | | | | {int4,pg_catalog} | {1} | | | | - 3 | 2 | 3 | 0 | 8 | -1 | {2,3,0,0,0} | {<,pg_catalog,float8,pg_catalog,float8,pg_catalog,<,pg_catalog,float8,pg_catalog,float8,pg_catalog} | {0,0,0,0,0} | | {-1} | | | | {float8,pg_catalog} | {21.1,23.4} | | | | - 3 | 2 | 4 | 0 | 6 | -0.5 | {1,3,0,0,0} | {=,pg_catalog,text,pg_catalog,text,pg_catalog,<,pg_catalog,text,pg_catalog,text,pg_catalog} | {100,100,0,0,0} | {1} | {1} | | | | {text,pg_catalog} | {green} | | | | -(4 rows) - -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; - relname | reltuples | relpages | relallvisible ------------------------+-----------+----------+--------------- - _dist_hyper_2_3_chunk | 2 | 1 | 0 - _dist_hyper_2_4_chunk | 0 | 0 | 0 -(2 rows) - -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram ------------------------+-----------------------+---------+-----------+-----------+-----------+------------+------------------+-------------------+-----------------------------------------------------------------+-------------+-------------------+------------------------+---------------------- - _timescaledb_internal | _dist_hyper_2_3_chunk | color | f | 0 | 6 | -0.5 | {green} | {1} | | 1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | device | f | 0 | 4 | -0.5 | {1} | {1} | | 1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | temp | f | 0 | 8 | -1 | | | {21.1,23.4} | -1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | time | f | 0 | 8 | -1 | | | {"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"} | 1 | | | -(4 rows) - --- Test that user without table permissions can't get column stats -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - chunk_id | hypertable_id | att_num | nullfrac | width | distinctval | slotkind | slotopstrings | slotcollations | slot1numbers | slot2numbers | slot3numbers | slot4numbers | slot5numbers | slotvaluetypetrings | slot1values | slot2values | slot3values | slot4values | slot5values -----------+---------------+---------+----------+-------+-------------+----------+---------------+----------------+--------------+--------------+--------------+--------------+--------------+---------------------+-------------+-------------+-------------+-------------+------------- -(0 rows) - -SET ROLE :ROLE_1; --- Run ANALYZE again, but on both nodes. -ANALYZE disttable; --- Now expect stats from all data node chunks -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); - chunk_id | hypertable_id | num_pages | num_tuples | num_allvisible -----------+---------------+-----------+------------+---------------- - 3 | 2 | 1 | 2 | 0 - 4 | 2 | 1 | 1 | 0 -(2 rows) - -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - chunk_id | hypertable_id | att_num | nullfrac | width | distinctval | slotkind | slotopstrings | slotcollations | slot1numbers | slot2numbers | slot3numbers | slot4numbers | slot5numbers | slotvaluetypetrings | slot1values | slot2values | slot3values | slot4values | slot5values -----------+---------------+---------+----------+-------+-------------+-------------+-------------------------------------------------------------------------------------------------------------------------+-----------------+--------------+--------------+--------------+--------------+--------------+--------------------------+-----------------------------------------------------------------+-------------+-------------+-------------+------------- - 3 | 2 | 1 | 0 | 8 | -1 | {2,3,0,0,0} | {<,pg_catalog,timestamptz,pg_catalog,timestamptz,pg_catalog,<,pg_catalog,timestamptz,pg_catalog,timestamptz,pg_catalog} | {0,0,0,0,0} | | {1} | | | | {timestamptz,pg_catalog} | {"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"} | | | | - 3 | 2 | 2 | 0 | 4 | -0.5 | {1,3,0,0,0} | {=,pg_catalog,int4,pg_catalog,int4,pg_catalog,<,pg_catalog,int4,pg_catalog,int4,pg_catalog} | {0,0,0,0,0} | {1} | {1} | | | | {int4,pg_catalog} | {1} | | | | - 3 | 2 | 3 | 0 | 8 | -1 | {2,3,0,0,0} | {<,pg_catalog,float8,pg_catalog,float8,pg_catalog,<,pg_catalog,float8,pg_catalog,float8,pg_catalog} | {0,0,0,0,0} | | {-1} | | | | {float8,pg_catalog} | {21.1,23.4} | | | | - 3 | 2 | 4 | 0 | 6 | -0.5 | {1,3,0,0,0} | {=,pg_catalog,text,pg_catalog,text,pg_catalog,<,pg_catalog,text,pg_catalog,text,pg_catalog} | {100,100,0,0,0} | {1} | {1} | | | | {text,pg_catalog} | {green} | | | | - 4 | 2 | 1 | 0 | 8 | -1 | {0,0,0,0,0} | {} | {0,0,0,0,0} | | | | | | {} | | | | | - 4 | 2 | 2 | 0 | 4 | -1 | {0,0,0,0,0} | {} | {0,0,0,0,0} | | | | | | {} | | | | | - 4 | 2 | 3 | 0 | 8 | -1 | {0,0,0,0,0} | {} | {0,0,0,0,0} | | | | | | {} | | | | | - 4 | 2 | 4 | 1 | 0 | 0 | {0,0,0,0,0} | {} | {0,0,0,0,0} | | | | | | {} | | | | | -(8 rows) - --- Test ANALYZE with a replica chunk. We'd like to ensure the --- stats-fetching functions handle duplicate stats from different (but --- identical) replica chunks. -SELECT set_replication_factor('disttable', 2); -WARNING: hypertable "disttable" is under-replicated - set_replication_factor ------------------------- - -(1 row) - -INSERT INTO disttable VALUES ('2019-01-01 05:00:00-8', 1, 23.4, 'green'); --- Run twice to test that stats-fetching functions handle replica chunks. -ANALYZE disttable; -ANALYZE disttable; -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; - relname | reltuples | relpages | relallvisible ------------------------+-----------+----------+--------------- - _dist_hyper_2_3_chunk | 2 | 1 | 0 - _dist_hyper_2_4_chunk | 1 | 1 | 0 - _dist_hyper_2_5_chunk | 1 | 1 | 0 -(3 rows) - -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram ------------------------+-----------------------+---------+-----------+-----------+-----------+------------+------------------+-------------------+-----------------------------------------------------------------+-------------+-------------------+------------------------+---------------------- - _timescaledb_internal | _dist_hyper_2_3_chunk | color | f | 0 | 6 | -0.5 | {green} | {1} | | 1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | device | f | 0 | 4 | -0.5 | {1} | {1} | | 1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | temp | f | 0 | 8 | -1 | | | {21.1,23.4} | -1 | | | - _timescaledb_internal | _dist_hyper_2_3_chunk | time | f | 0 | 8 | -1 | | | {"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"} | 1 | | | - _timescaledb_internal | _dist_hyper_2_4_chunk | color | f | 1 | 0 | 0 | | | | | | | - _timescaledb_internal | _dist_hyper_2_4_chunk | device | f | 0 | 4 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_4_chunk | temp | f | 0 | 8 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_4_chunk | time | f | 0 | 8 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_5_chunk | color | f | 0 | 6 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_5_chunk | device | f | 0 | 4 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_5_chunk | temp | f | 0 | 8 | -1 | | | | | | | - _timescaledb_internal | _dist_hyper_2_5_chunk | time | f | 0 | 8 | -1 | | | | | | | -(12 rows) - --- Check underlying pg_statistics table (looking at all columns except --- starelid, which changes depending on how many tests are run before --- this) -RESET ROLE; -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum; - ch | staattnum | stainherit | stanullfrac | stawidth | stadistinct | stakind1 | stakind2 | stakind3 | stakind4 | stakind5 | staop1 | staop2 | staop3 | staop4 | staop5 | stanumbers1 | stanumbers2 | stanumbers3 | stanumbers4 | stanumbers5 | stavalues1 | stavalues2 | stavalues3 | stavalues4 | stavalues5 ----------------------------------------------+-----------+------------+-------------+----------+-------------+----------+----------+----------+----------+----------+--------+--------+--------+--------+--------+-------------+-------------+-------------+-------------+-------------+-----------------------------------------------------------------+------------+------------+------------+------------ - _timescaledb_internal._dist_hyper_2_3_chunk | 1 | f | 0 | 8 | -1 | 2 | 3 | 0 | 0 | 0 | 1322 | 1322 | 0 | 0 | 0 | | {1} | | | | {"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"} | | | | - _timescaledb_internal._dist_hyper_2_3_chunk | 2 | f | 0 | 4 | -0.5 | 1 | 3 | 0 | 0 | 0 | 96 | 97 | 0 | 0 | 0 | {1} | {1} | | | | {1} | | | | - _timescaledb_internal._dist_hyper_2_3_chunk | 3 | f | 0 | 8 | -1 | 2 | 3 | 0 | 0 | 0 | 672 | 672 | 0 | 0 | 0 | | {-1} | | | | {21.1,23.4} | | | | - _timescaledb_internal._dist_hyper_2_3_chunk | 4 | f | 0 | 6 | -0.5 | 1 | 3 | 0 | 0 | 0 | 98 | 664 | 0 | 0 | 0 | {1} | {1} | | | | {green} | | | | - _timescaledb_internal._dist_hyper_2_4_chunk | 1 | f | 0 | 8 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_4_chunk | 2 | f | 0 | 4 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_4_chunk | 3 | f | 0 | 8 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_4_chunk | 4 | f | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_5_chunk | 1 | f | 0 | 8 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_5_chunk | 2 | f | 0 | 4 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_5_chunk | 3 | f | 0 | 8 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | - _timescaledb_internal._dist_hyper_2_5_chunk | 4 | f | 0 | 6 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | | | | | | | -(12 rows) - -SELECT test.remote_exec(NULL, $$ -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum; -$$); -NOTICE: [db_chunk_api_1]: -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum -NOTICE: [db_chunk_api_1]: -ch |staattnum|stainherit|stanullfrac|stawidth|stadistinct|stakind1|stakind2|stakind3|stakind4|stakind5|staop1|staop2|staop3|staop4|staop5|stanumbers1|stanumbers2|stanumbers3|stanumbers4|stanumbers5|stavalues1 |stavalues2|stavalues3|stavalues4|stavalues5 --------------------------------------------+---------+----------+-----------+--------+-----------+--------+--------+--------+--------+--------+------+------+------+------+------+-----------+-----------+-----------+-----------+-----------+---------------------------------------------------------------+----------+----------+----------+---------- -_timescaledb_internal._dist_hyper_2_3_chunk| 1|f | 0| 8| -1| 2| 3| 0| 0| 0| 1322| 1322| 0| 0| 0| |{1} | | | |{"Mon Jan 01 05:00:00 2018 PST","Mon Jan 01 06:00:00 2018 PST"}| | | | -_timescaledb_internal._dist_hyper_2_3_chunk| 2|f | 0| 4| -0.5| 1| 3| 0| 0| 0| 96| 97| 0| 0| 0|{1} |{1} | | | |{1} | | | | -_timescaledb_internal._dist_hyper_2_3_chunk| 3|f | 0| 8| -1| 2| 3| 0| 0| 0| 672| 672| 0| 0| 0| |{-1} | | | |{21.1,23.4} | | | | -_timescaledb_internal._dist_hyper_2_3_chunk| 4|f | 0| 6| -0.5| 1| 3| 0| 0| 0| 98| 664| 0| 0| 0|{1} |{1} | | | |{green} | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 1|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 2|f | 0| 4| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 3|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 4|f | 0| 6| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -(8 rows) - - -NOTICE: [db_chunk_api_2]: -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum -NOTICE: [db_chunk_api_2]: -ch |staattnum|stainherit|stanullfrac|stawidth|stadistinct|stakind1|stakind2|stakind3|stakind4|stakind5|staop1|staop2|staop3|staop4|staop5|stanumbers1|stanumbers2|stanumbers3|stanumbers4|stanumbers5|stavalues1|stavalues2|stavalues3|stavalues4|stavalues5 --------------------------------------------+---------+----------+-----------+--------+-----------+--------+--------+--------+--------+--------+------+------+------+------+------+-----------+-----------+-----------+-----------+-----------+----------+----------+----------+----------+---------- -_timescaledb_internal._dist_hyper_2_4_chunk| 1|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_4_chunk| 2|f | 0| 4| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_4_chunk| 3|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_4_chunk| 4|f | 1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 1|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 2|f | 0| 4| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 3|f | 0| 8| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -_timescaledb_internal._dist_hyper_2_5_chunk| 4|f | 0| 6| -1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| | | | | | | | | | -(8 rows) - - - remote_exec -------------- - -(1 row) - --- Clean up -RESET ROLE; -TRUNCATE disttable; -SELECT * FROM delete_data_node(:'DATA_NODE_1', force => true); -WARNING: insufficient number of data nodes for distributed hypertable "disttable" -NOTICE: the number of partitions in dimension "device" of hypertable "disttable" was decreased to 1 - delete_data_node ------------------- - t -(1 row) - -SELECT * FROM delete_data_node(:'DATA_NODE_2', force => true); -WARNING: insufficient number of data nodes for distributed hypertable "disttable" - delete_data_node ------------------- - t -(1 row) - -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -- Test create_chunk_table to recreate the chunk table and show dimension slices SET ROLE :ROLE_DEFAULT_PERM_USER; SELECT * FROM chunkapi ORDER BY time; @@ -637,7 +325,7 @@ SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 2); NOTICE: adding not-null constraint to column "time" hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 3 | public | chunkapi | t + 2 | public | chunkapi | t (1 row) SELECT count(*) FROM @@ -654,7 +342,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 2, '3d'); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 4 | public | chunkapi | t + 3 | public | chunkapi | t (1 row) SELECT count(*) FROM @@ -670,7 +358,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 3); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 5 | public | chunkapi | t + 4 | public | chunkapi | t (1 row) SELECT count(*) FROM @@ -687,7 +375,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 3); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 6 | public | chunkapi | t + 5 | public | chunkapi | t (1 row) INSERT INTO chunkapi VALUES ('2018-01-01 05:00:00-8', 1, 23.4); @@ -701,7 +389,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 2); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 7 | public | chunkapi | t + 6 | public | chunkapi | t (1 row) INSERT INTO chunkapi VALUES ('2018-01-01 05:00:00-8', 2, 23.4); @@ -718,7 +406,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 2); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 8 | public | chunkapi | t + 7 | public | chunkapi | t (1 row) INSERT INTO chunkapi VALUES ('2018-02-01 05:00:00-8', 1, 23.4); @@ -744,7 +432,7 @@ CREATE TABLE chunkapi (time timestamptz not null, device int, temp float); SELECT * FROM create_hypertable('chunkapi', 'time', 'device', 3); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 9 | public | chunkapi | t + 8 | public | chunkapi | t (1 row) SELECT attach_tablespace('tablespace1', 'chunkapi'); @@ -781,7 +469,7 @@ CREATE TABLE chunkapi (time timestamptz NOT NULL PRIMARY KEY, device int REFEREN SELECT * FROM create_hypertable('chunkapi', 'time'); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 10 | public | chunkapi | t + 9 | public | chunkapi | t (1 row) INSERT INTO chunkapi VALUES ('2018-01-01 05:00:00-8', 1, 23.4); @@ -828,43 +516,43 @@ SELECT count(*) FROM SELECT tablespace FROM pg_tables WHERE tablename = :'CHUNK_NAME'; tablespace ------------- - tablespace1 + tablespace2 (1 row) -- Now create the complete chunk from the chunk table SELECT _timescaledb_functions.create_chunk('chunkapi', :'SLICES', :'CHUNK_SCHEMA', :'CHUNK_NAME', format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); - create_chunk ---------------------------------------------------------------------------------------------------------- - (11,10,_timescaledb_internal,_hyper_10_10_chunk,r,"{""time"": [1514419200000000, 1515024000000000]}",t) + create_chunk +----------------------------------------------------------------------------------------------------- + (8,9,_timescaledb_internal,_hyper_9_7_chunk,r,"{""time"": [1514419200000000, 1515024000000000]}",t) (1 row) -- Compare original and new constraints SELECT * FROM original_chunk_constraints; - Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- - 10_1_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t - 10_2_chunkapi_pkey | p | {time} | _timescaledb_internal."10_2_chunkapi_pkey" | | f | f | t - chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t - constraint_15 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t + Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated +--------------------------+------+----------+-------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- + 7_1_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 7_2_chunkapi_pkey | p | {time} | _timescaledb_internal."7_2_chunkapi_pkey" | | f | f | t + chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t + constraint_11 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t (4 rows) SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); - Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- - 11_3_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t - 11_4_chunkapi_pkey | p | {time} | _timescaledb_internal."11_4_chunkapi_pkey" | | f | f | t - chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t - constraint_16 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t + Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated +--------------------------+------+----------+-------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- + 8_3_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 8_4_chunkapi_pkey | p | {time} | _timescaledb_internal."8_4_chunkapi_pkey" | | f | f | t + chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t + constraint_12 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t (4 rows) -- Compare original and new chunk constraints metadata SELECT * FROM original_chunk_constraints_metadata; - chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name -----------+--------------------+---------------------------+---------------------------- - 10 | | 10_1_chunkapi_device_fkey | chunkapi_device_fkey - 10 | | 10_2_chunkapi_pkey | chunkapi_pkey - 10 | 15 | constraint_15 | + chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name +----------+--------------------+--------------------------+---------------------------- + 7 | | 7_1_chunkapi_device_fkey | chunkapi_device_fkey + 7 | | 7_2_chunkapi_pkey | chunkapi_pkey + 7 | 11 | constraint_11 | (3 rows) SELECT @@ -875,11 +563,11 @@ SELECT FROM _timescaledb_catalog.chunk_constraint con INNER JOIN _timescaledb_catalog.chunk ch ON (con.chunk_id = ch.id) WHERE ch.schema_name = :'CHUNK_SCHEMA' AND ch.table_name = :'CHUNK_NAME'; - chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name -----------+--------------------+---------------------------+---------------------------- - 11 | | 11_3_chunkapi_device_fkey | chunkapi_device_fkey - 11 | | 11_4_chunkapi_pkey | chunkapi_pkey - 11 | 16 | constraint_16 | + chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name +----------+--------------------+--------------------------+---------------------------- + 8 | | 8_3_chunkapi_device_fkey | chunkapi_device_fkey + 8 | | 8_4_chunkapi_pkey | chunkapi_pkey + 8 | 12 | constraint_12 | (3 rows) DROP TABLE original_chunk_constraints; @@ -899,9 +587,9 @@ SELECT :'CHUNK_NAME' AS expected_table_name, (_timescaledb_functions.show_chunk(ch)).* FROM show_chunks('chunkapi') ch; - expected_schema | expected_table_name | chunk_id | hypertable_id | schema_name | table_name | relkind | slices ------------------------+---------------------+----------+---------------+-----------------------+--------------------+---------+------------------------------------------------ - _timescaledb_internal | _hyper_10_10_chunk | 11 | 10 | _timescaledb_internal | _hyper_10_10_chunk | r | {"time": [1514419200000000, 1515024000000000]} + expected_schema | expected_table_name | chunk_id | hypertable_id | schema_name | table_name | relkind | slices +-----------------------+---------------------+----------+---------------+-----------------------+------------------+---------+------------------------------------------------ + _timescaledb_internal | _hyper_9_7_chunk | 8 | 9 | _timescaledb_internal | _hyper_9_7_chunk | r | {"time": [1514419200000000, 1515024000000000]} (1 row) DROP TABLE chunkapi; @@ -914,7 +602,7 @@ CREATE TABLE chunkapi (time timestamptz NOT NULL PRIMARY KEY, device int REFEREN SELECT * FROM create_hypertable('chunkapi', 'time'); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 11 | public | chunkapi | t + 10 | public | chunkapi | t (1 row) CREATE TABLE newchunk (time timestamptz NOT NULL, device int, temp float); @@ -939,9 +627,9 @@ ALTER TABLE newchunk ADD CONSTRAINT chunkapi_temp_check CHECK (temp > 0); CREATE TABLE newchunk2 as select * from newchunk; ALTER TABLE newchunk2 ADD CONSTRAINT chunkapi_temp_check CHECK (temp > 0); SELECT * FROM _timescaledb_functions.create_chunk('chunkapi', :'SLICES', :'CHUNK_SCHEMA', :'CHUNK_NAME', 'newchunk'); - chunk_id | hypertable_id | schema_name | table_name | relkind | slices | created -----------+---------------+-----------------------+--------------------+---------+------------------------------------------------+--------- - 13 | 11 | _timescaledb_internal | _hyper_10_10_chunk | r | {"time": [1514419200000000, 1515024000000000]} | t + chunk_id | hypertable_id | schema_name | table_name | relkind | slices | created +----------+---------------+-----------------------+------------------+---------+------------------------------------------------+--------- + 10 | 10 | _timescaledb_internal | _hyper_9_7_chunk | r | {"time": [1514419200000000, 1515024000000000]} | t (1 row) -- adding an existing table to an exiting range must fail @@ -955,9 +643,9 @@ SELECT :'CHUNK_NAME' AS expected_table_name, (_timescaledb_functions.show_chunk(ch)).* FROM show_chunks('chunkapi') ch; - expected_schema | expected_table_name | chunk_id | hypertable_id | schema_name | table_name | relkind | slices ------------------------+---------------------+----------+---------------+-----------------------+--------------------+---------+------------------------------------------------ - _timescaledb_internal | _hyper_10_10_chunk | 13 | 11 | _timescaledb_internal | _hyper_10_10_chunk | r | {"time": [1514419200000000, 1515024000000000]} + expected_schema | expected_table_name | chunk_id | hypertable_id | schema_name | table_name | relkind | slices +-----------------------+---------------------+----------+---------------+-----------------------+------------------+---------+------------------------------------------------ + _timescaledb_internal | _hyper_9_7_chunk | 10 | 10 | _timescaledb_internal | _hyper_9_7_chunk | r | {"time": [1514419200000000, 1515024000000000]} (1 row) -- The chunk should inherit the hypertable @@ -983,16 +671,10 @@ SELECT * FROM chunkapi ORDER BY 1,2,3; SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ---------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- - 13_7_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t - 13_8_chunkapi_pkey | p | {time} | _timescaledb_internal."13_8_chunkapi_pkey" | | f | f | t + 10_7_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 10_8_chunkapi_pkey | p | {time} | _timescaledb_internal."10_8_chunkapi_pkey" | | f | f | t chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t - constraint_18 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t + constraint_14 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t (4 rows) DROP TABLE chunkapi; -\c :TEST_DBNAME :ROLE_SUPERUSER -SET client_min_messages = ERROR; -DROP TABLESPACE tablespace1; -DROP TABLESPACE tablespace2; -SET client_min_messages = NOTICE; -\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER diff --git a/tsl/test/expected/chunk_utils_internal.out b/tsl/test/expected/chunk_utils_internal.out index cec4cac6465..616ddf54696 100644 --- a/tsl/test/expected/chunk_utils_internal.out +++ b/tsl/test/expected/chunk_utils_internal.out @@ -803,44 +803,6 @@ ORDER BY table_name; ------------+--------+----------- (0 rows) --- TEST error try freeze/unfreeze on dist hypertable --- Add distributed hypertables -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\c :TEST_DBNAME :ROLE_SUPERUSER -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2')) v(name) -) a; - node_name | database | node_created | database_created | extension_created ----------------------------+---------------------------+--------------+------------------+------------------- - db_chunk_utils_internal_1 | db_chunk_utils_internal_1 | t | t | t - db_chunk_utils_internal_2 | db_chunk_utils_internal_2 | t | t | t -(2 rows) - -CREATE TABLE disthyper (timec timestamp, device integer); -SELECT create_distributed_hypertable('disthyper', 'timec', 'device'); -WARNING: column type "timestamp without time zone" used for "timec" does not follow best practices -NOTICE: adding not-null constraint to column "timec" - create_distributed_hypertable -------------------------------- - (6,public,disthyper,t) -(1 row) - -INSERT into disthyper VALUES ('2020-01-01', 10); ---freeze one of the chunks -SELECT chunk_schema || '.' || chunk_name as "CHNAME3" -FROM timescaledb_information.chunks -WHERE hypertable_name = 'disthyper' -ORDER BY chunk_name LIMIT 1 -\gset -\set ON_ERROR_STOP 0 -SELECT _timescaledb_functions.freeze_chunk( :'CHNAME3'); -ERROR: operation not supported on distributed chunk or foreign table "_dist_hyper_6_12_chunk" -SELECT _timescaledb_functions.unfreeze_chunk( :'CHNAME3'); -ERROR: operation not supported on distributed chunk or foreign table "_dist_hyper_6_12_chunk" -\set ON_ERROR_STOP 1 -- TEST can create OSM chunk if there are constraints on the hypertable \c :TEST_DBNAME :ROLE_4 CREATE TABLE measure( id integer PRIMARY KEY, mname varchar(10)); @@ -853,7 +815,7 @@ CREATE TABLE hyper_constr ( id integer, time bigint, temp float, mid integer SELECT create_hypertable('hyper_constr', 'time', chunk_time_interval => 10); create_hypertable --------------------------- - (7,public,hyper_constr,t) + (6,public,hyper_constr,t) (1 row) INSERT INTO hyper_constr VALUES( 10, 200, 22, 1); @@ -886,7 +848,7 @@ WHERE hypertable_id IN (SELECT id from _timescaledb_catalog.hypertable ORDER BY table_name; table_name | status | osm_chunk --------------------+--------+----------- - _hyper_7_13_chunk | 0 | f + _hyper_6_12_chunk | 0 | f child_hyper_constr | 0 | t (2 rows) @@ -935,8 +897,8 @@ where hypertable_id = (Select id from _timescaledb_catalog.hypertable where tabl ORDER BY id; id | table_name ----+-------------------- - 13 | _hyper_7_13_chunk - 14 | child_hyper_constr + 12 | _hyper_6_12_chunk + 13 | child_hyper_constr (2 rows) ROLLBACK; @@ -968,7 +930,7 @@ CREATE TABLE test1.copy_test ( SELECT create_hypertable('test1.copy_test', 'time', chunk_time_interval => interval '1 day'); create_hypertable ----------------------- - (8,test1,copy_test,t) + (7,test1,copy_test,t) (1 row) COPY test1.copy_test FROM STDIN DELIMITER ','; @@ -989,13 +951,13 @@ SELECT table_name, status FROM _timescaledb_catalog.chunk WHERE table_name = :'COPY_CHUNK_NAME'; table_name | status -------------------+-------- - _hyper_8_15_chunk | 4 + _hyper_7_14_chunk | 4 (1 row) \set ON_ERROR_STOP 0 -- Copy should fail because one of che chunks is frozen COPY test1.copy_test FROM STDIN DELIMITER ','; -ERROR: cannot INSERT into frozen chunk "_hyper_8_15_chunk" +ERROR: cannot INSERT into frozen chunk "_hyper_7_14_chunk" \set ON_ERROR_STOP 1 -- Count existing rows SELECT COUNT(*) FROM test1.copy_test; @@ -1009,13 +971,13 @@ SELECT table_name, status FROM _timescaledb_catalog.chunk WHERE table_name = :'COPY_CHUNK_NAME'; table_name | status -------------------+-------- - _hyper_8_15_chunk | 4 + _hyper_7_14_chunk | 4 (1 row) \set ON_ERROR_STOP 0 -- Copy should fail because one of che chunks is frozen COPY test1.copy_test FROM STDIN DELIMITER ','; -ERROR: cannot INSERT into frozen chunk "_hyper_8_15_chunk" +ERROR: cannot INSERT into frozen chunk "_hyper_7_14_chunk" \set ON_ERROR_STOP 1 -- Count existing rows SELECT COUNT(*) FROM test1.copy_test; @@ -1036,7 +998,7 @@ SELECT table_name, status FROM _timescaledb_catalog.chunk WHERE table_name = :'COPY_CHUNK_NAME'; table_name | status -------------------+-------- - _hyper_8_15_chunk | 0 + _hyper_7_14_chunk | 0 (1 row) -- Copy should work now @@ -1131,12 +1093,12 @@ WHERE ht.table_name LIKE 'osm%' ORDER BY 2,3; table_name | id | dimension_id | range_start | range_end ------------+----+--------------+---------------------+--------------------- - osm_int2 | 17 | 9 | 9223372036854775806 | 9223372036854775807 - osm_int4 | 18 | 10 | 9223372036854775806 | 9223372036854775807 - osm_int8 | 19 | 11 | 9223372036854775806 | 9223372036854775807 - osm_date | 20 | 12 | 9223372036854775806 | 9223372036854775807 - osm_ts | 21 | 13 | 9223372036854775806 | 9223372036854775807 - osm_tstz | 22 | 14 | 9223372036854775806 | 9223372036854775807 + osm_int2 | 15 | 7 | 9223372036854775806 | 9223372036854775807 + osm_int4 | 16 | 8 | 9223372036854775806 | 9223372036854775807 + osm_int8 | 17 | 9 | 9223372036854775806 | 9223372036854775807 + osm_date | 18 | 10 | 9223372036854775806 | 9223372036854775807 + osm_ts | 19 | 11 | 9223372036854775806 | 9223372036854775807 + osm_tstz | 20 | 12 | 9223372036854775806 | 9223372036854775807 (6 rows) -- test that correct slice is found and updated for table with multiple chunk constraints @@ -1149,8 +1111,8 @@ _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc WHERE c.h AND c.id = cc.chunk_id; id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped | status | osm_chunk | chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name ----+---------------+-----------------------+--------------------+---------------------+---------+--------+-----------+----------+--------------------+-----------------------------+---------------------------- - 23 | 15 | _timescaledb_internal | _hyper_15_23_chunk | | f | 0 | f | 23 | | 23_3_test_multicon_time_key | test_multicon_time_key - 23 | 15 | _timescaledb_internal | _hyper_15_23_chunk | | f | 0 | f | 23 | 23 | constraint_23 | + 22 | 14 | _timescaledb_internal | _hyper_14_22_chunk | | f | 0 | f | 22 | | 22_3_test_multicon_time_key | test_multicon_time_key + 22 | 14 | _timescaledb_internal | _hyper_14_22_chunk | | f | 0 | f | 22 | 21 | constraint_21 | (2 rows) \c :TEST_DBNAME :ROLE_SUPERUSER ; @@ -1168,7 +1130,7 @@ FROM _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc, _ti WHERE c.hypertable_id = :htid AND cc.chunk_id = c.id AND ds.id = cc.dimension_slice_id; chunk_id | table_name | status | osm_chunk | dimension_slice_id | range_start | range_end ----------+--------------------+--------+-----------+--------------------+------------------+------------------ - 23 | _hyper_15_23_chunk | 0 | t | 23 | 1577955600000000 | 1578128400000000 + 22 | _hyper_14_22_chunk | 0 | t | 21 | 1577955600000000 | 1578128400000000 (1 row) -- check that range was reset to default - infinity @@ -1196,7 +1158,7 @@ FROM _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc, _ti WHERE c.hypertable_id = :htid AND cc.chunk_id = c.id AND ds.id = cc.dimension_slice_id ORDER BY cc.chunk_id; chunk_id | table_name | status | osm_chunk | dimension_slice_id | range_start | range_end ----------+--------------------+--------+-----------+--------------------+---------------------+--------------------- - 23 | _hyper_15_23_chunk | 0 | t | 23 | 9223372036854775806 | 9223372036854775807 + 22 | _hyper_14_22_chunk | 0 | t | 21 | 9223372036854775806 | 9223372036854775807 (1 row) -- test further with ordered append @@ -1220,9 +1182,9 @@ FROM _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc, _ti WHERE c.hypertable_id = :htid AND cc.chunk_id = c.id AND ds.id = cc.dimension_slice_id ORDER BY cc.chunk_id; chunk_id | table_name | status | osm_chunk | dimension_slice_id | range_start | range_end ----------+-------------------------+--------+-----------+--------------------+---------------------+--------------------- - 24 | _hyper_16_24_chunk | 0 | f | 24 | 1577836800000000 | 1577923200000000 - 25 | _hyper_16_25_chunk | 0 | f | 25 | 1577923200000000 | 1578009600000000 - 26 | test_chunkapp_fdw_child | 0 | t | 26 | 9223372036854775806 | 9223372036854775807 + 23 | _hyper_15_23_chunk | 0 | f | 22 | 1577836800000000 | 1577923200000000 + 24 | _hyper_15_24_chunk | 0 | f | 23 | 1577923200000000 | 1578009600000000 + 25 | test_chunkapp_fdw_child | 0 | t | 24 | 9223372036854775806 | 9223372036854775807 (3 rows) -- attempt to update overlapping range, should fail @@ -1243,9 +1205,9 @@ FROM _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc, _ti WHERE c.hypertable_id = :htid AND cc.chunk_id = c.id AND ds.id = cc.dimension_slice_id ORDER BY cc.chunk_id; chunk_id | table_name | status | osm_chunk | dimension_slice_id | range_start | range_end ----------+-------------------------+--------+-----------+--------------------+------------------+------------------ - 24 | _hyper_16_24_chunk | 0 | f | 24 | 1577836800000000 | 1577923200000000 - 25 | _hyper_16_25_chunk | 0 | f | 25 | 1577923200000000 | 1578009600000000 - 26 | test_chunkapp_fdw_child | 0 | t | 26 | 1578038400000000 | 1578124800000000 + 23 | _hyper_15_23_chunk | 0 | f | 22 | 1577836800000000 | 1577923200000000 + 24 | _hyper_15_24_chunk | 0 | f | 23 | 1577923200000000 | 1578009600000000 + 25 | test_chunkapp_fdw_child | 0 | t | 24 | 1578038400000000 | 1578124800000000 (3 rows) -- ordered append should be possible as ranges do not overlap @@ -1254,8 +1216,8 @@ EXPLAIN SELECT * FROM test_chunkapp ORDER BY 1; ---------------------------------------------------------------------------------------------------------------------------------------- Custom Scan (ChunkAppend) on test_chunkapp (cost=0.15..270.31 rows=6355 width=12) Order: test_chunkapp."time" - -> Index Scan Backward using _hyper_16_24_chunk_test_chunkapp_time_idx on _hyper_16_24_chunk (cost=0.15..42.75 rows=2040 width=12) - -> Index Scan Backward using _hyper_16_25_chunk_test_chunkapp_time_idx on _hyper_16_25_chunk (cost=0.15..42.75 rows=2040 width=12) + -> Index Scan Backward using _hyper_15_23_chunk_test_chunkapp_time_idx on _hyper_15_23_chunk (cost=0.15..42.75 rows=2040 width=12) + -> Index Scan Backward using _hyper_15_24_chunk_test_chunkapp_time_idx on _hyper_15_24_chunk (cost=0.15..42.75 rows=2040 width=12) -> Foreign Scan on test_chunkapp_fdw_child (cost=100.00..184.80 rows=2275 width=12) (5 rows) @@ -1296,9 +1258,9 @@ EXPLAIN SELECT * FROM test_chunkapp ORDER BY 1; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Merge Append (cost=100.33..352.47 rows=6355 width=12) - Sort Key: _hyper_16_24_chunk."time" - -> Index Scan Backward using _hyper_16_24_chunk_test_chunkapp_time_idx on _hyper_16_24_chunk (cost=0.15..42.75 rows=2040 width=12) - -> Index Scan Backward using _hyper_16_25_chunk_test_chunkapp_time_idx on _hyper_16_25_chunk (cost=0.15..42.75 rows=2040 width=12) + Sort Key: _hyper_15_23_chunk."time" + -> Index Scan Backward using _hyper_15_23_chunk_test_chunkapp_time_idx on _hyper_15_23_chunk (cost=0.15..42.75 rows=2040 width=12) + -> Index Scan Backward using _hyper_15_24_chunk_test_chunkapp_time_idx on _hyper_15_24_chunk (cost=0.15..42.75 rows=2040 width=12) -> Foreign Scan on test_chunkapp_fdw_child (cost=100.00..184.80 rows=2275 width=12) (5 rows) @@ -1315,9 +1277,9 @@ FROM _timescaledb_catalog.chunk c, _timescaledb_catalog.chunk_constraint cc, _ti WHERE c.hypertable_id = :htid AND cc.chunk_id = c.id AND ds.id = cc.dimension_slice_id ORDER BY cc.chunk_id; chunk_id | table_name | status | osm_chunk | dimension_slice_id | range_start | range_end ----------+-------------------------+--------+-----------+--------------------+---------------------+--------------------- - 24 | _hyper_16_24_chunk | 0 | f | 24 | 1577836800000000 | 1577923200000000 - 25 | _hyper_16_25_chunk | 0 | f | 25 | 1577923200000000 | 1578009600000000 - 26 | test_chunkapp_fdw_child | 0 | t | 26 | 9223372036854775806 | 9223372036854775807 + 23 | _hyper_15_23_chunk | 0 | f | 22 | 1577836800000000 | 1577923200000000 + 24 | _hyper_15_24_chunk | 0 | f | 23 | 1577923200000000 | 1578009600000000 + 25 | test_chunkapp_fdw_child | 0 | t | 24 | 9223372036854775806 | 9223372036854775807 (3 rows) -- now set empty to true, should ordered append @@ -1335,8 +1297,8 @@ EXPLAIN SELECT * FROM test_chunkapp ORDER BY 1; ---------------------------------------------------------------------------------------------------------------------------------------- Custom Scan (ChunkAppend) on test_chunkapp (cost=0.15..270.31 rows=6355 width=12) Order: test_chunkapp."time" - -> Index Scan Backward using _hyper_16_24_chunk_test_chunkapp_time_idx on _hyper_16_24_chunk (cost=0.15..42.75 rows=2040 width=12) - -> Index Scan Backward using _hyper_16_25_chunk_test_chunkapp_time_idx on _hyper_16_25_chunk (cost=0.15..42.75 rows=2040 width=12) + -> Index Scan Backward using _hyper_15_23_chunk_test_chunkapp_time_idx on _hyper_15_23_chunk (cost=0.15..42.75 rows=2040 width=12) + -> Index Scan Backward using _hyper_15_24_chunk_test_chunkapp_time_idx on _hyper_15_24_chunk (cost=0.15..42.75 rows=2040 width=12) -> Foreign Scan on test_chunkapp_fdw_child (cost=100.00..184.80 rows=2275 width=12) (5 rows) @@ -1352,7 +1314,7 @@ CREATE TABLE test2(time timestamptz not null, a int); SELECT create_hypertable('test2', 'time'); create_hypertable --------------------- - (17,public,test2,t) + (16,public,test2,t) (1 row) INSERT INTO test2 VALUES ('2020-01-01'::timestamptz, 1); @@ -1360,7 +1322,7 @@ ALTER TABLE test2 SET (timescaledb.compress); SELECT compress_chunk(show_chunks('test2')); compress_chunk ------------------------------------------ - _timescaledb_internal._hyper_17_27_chunk + _timescaledb_internal._hyper_16_26_chunk (1 row) -- find internal compression table, call API function on it @@ -1369,7 +1331,7 @@ FROM _timescaledb_catalog.hypertable ht, _timescaledb_catalog.hypertable cht WHERE ht.table_name = 'test2' and cht.id = ht.compressed_hypertable_id \gset \set ON_ERROR_STOP 0 SELECT _timescaledb_functions.hypertable_osm_range_update(:'COMPRESSION_TBLNM'::regclass, '2020-01-01'::timestamptz); -ERROR: could not find time dimension for hypertable _timescaledb_internal._compressed_hypertable_18 +ERROR: could not find time dimension for hypertable _timescaledb_internal._compressed_hypertable_17 \set ON_ERROR_STOP 1 -- test wrong/incompatible data types with hypertable time dimension -- update range of int2 with int4 @@ -1422,5 +1384,3 @@ ERROR: Cannot insert into tiered chunk range of public.osm_slice_update - attem -- clean up databases created \c :TEST_DBNAME :ROLE_SUPERUSER DROP DATABASE postgres_fdw_db WITH (FORCE); -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); diff --git a/tsl/test/expected/dist_param.out b/tsl/test/expected/dist_param.out index 564a15705a4..7809fd80f6f 100644 --- a/tsl/test/expected/dist_param.out +++ b/tsl/test/expected/dist_param.out @@ -511,14 +511,14 @@ limit 1 Output: metric_dist.id, metric_dist.ts, metric_dist.value -> Unique Output: metric_dist.id, metric_dist.ts, metric_dist.value - -> Incremental Sort + -> Sort Output: metric_dist.id, metric_dist.ts, metric_dist.value Sort Key: metric_dist.id, metric_dist.ts, metric_dist.value - Presorted Key: metric_dist.id -> Nested Loop Output: metric_dist.id, metric_dist.ts, metric_dist.value - -> Index Scan using metric_name_id on public.metric_name + -> Index Scan using metric_name_name on public.metric_name Output: metric_name.id, metric_name.name + Index Cond: ((metric_name.name >= 'cpu'::text) AND (metric_name.name < 'cpv'::text)) Filter: (metric_name.name ~~ 'cpu%'::text) -> Custom Scan (DataNodeScan) on public.metric_dist Output: metric_dist.id, metric_dist.ts, metric_dist.value @@ -556,10 +556,9 @@ limit 1 Output: metric_name.name, metric_dist.ts, metric_dist.value -> Unique Output: metric_name.name, metric_dist.ts, metric_dist.value - -> Incremental Sort + -> Sort Output: metric_name.name, metric_dist.ts, metric_dist.value Sort Key: metric_name.name COLLATE "C", metric_dist.ts, metric_dist.value - Presorted Key: metric_name.name -> Nested Loop Output: metric_name.name, metric_dist.ts, metric_dist.value -> Index Scan using metric_name_name on public.metric_name @@ -571,7 +570,7 @@ limit 1 Data node: data_node_1 Chunks: _dist_hyper_1_3_chunk, _dist_hyper_1_16_chunk, _dist_hyper_1_20_chunk, _dist_hyper_1_37_chunk, _dist_hyper_1_52_chunk Remote SQL: SELECT ts, id, value FROM public.metric_dist WHERE _timescaledb_functions.chunks_in(public.metric_dist.*, ARRAY[3, 16, 20, 37, 52]) AND ((ts >= '2022-02-01 15:02:02-08'::timestamp with time zone)) AND ((ts <= '2022-03-02 15:02:02-08'::timestamp with time zone)) AND (($1::integer = id)) -(19 rows) +(18 rows) -- If there are a lot of rows chosen from the local table, the parameterized -- nested loop might download the entire dist table or even more than that (in @@ -674,7 +673,7 @@ group by id -> Nested Loop Output: metric_location.id, metric_name.id Inner Unique: true - Join Filter: (metric_name.id = metric_location.id) + Join Filter: (metric_location.id = metric_name.id) -> Seq Scan on public.metric_location Output: metric_location.id, metric_location.location Filter: texteq(metric_location.location, 'Yerevan'::text) @@ -709,7 +708,7 @@ order by 1 Output: metric_dist.id, metric_dist.value -> Nested Loop Output: max_value_times.ts, max_value_times.id, metric_name.id - Join Filter: (metric_name.id = max_value_times.id) + Join Filter: (max_value_times.id = metric_name.id) -> Index Scan using metric_name_id on public.metric_name Output: metric_name.id, metric_name.name Filter: (metric_name.name ~~ 'cpu%'::text) diff --git a/tsl/test/expected/exp_cagg_monthly.out b/tsl/test/expected/exp_cagg_monthly.out index 9ac5062e4d3..2ca5eaa9e9d 100644 --- a/tsl/test/expected/exp_cagg_monthly.out +++ b/tsl/test/expected/exp_cagg_monthly.out @@ -1027,179 +1027,6 @@ SELECT * FROM conditions_large_1y ORDER BY bucket; (11 rows) RESET timescaledb.materializations_per_refresh_window; --- Test caggs with monthly buckets on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created ------------------------+-----------------------+--------------+------------------+------------------- - db_exp_cagg_monthly_1 | db_exp_cagg_monthly_1 | t | t | t - db_exp_cagg_monthly_2 | db_exp_cagg_monthly_2 | t | t | t - db_exp_cagg_monthly_3 | db_exp_cagg_monthly_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE conditions_dist( - day DATE NOT NULL, - temperature INT NOT NULL); -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - table_name ------------------ - conditions_dist -(1 row) - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01' :: date, '2010-03-01' :: date - interval '1 day', '1 day') as ts; -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day) AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; -NOTICE: refreshing continuous aggregate "conditions_dist_1m" -SELECT mat_hypertable_id AS cagg_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset -SELECT raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - bucket_width --------------- - -1 -(1 row) - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - experimental | name | bucket_width | origin | timezone ---------------+----------------+--------------+--------+---------- - t | time_bucket_ng | @ 1 mon | | -(1 row) - -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - --- Same test but with non-realtime, NO DATA aggregate and manual refresh -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day) AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max ---------+-----+----- -(0 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-03-01'); -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - --- Check invalidation for caggs on top of distributed hypertable -INSERT INTO conditions_dist(day, temperature) -VALUES ('2010-01-15', 999), ('2010-02-15', -999), ('2010-03-01', 15); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); -NOTICE: defaulting compress_orderby to bucket -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; - compress_chunk -------------------------------------------- - _timescaledb_internal._hyper_17_533_chunk -(1 row) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - --- Clean up -DROP TABLE conditions_dist CASCADE; -NOTICE: drop cascades to 5 other objects -NOTICE: drop cascades to 3 other objects -NOTICE: drop cascades to 3 other objects -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); -- Test the specific code path of creating a CAGG on top of empty hypertable. CREATE TABLE conditions_empty( day DATE NOT NULL, @@ -1211,7 +1038,7 @@ SELECT create_hypertable( ); create_hypertable -------------------------------- - (19,public,conditions_empty,t) + (15,public,conditions_empty,t) (1 row) CREATE MATERIALIZED VIEW conditions_summary_empty @@ -1268,7 +1095,7 @@ SELECT create_hypertable( ); create_hypertable --------------------------------- - (21,public,conditions_policy,t) + (17,public,conditions_policy,t) (1 row) INSERT INTO conditions_policy (day, city, temperature) VALUES diff --git a/tsl/test/expected/exp_cagg_origin.out b/tsl/test/expected/exp_cagg_origin.out index 6b0c70c3add..c7f2ab6b9be 100644 --- a/tsl/test/expected/exp_cagg_origin.out +++ b/tsl/test/expected/exp_cagg_origin.out @@ -466,183 +466,6 @@ NOTICE: drop cascades to 7 other objects NOTICE: drop cascades to 3 other objects NOTICE: drop cascades to 3 other objects NOTICE: drop cascades to 5 other objects --- Test caggs with monthly buckets and custom origin on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created -----------------------+----------------------+--------------+------------------+------------------- - db_exp_cagg_origin_1 | db_exp_cagg_origin_1 | t | t | t - db_exp_cagg_origin_2 | db_exp_cagg_origin_2 | t | t | t - db_exp_cagg_origin_3 | db_exp_cagg_origin_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE conditions_dist( - day date NOT NULL, - temperature INT NOT NULL); -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - table_name ------------------ - conditions_dist -(1 row) - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01' :: date, '2010-03-01' :: date - interval '1 day', '1 day') as ts; -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, '2010-01-01') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; -NOTICE: refreshing continuous aggregate "conditions_dist_1m" -SELECT mat_hypertable_id AS cagg_id, raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - bucket_width --------------- - -1 -(1 row) - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - experimental | name | bucket_width | origin | timezone ---------------+----------------+--------------+--------------------------+---------- - t | time_bucket_ng | @ 1 mon | Fri Jan 01 00:00:00 2010 | -(1 row) - -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - --- Same test but with non-realtime, NO DATA aggregate and manual refresh -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, '2005-01-01') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max ---------+-----+----- -(0 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-03-01'); -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - --- Check invalidation for caggs on top of distributed hypertable -INSERT INTO conditions_dist(day, temperature) -VALUES ('2010-01-15', 999), ('2010-02-15', -999), ('2010-03-01', 15); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+-----+----- - 01-01-2010 | 101 | 131 - 02-01-2010 | 201 | 228 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - --- Compression on top of distributed hypertables -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); -NOTICE: defaulting compress_orderby to bucket -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; - compress_chunk ------------------------------------------- - _timescaledb_internal._hyper_8_205_chunk -(1 row) - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - bucket | min | max -------------+------+----- - 01-01-2010 | 101 | 999 - 02-01-2010 | -999 | 228 - 03-01-2010 | 15 | 15 -(3 rows) - --- Clean up -DROP TABLE conditions_dist CASCADE; -NOTICE: drop cascades to 5 other objects -NOTICE: drop cascades to 3 other objects -NOTICE: drop cascades to 3 other objects -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -SELECT delete_data_node(name) -FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v (name); - delete_data_node ------------------- - t - t - t -(3 rows) - -SET ROLE :ROLE_DEFAULT_PERM_USER; -- Test the specific code path of creating a CAGG on top of empty hypertable. CREATE TABLE conditions_empty( day DATE NOT NULL, @@ -652,9 +475,9 @@ SELECT create_hypertable( 'conditions_empty', 'day', chunk_time_interval => INTERVAL '1 day' ); - create_hypertable --------------------------------- - (10,public,conditions_empty,t) + create_hypertable +------------------------------- + (6,public,conditions_empty,t) (1 row) CREATE MATERIALIZED VIEW conditions_summary_empty @@ -703,7 +526,7 @@ ORDER BY month, city; -- Clean up DROP TABLE conditions_empty CASCADE; NOTICE: drop cascades to 2 other objects -NOTICE: drop cascades to table _timescaledb_internal._hyper_11_225_chunk +NOTICE: drop cascades to table _timescaledb_internal._hyper_7_158_chunk -- Make sure add_continuous_aggregate_policy() works CREATE TABLE conditions_policy( day DATE NOT NULL, @@ -713,9 +536,9 @@ SELECT create_hypertable( 'conditions_policy', 'day', chunk_time_interval => INTERVAL '1 day' ); - create_hypertable ---------------------------------- - (12,public,conditions_policy,t) + create_hypertable +-------------------------------- + (8,public,conditions_policy,t) (1 row) INSERT INTO conditions_policy (day, city, temperature) VALUES @@ -770,7 +593,7 @@ SELECT add_continuous_aggregate_policy('conditions_summary_policy', -- Clean up DROP TABLE conditions_policy CASCADE; NOTICE: drop cascades to 2 other objects -NOTICE: drop cascades to table _timescaledb_internal._hyper_13_240_chunk +NOTICE: drop cascades to table _timescaledb_internal._hyper_9_173_chunk -- Make sure CAGGs with custom origin work for timestamp type CREATE TABLE conditions_timestamp( tstamp TIMESTAMP NOT NULL, @@ -783,7 +606,7 @@ SELECT create_hypertable( WARNING: column type "timestamp without time zone" used for "tstamp" does not follow best practices create_hypertable ------------------------------------ - (14,public,conditions_timestamp,t) + (10,public,conditions_timestamp,t) (1 row) CREATE MATERIALIZED VIEW conditions_summary_timestamp @@ -845,8 +668,8 @@ ALTER TABLE conditions_timestamp SET ( SELECT compress_chunk(ch) FROM show_chunks('conditions_timestamp') AS ch; compress_chunk ------------------------------------------- - _timescaledb_internal._hyper_14_241_chunk - _timescaledb_internal._hyper_14_243_chunk + _timescaledb_internal._hyper_10_174_chunk + _timescaledb_internal._hyper_10_176_chunk (2 rows) -- New data is seen because the cagg is real-time @@ -896,7 +719,7 @@ SELECT add_continuous_aggregate_policy('conditions_summary_timestamp', DROP TABLE conditions_timestamp CASCADE; NOTICE: drop cascades to 2 other objects NOTICE: drop cascades to 3 other objects -NOTICE: drop cascades to table _timescaledb_internal._hyper_15_242_chunk +NOTICE: drop cascades to table _timescaledb_internal._hyper_11_175_chunk -- Make sure CAGGs with custom origin work for timestamptz type CREATE TABLE conditions_timestamptz( tstamp TIMESTAMPTZ NOT NULL, @@ -908,7 +731,7 @@ SELECT create_hypertable( ); create_hypertable -------------------------------------- - (17,public,conditions_timestamptz,t) + (13,public,conditions_timestamptz,t) (1 row) -- Add some data to the hypertable and make sure it is visible in the cagg @@ -1059,9 +882,9 @@ ALTER TABLE conditions_timestamptz SET ( SELECT compress_chunk(ch) FROM show_chunks('conditions_timestamptz') AS ch; compress_chunk ------------------------------------------- - _timescaledb_internal._hyper_17_246_chunk - _timescaledb_internal._hyper_17_247_chunk - _timescaledb_internal._hyper_17_249_chunk + _timescaledb_internal._hyper_13_179_chunk + _timescaledb_internal._hyper_13_180_chunk + _timescaledb_internal._hyper_13_182_chunk (3 rows) -- New data is seen because the cagg is real-time @@ -1111,8 +934,4 @@ SELECT add_continuous_aggregate_policy('conditions_summary_timestamptz', DROP TABLE conditions_timestamptz CASCADE; NOTICE: drop cascades to 3 other objects NOTICE: drop cascades to 3 other objects -NOTICE: drop cascades to table _timescaledb_internal._hyper_19_248_chunk -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); +NOTICE: drop cascades to table _timescaledb_internal._hyper_15_181_chunk diff --git a/tsl/test/expected/exp_cagg_timezone.out b/tsl/test/expected/exp_cagg_timezone.out index a5c207130a8..b139f89d255 100644 --- a/tsl/test/expected/exp_cagg_timezone.out +++ b/tsl/test/expected/exp_cagg_timezone.out @@ -650,202 +650,6 @@ ORDER by month, city; Moscow | 2021-10-02 00:00:00 | 2 | 4 (7 rows) --- Test caggs with monthly buckets on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - node_name | database | node_created | database_created | extension_created -------------------------+------------------------+--------------+------------------+------------------- - db_exp_cagg_timezone_1 | db_exp_cagg_timezone_1 | t | t | t - db_exp_cagg_timezone_2 | db_exp_cagg_timezone_2 | t | t | t - db_exp_cagg_timezone_3 | db_exp_cagg_timezone_3 | t | t | t -(3 rows) - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; -CREATE TABLE conditions_dist( - day timestamptz NOT NULL, - temperature INT NOT NULL); -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - table_name ------------------ - conditions_dist -(1 row) - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01 00:00:00 MSK' :: timestamptz, '2010-03-01 00:00:00 MSK' :: timestamptz - interval '1 day', '1 day') as ts; -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, 'MSK') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; -NOTICE: refreshing continuous aggregate "conditions_dist_1m" -SELECT mat_hypertable_id AS cagg_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset -SELECT raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - bucket_width --------------- - -1 -(1 row) - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - experimental | name | bucket_width | origin | timezone ---------------+----------------+--------------+--------+---------- - t | time_bucket_ng | @ 1 mon | | MSK -(1 row) - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - month | min | max ----------------------+-----+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | 131 | 227 -(2 rows) - --- Same test but with non-realtime, NO DATA aggregate and manual refresh -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, 'MSK') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max --------+-----+----- -(0 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01 00:00:00 MSK', '2010-03-01 00:00:00 MSK'); -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max ----------------------+-----+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | 131 | 227 -(2 rows) - --- Check invalidation for caggs on top of distributed hypertable -INSERT INTO conditions_dist(day, temperature) VALUES -('2010-01-15 00:00:00 MSK', 999), -('2010-02-15 00:00:00 MSK', -999), -('2010-03-01 00:00:00 MSK', 15); -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - month | min | max ----------------------+-----+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | 131 | 227 - 2010-03-01 00:00:00 | 15 | 15 -(3 rows) - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max ----------------------+-----+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | 131 | 227 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01 00:00:00 MSK', '2010-04-01 00:00:00 MSK'); -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - month | min | max ----------------------+------+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | -999 | 227 - 2010-03-01 00:00:00 | 15 | 15 -(3 rows) - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max ----------------------+-----+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | 131 | 227 -(2 rows) - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01 00:00:00 MSK', '2010-04-01 00:00:00 MSK'); -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - month | min | max ----------------------+------+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | -999 | 227 - 2010-03-01 00:00:00 | 15 | 15 -(3 rows) - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max ----------------------+------+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | -999 | 227 - 2010-03-01 00:00:00 | 15 | 15 -(3 rows) - --- Check compatibility with compressed distributed hypertables -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); -NOTICE: defaulting compress_orderby to bucket -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; - compress_chunk -------------------------------------------- - _timescaledb_internal._hyper_12_201_chunk -(1 row) - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - month | min | max ----------------------+------+------ - 2010-01-01 00:00:00 | 101 | 1231 - 2010-02-01 00:00:00 | -999 | 227 - 2010-03-01 00:00:00 | 15 | 15 -(3 rows) - --- Clean up -DROP TABLE conditions_dist CASCADE; -NOTICE: drop cascades to 5 other objects -NOTICE: drop cascades to 3 other objects -NOTICE: drop cascades to 3 other objects -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); -- Make sure add_continuous_aggregate_policy() works CREATE TABLE conditions_policy( day TIMESTAMPTZ NOT NULL, @@ -857,7 +661,7 @@ SELECT create_hypertable( ); create_hypertable --------------------------------- - (14,public,conditions_policy,t) + (10,public,conditions_policy,t) (1 row) INSERT INTO conditions_policy (day, city, temperature) VALUES diff --git a/tsl/test/expected/read_only.out b/tsl/test/expected/read_only.out index ced4a428446..aa74d1540dd 100644 --- a/tsl/test/expected/read_only.out +++ b/tsl/test/expected/read_only.out @@ -5,8 +5,6 @@ -- Following tests checks that API functions which modify data (including catalog) -- properly recognize read-only transaction state -- -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -- create_hypertable() -- CREATE TABLE test_table(time bigint NOT NULL, device int); @@ -96,112 +94,13 @@ ERROR: cannot execute DROP TABLE in a read-only transaction \set ON_ERROR_STOP 1 SET default_transaction_read_only TO off; DROP TABLE test_table; --- data nodes --- -CREATE TABLE disttable(time timestamptz NOT NULL, device int); --- add_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM add_data_node(:'DATA_NODE_1', host => 'localhost', database => :'DATA_NODE_1'); -ERROR: cannot execute add_data_node() in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node(:'DATA_NODE_1', host => 'localhost', database => :'DATA_NODE_1'); - node_name | database | node_created | database_created | extension_created -----------------+----------------+--------------+------------------+------------------- - db_read_only_1 | db_read_only_1 | t | t | t -(1 row) - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node(:'DATA_NODE_2', host => 'localhost', database => :'DATA_NODE_2'); - node_name | database | node_created | database_created | extension_created -----------------+----------------+--------------+------------------+------------------- - db_read_only_2 | db_read_only_2 | t | t | t -(1 row) - --- create_distributed_hypertable() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device', data_nodes => ARRAY[:'DATA_NODE_1']); -ERROR: cannot execute create_distributed_hypertable() in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device', data_nodes => ARRAY[:'DATA_NODE_1']); -WARNING: only one data node was assigned to the hypertable - hypertable_id | schema_name | table_name | created ----------------+-------------+------------+--------- - 2 | public | disttable | t -(1 row) - --- attach_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM attach_data_node(:'DATA_NODE_2', 'disttable'); -ERROR: cannot execute attach_data_node() in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM attach_data_node(:'DATA_NODE_2', 'disttable'); -NOTICE: the number of partitions in dimension "device" was increased to 2 - hypertable_id | node_hypertable_id | node_name ----------------+--------------------+---------------- - 2 | 1 | db_read_only_2 -(1 row) - --- detach_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM detach_data_node(:'DATA_NODE_2', 'disttable'); -ERROR: cannot execute detach_data_node() in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM detach_data_node(:'DATA_NODE_2', 'disttable'); -NOTICE: the number of partitions in dimension "device" of hypertable "disttable" was decreased to 1 - detach_data_node ------------------- - 1 -(1 row) - --- delete_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM delete_data_node(:'DATA_NODE_2'); -ERROR: cannot execute delete_data_node() in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM delete_data_node(:'DATA_NODE_2'); - delete_data_node ------------------- - t -(1 row) - --- set_replication_factor() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM set_replication_factor('disttable', 2); -ERROR: cannot execute set_replication_factor() in a read-only transaction -\set ON_ERROR_STOP 1 --- drop distributed hypertable --- -\set ON_ERROR_STOP 0 -DROP TABLE disttable; -ERROR: cannot execute DROP TABLE in a read-only transaction -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -DROP TABLE disttable; -- Test some read-only cases of DDL operations -- CREATE TABLE test_table(time bigint NOT NULL, device int); SELECT * FROM create_hypertable('test_table', 'time', chunk_time_interval => 1000000::bigint); hypertable_id | schema_name | table_name | created ---------------+-------------+------------+--------- - 3 | public | test_table | t + 2 | public | test_table | t (1 row) INSERT INTO test_table VALUES (0, 1), (1, 1), (2, 2); @@ -262,7 +161,7 @@ CREATE TABLE test_contagg ( SELECT create_hypertable('test_contagg', 'observation_time'); create_hypertable --------------------------- - (4,public,test_contagg,t) + (3,public,test_contagg,t) (1 row) SET default_transaction_read_only TO on; @@ -289,7 +188,7 @@ CREATE TABLE test_table_int(time bigint NOT NULL, device int); SELECt create_hypertable('test_table_int', 'time', chunk_time_interval=>'1'::bigint); create_hypertable ----------------------------- - (5,public,test_table_int,t) + (4,public,test_table_int,t) (1 row) create or replace function dummy_now() returns BIGINT LANGUAGE SQL IMMUTABLE as 'SELECT 5::BIGINT'; @@ -306,7 +205,7 @@ SELECT config as comp_job_config FROM _timescaledb_config.bgw_job WHERE id = :comp_job_id \gset SET default_transaction_read_only TO on; CALL _timescaledb_functions.policy_compression(:comp_job_id, :'comp_job_config'); -WARNING: compressing chunk "_timescaledb_internal._hyper_5_2_chunk" failed when compression policy is executed +WARNING: compressing chunk "_timescaledb_internal._hyper_4_2_chunk" failed when compression policy is executed SET default_transaction_read_only TO off; --verify chunks are not compressed SELECT count(*) , count(*) FILTER ( WHERE is_compressed is true) @@ -344,6 +243,3 @@ SELECT alter_job(1,scheduled:=false); ERROR: cannot execute alter_job() in a read-only transaction SELECT delete_job(1); ERROR: cannot execute delete_job() in a read-only transaction -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); diff --git a/tsl/test/expected/telemetry_stats-14.out b/tsl/test/expected/telemetry_stats-14.out deleted file mode 100644 index 095d92d7cd7..00000000000 --- a/tsl/test/expected/telemetry_stats-14.out +++ /dev/null @@ -1,1197 +0,0 @@ --- 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. ---telemetry tests that require a community license -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; --- function call info size is too variable for this test, so disable it -SET timescaledb.telemetry_level='no_functions'; -SELECT setseed(1); - setseed ---------- - -(1 row) - --- Create a materialized view from the telemetry report so that we --- don't regenerate telemetry for every query. Filter heap_size for --- materialized views since PG14 reports a different heap size for --- them compared to earlier PG versions. -CREATE MATERIALIZED VIEW telemetry_report AS -SELECT (r #- '{relations,materialized_views,heap_size}') AS r -FROM get_telemetry_report() r; -CREATE VIEW relations AS -SELECT r -> 'relations' AS rels -FROM telemetry_report; -SELECT rels -> 'continuous_aggregates' -> 'num_relations' AS num_continuous_aggs, - rels -> 'hypertables' -> 'num_relations' AS num_hypertables -FROM relations; - num_continuous_aggs | num_hypertables ----------------------+----------------- - 0 | 0 -(1 row) - --- check telemetry picks up flagged content from metadata -SELECT r -> 'db_metadata' AS db_metadata -FROM telemetry_report; - db_metadata -------------- - {} -(1 row) - --- check timescaledb_telemetry.cloud -SELECT r -> 'instance_metadata' AS instance_metadata -FROM telemetry_report r; - instance_metadata -------------------- - {"cloud": "ci"} -(1 row) - -CREATE TABLE normal (time timestamptz NOT NULL, device int, temp float); -CREATE TABLE part (time timestamptz NOT NULL, device int, temp float) PARTITION BY RANGE (time); -CREATE TABLE part_t1 PARTITION OF part FOR VALUES FROM ('2018-01-01') TO ('2018-02-01') PARTITION BY HASH (device); -CREATE TABLE part_t2 PARTITION OF part FOR VALUES FROM ('2018-02-01') TO ('2018-03-01') PARTITION BY HASH (device); -CREATE TABLE part_t1_d1 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t1_d2 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE part_t2_d1 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t2_d2 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE hyper (LIKE normal); -SELECT table_name FROM create_hypertable('hyper', 'time'); - table_name ------------- - hyper -(1 row) - -CREATE MATERIALIZED VIEW contagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg" is already up-to-date -CREATE MATERIALIZED VIEW contagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg_old" is already up-to-date --- Create another view (already have the "relations" view) -CREATE VIEW devices AS -SELECT DISTINCT ON (device) device -FROM hyper; --- Show relations with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 0, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 0 + - }, + - "hypertables": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 8192, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 0, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "continuous_aggregates": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Insert data -INSERT INTO normal -SELECT t, ceil(random() * 10)::int, random() * 30 -FROM generate_series('2018-01-01'::timestamptz, '2018-02-28', '2h') t; -INSERT INTO hyper -SELECT * FROM normal; -INSERT INTO part -SELECT * FROM normal; -CALL refresh_continuous_aggregate('contagg', NULL, NULL); -CALL refresh_continuous_aggregate('contagg_old', NULL, NULL); --- ANALYZE to get updated reltuples stats -ANALYZE normal, hyper, part; -SELECT count(c) FROM show_chunks('hyper') c; - count -------- - 9 -(1 row) - -SELECT count(c) FROM show_chunks('contagg') c; - count -------- - 2 -(1 row) - -SELECT count(c) FROM show_chunks('contagg_old') c; - count -------- - 2 -(1 row) - --- Update and show the telemetry report -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 155648, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 188416, + - "toast_size": 16384, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 229376, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Actual row count should be the same as reltuples stats for all tables -SELECT (SELECT count(*) FROM normal) num_inserted_rows, - (SELECT rels -> 'tables' -> 'num_reltuples' FROM relations) normal_reltuples, - (SELECT rels -> 'hypertables' -> 'num_reltuples' FROM relations) hyper_reltuples, - (SELECT rels -> 'partitioned_tables' -> 'num_reltuples' FROM relations) part_reltuples; - num_inserted_rows | normal_reltuples | hyper_reltuples | part_reltuples --------------------+------------------+-----------------+---------------- - 697 | 697 | 697 | 697 -(1 row) - --- Add compression -ALTER TABLE hyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('hyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------- - _timescaledb_internal._hyper_1_1_chunk - _timescaledb_internal._hyper_1_2_chunk - _timescaledb_internal._hyper_1_3_chunk - _timescaledb_internal._hyper_1_4_chunk -(4 rows) - -ALTER MATERIALIZED VIEW contagg SET (timescaledb.compress); -NOTICE: defaulting compress_segmentby to device -NOTICE: defaulting compress_orderby to hour -SELECT compress_chunk(c) -FROM show_chunks('contagg') c ORDER BY c LIMIT 1; - compress_chunk ------------------------------------------ - _timescaledb_internal._hyper_2_10_chunk -(1 row) - --- Turn of real-time aggregation -ALTER MATERIALIZED VIEW contagg SET (timescaledb.materialized_only = true); -ANALYZE normal, hyper, part; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 32768, + - "compression": { + - "compressed_heap_size": 32768, + - "compressed_row_count": 4, + - "compressed_toast_size": 32768, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 32768, + - "uncompressed_row_count": 284, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 65536, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 122880, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 413 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 180224, + - "toast_size": 24576, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 180224, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 1 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Add distributed hypertables -\set DN_DBNAME_1 :TEST_DBNAME _1 -\set DN_DBNAME_2 :TEST_DBNAME _2 --- Not an access node or data node -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - | "none" -(1 row) - --- Become an access node by adding a data node -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_1', host => 'localhost', database => :'DN_DBNAME_1'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_1 | db_telemetry_stats_1 | t | t | t -(1 row) - --- Telemetry should show one data node and "acces node" status -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - 1 | "access node" -(1 row) - --- See telemetry report from a data node -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT test.remote_exec(NULL, $$ - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -num_data_nodes|distributed_member ---------------+------------------ - |"data node" -(1 row) - - - remote_exec -------------- - -(1 row) - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_2', host => 'localhost', database => :'DN_DBNAME_2'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_2 | db_telemetry_stats_2 | t | t | t -(1 row) - -CREATE TABLE disthyper (LIKE normal); -SELECT create_distributed_hypertable('disthyper', 'time', 'device'); - create_distributed_hypertable -------------------------------- - (6,public,disthyper,t) -(1 row) - --- Show distributed hypertables stats with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- No datanode-related stats on the access node -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn -FROM relations; - distributed_hypertables_dn ------------------------------------------ - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0,+ - "num_compressed_hypertables": 0+ - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - } -(1 row) - --- Insert data into the distributed hypertable -INSERT INTO disthyper -SELECT * FROM normal; --- Update telemetry stats and show output on access node and data --- nodes. Note that the access node doesn't store data so shows --- zero. It should have stats from ANALYZE, though, like --- num_reltuples. -ANALYZE disthyper; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 697, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 357 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 340 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Add compression -ALTER TABLE disthyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('disthyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------------- - _timescaledb_internal._dist_hyper_6_19_chunk - _timescaledb_internal._dist_hyper_6_20_chunk - _timescaledb_internal._dist_hyper_6_21_chunk - _timescaledb_internal._dist_hyper_6_22_chunk -(4 rows) - -ANALYZE disthyper; --- Update telemetry stats and show updated compression stats -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 581, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 72, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 285 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 44, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 296 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Create a replicated distributed hypertable and show replication stats -CREATE TABLE disthyper_repl (LIKE normal); -SELECT create_distributed_hypertable('disthyper_repl', 'time', 'device', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (7,public,disthyper_repl,t) -(1 row) - -INSERT INTO disthyper_repl -SELECT * FROM normal; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 36, + - "num_relations": 2, + - "num_reltuples": 581, + - "num_replica_chunks": 18, + - "num_replicated_distributed_hypertables": 1+ - } -(1 row) - --- Create a continuous aggregate on the distributed hypertable -CREATE MATERIALIZED VIEW distcontagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg" -CREATE MATERIALIZED VIEW distcontagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg_old" -VACUUM; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'continuous_aggregates') AS continuous_aggregates -FROM relations; - continuous_aggregates ------------------------------------------------- - { + - "heap_size": 425984, + - "toast_size": 40960, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 409600, + - "num_children": 8, + - "num_relations": 4, + - "num_reltuples": 2336, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 2, + - "num_caggs_on_distributed_hypertables": 2,+ - "num_caggs_using_real_time_aggregation": 3+ - } -(1 row) - --- check telemetry for fixed schedule jobs works -create or replace procedure job_test_fixed(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_fixed'; -end -$$; -create or replace procedure job_test_drifting(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_drifting'; -end -$$; --- before adding the jobs -select get_telemetry_report()->'num_user_defined_actions_fixed'; - ?column? ----------- - 0 -(1 row) - -select get_telemetry_report()->'num_user_defined_actions'; - ?column? ----------- - 0 -(1 row) - -select add_job('job_test_fixed', '1 week'); - add_job ---------- - 1000 -(1 row) - -select add_job('job_test_drifting', '1 week', fixed_schedule => false); - add_job ---------- - 1001 -(1 row) - --- add continuous aggregate refresh policy for contagg -select add_continuous_aggregate_policy('contagg', interval '3 weeks', NULL, interval '3 weeks'); -- drifting - add_continuous_aggregate_policy ---------------------------------- - 1002 -(1 row) - -select add_continuous_aggregate_policy('contagg_old', interval '3 weeks', NULL, interval '3 weeks', initial_start => now()); -- fixed - add_continuous_aggregate_policy ---------------------------------- - 1003 -(1 row) - --- add retention policy, fixed -select add_retention_policy('hyper', interval '1 year', initial_start => now()); - add_retention_policy ----------------------- - 1004 -(1 row) - --- add compression policy -select add_compression_policy('hyper', interval '3 weeks', initial_start => now()); - add_compression_policy ------------------------- - 1005 -(1 row) - -select r->'num_user_defined_actions_fixed' as UDA_fixed, r->'num_user_defined_actions' AS UDA_drifting FROM get_telemetry_report() r; - uda_fixed | uda_drifting ------------+-------------- - 1 | 1 -(1 row) - -select r->'num_continuous_aggs_policies_fixed' as contagg_fixed, r->'num_continuous_aggs_policies' as contagg_drifting FROM get_telemetry_report() r; - contagg_fixed | contagg_drifting ----------------+------------------ - 1 | 1 -(1 row) - -select r->'num_compression_policies_fixed' as compress_fixed, r->'num_retention_policies_fixed' as retention_fixed FROM get_telemetry_report() r; - compress_fixed | retention_fixed -----------------+----------------- - 1 | 1 -(1 row) - -DELETE FROM _timescaledb_config.bgw_job WHERE id = 2; -TRUNCATE _timescaledb_internal.job_errors; --- create some "errors" for testing -INSERT INTO -_timescaledb_config.bgw_job(id, application_name, schedule_interval, max_runtime, max_retries, retry_period, proc_schema, proc_name) -VALUES (2000, 'User-Defined Action [2000]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_1'), -(2001, 'User-Defined Action [2001]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_2'), -(2002, 'Compression Policy [2002]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_compression'), -(2003, 'Retention Policy [2003]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_retention'), -(2004, 'Refresh Continuous Aggregate Policy [2004]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'), --- user decided to define a custom action in the _timescaledb_functions schema, we group it with the User-defined actions -(2005, 'User-Defined Action [2005]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'); --- create some errors for them -INSERT INTO -_timescaledb_internal.job_errors(job_id, pid, start_time, finish_time, error_data) -values (2000, 12345, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_1"}'), -(2000, 23456, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"ABCDE", "proc_schema": "public", "proc_name": "custom_action_1"}'), -(2001, 54321, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_2"}'), -(2002, 23443, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"JF009", "proc_schema":"_timescaledb_functions", "proc_name": "policy_compression"}'), -(2003, 14567, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_retention"}'), -(2004, 78907, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'), -(2005, 45757, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'); --- we have 3 error records for user-defined actions, and three for policies, so we expect 4 types of jobs -SELECT jsonb_pretty(get_telemetry_report() -> 'errors_by_sqlerrcode'); - jsonb_pretty ----------------------------------------------- - { + - "policy_retention": { + - "P0001": 1 + - }, + - "policy_compression": { + - "JF009": 1 + - }, + - "user_defined_action": { + - "ABCDE": 1, + - "P0001": 2 + - }, + - "policy_refresh_continuous_aggregate": {+ - "P0001": 2 + - } + - } -(1 row) - --- for job statistics, insert some records into bgw_job_stats -INSERT INTO _timescaledb_internal.bgw_job_stat -values -(2000, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2001, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2002, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2003, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2004, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2005, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0); -SELECT jsonb_pretty(get_telemetry_report() -> 'stats_by_job_type'); - jsonb_pretty ------------------------------------------------- - { + - "policy_retention": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_compression": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "user_defined_action": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_refresh_continuous_aggregate": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - } + - } -(1 row) - --- create nested continuous aggregates - copied from cagg_on_cagg_common -CREATE TABLE conditions ( - time timestamptz NOT NULL, - temperature int -); -SELECT create_hypertable('conditions', 'time'); - create_hypertable --------------------------- - (10,public,conditions,t) -(1 row) - -CREATE MATERIALIZED VIEW conditions_summary_hourly_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 hour', "time") AS bucket, - SUM(temperature) AS temperature -FROM conditions -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_daily_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 day', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_hourly_1 -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_weekly_3 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 week', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_daily_2 -GROUP BY 1 -WITH NO DATA; -SELECT jsonb_pretty(get_telemetry_report() -> 'relations' -> 'continuous_aggregates' -> 'num_caggs_nested'); - jsonb_pretty --------------- - 2 -(1 row) - -DROP VIEW relations; -DROP MATERIALIZED VIEW telemetry_report; -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DN_DBNAME_1 WITH (FORCE); -DROP DATABASE :DN_DBNAME_2 WITH (FORCE); diff --git a/tsl/test/expected/telemetry_stats-15.out b/tsl/test/expected/telemetry_stats-15.out deleted file mode 100644 index cf2aa3db3df..00000000000 --- a/tsl/test/expected/telemetry_stats-15.out +++ /dev/null @@ -1,1197 +0,0 @@ --- 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. ---telemetry tests that require a community license -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; --- function call info size is too variable for this test, so disable it -SET timescaledb.telemetry_level='no_functions'; -SELECT setseed(1); - setseed ---------- - -(1 row) - --- Create a materialized view from the telemetry report so that we --- don't regenerate telemetry for every query. Filter heap_size for --- materialized views since PG14 reports a different heap size for --- them compared to earlier PG versions. -CREATE MATERIALIZED VIEW telemetry_report AS -SELECT (r #- '{relations,materialized_views,heap_size}') AS r -FROM get_telemetry_report() r; -CREATE VIEW relations AS -SELECT r -> 'relations' AS rels -FROM telemetry_report; -SELECT rels -> 'continuous_aggregates' -> 'num_relations' AS num_continuous_aggs, - rels -> 'hypertables' -> 'num_relations' AS num_hypertables -FROM relations; - num_continuous_aggs | num_hypertables ----------------------+----------------- - 0 | 0 -(1 row) - --- check telemetry picks up flagged content from metadata -SELECT r -> 'db_metadata' AS db_metadata -FROM telemetry_report; - db_metadata -------------- - {} -(1 row) - --- check timescaledb_telemetry.cloud -SELECT r -> 'instance_metadata' AS instance_metadata -FROM telemetry_report r; - instance_metadata -------------------- - {"cloud": "ci"} -(1 row) - -CREATE TABLE normal (time timestamptz NOT NULL, device int, temp float); -CREATE TABLE part (time timestamptz NOT NULL, device int, temp float) PARTITION BY RANGE (time); -CREATE TABLE part_t1 PARTITION OF part FOR VALUES FROM ('2018-01-01') TO ('2018-02-01') PARTITION BY HASH (device); -CREATE TABLE part_t2 PARTITION OF part FOR VALUES FROM ('2018-02-01') TO ('2018-03-01') PARTITION BY HASH (device); -CREATE TABLE part_t1_d1 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t1_d2 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE part_t2_d1 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t2_d2 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE hyper (LIKE normal); -SELECT table_name FROM create_hypertable('hyper', 'time'); - table_name ------------- - hyper -(1 row) - -CREATE MATERIALIZED VIEW contagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg" is already up-to-date -CREATE MATERIALIZED VIEW contagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg_old" is already up-to-date --- Create another view (already have the "relations" view) -CREATE VIEW devices AS -SELECT DISTINCT ON (device) device -FROM hyper; --- Show relations with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 0, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 0 + - }, + - "hypertables": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 8192, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 0, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "continuous_aggregates": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Insert data -INSERT INTO normal -SELECT t, ceil(random() * 10)::int, random() * 30 -FROM generate_series('2018-01-01'::timestamptz, '2018-02-28', '2h') t; -INSERT INTO hyper -SELECT * FROM normal; -INSERT INTO part -SELECT * FROM normal; -CALL refresh_continuous_aggregate('contagg', NULL, NULL); -CALL refresh_continuous_aggregate('contagg_old', NULL, NULL); --- ANALYZE to get updated reltuples stats -ANALYZE normal, hyper, part; -SELECT count(c) FROM show_chunks('hyper') c; - count -------- - 9 -(1 row) - -SELECT count(c) FROM show_chunks('contagg') c; - count -------- - 2 -(1 row) - -SELECT count(c) FROM show_chunks('contagg_old') c; - count -------- - 2 -(1 row) - --- Update and show the telemetry report -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 155648, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 188416, + - "toast_size": 16384, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 229376, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Actual row count should be the same as reltuples stats for all tables -SELECT (SELECT count(*) FROM normal) num_inserted_rows, - (SELECT rels -> 'tables' -> 'num_reltuples' FROM relations) normal_reltuples, - (SELECT rels -> 'hypertables' -> 'num_reltuples' FROM relations) hyper_reltuples, - (SELECT rels -> 'partitioned_tables' -> 'num_reltuples' FROM relations) part_reltuples; - num_inserted_rows | normal_reltuples | hyper_reltuples | part_reltuples --------------------+------------------+-----------------+---------------- - 697 | 697 | 697 | 697 -(1 row) - --- Add compression -ALTER TABLE hyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('hyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------- - _timescaledb_internal._hyper_1_1_chunk - _timescaledb_internal._hyper_1_2_chunk - _timescaledb_internal._hyper_1_3_chunk - _timescaledb_internal._hyper_1_4_chunk -(4 rows) - -ALTER MATERIALIZED VIEW contagg SET (timescaledb.compress); -NOTICE: defaulting compress_segmentby to device -NOTICE: defaulting compress_orderby to hour -SELECT compress_chunk(c) -FROM show_chunks('contagg') c ORDER BY c LIMIT 1; - compress_chunk ------------------------------------------ - _timescaledb_internal._hyper_2_10_chunk -(1 row) - --- Turn of real-time aggregation -ALTER MATERIALIZED VIEW contagg SET (timescaledb.materialized_only = true); -ANALYZE normal, hyper, part; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 32768, + - "compression": { + - "compressed_heap_size": 32768, + - "compressed_row_count": 4, + - "compressed_toast_size": 32768, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 32768, + - "uncompressed_row_count": 284, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 65536, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 122880, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 413 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 180224, + - "toast_size": 24576, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 180224, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 1 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Add distributed hypertables -\set DN_DBNAME_1 :TEST_DBNAME _1 -\set DN_DBNAME_2 :TEST_DBNAME _2 --- Not an access node or data node -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - | "none" -(1 row) - --- Become an access node by adding a data node -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_1', host => 'localhost', database => :'DN_DBNAME_1'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_1 | db_telemetry_stats_1 | t | t | t -(1 row) - --- Telemetry should show one data node and "acces node" status -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - 1 | "access node" -(1 row) - --- See telemetry report from a data node -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT test.remote_exec(NULL, $$ - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -num_data_nodes|distributed_member ---------------+------------------ - |"data node" -(1 row) - - - remote_exec -------------- - -(1 row) - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_2', host => 'localhost', database => :'DN_DBNAME_2'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_2 | db_telemetry_stats_2 | t | t | t -(1 row) - -CREATE TABLE disthyper (LIKE normal); -SELECT create_distributed_hypertable('disthyper', 'time', 'device'); - create_distributed_hypertable -------------------------------- - (6,public,disthyper,t) -(1 row) - --- Show distributed hypertables stats with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- No datanode-related stats on the access node -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn -FROM relations; - distributed_hypertables_dn ------------------------------------------ - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0,+ - "num_compressed_hypertables": 0+ - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - } -(1 row) - --- Insert data into the distributed hypertable -INSERT INTO disthyper -SELECT * FROM normal; --- Update telemetry stats and show output on access node and data --- nodes. Note that the access node doesn't store data so shows --- zero. It should have stats from ANALYZE, though, like --- num_reltuples. -ANALYZE disthyper; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 697, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 368 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 329 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Add compression -ALTER TABLE disthyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('disthyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------------- - _timescaledb_internal._dist_hyper_6_19_chunk - _timescaledb_internal._dist_hyper_6_20_chunk - _timescaledb_internal._dist_hyper_6_21_chunk - _timescaledb_internal._dist_hyper_6_22_chunk -(4 rows) - -ANALYZE disthyper; --- Update telemetry stats and show updated compression stats -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 581, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 56, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 312 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 60, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 269 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Create a replicated distributed hypertable and show replication stats -CREATE TABLE disthyper_repl (LIKE normal); -SELECT create_distributed_hypertable('disthyper_repl', 'time', 'device', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (7,public,disthyper_repl,t) -(1 row) - -INSERT INTO disthyper_repl -SELECT * FROM normal; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 36, + - "num_relations": 2, + - "num_reltuples": 581, + - "num_replica_chunks": 18, + - "num_replicated_distributed_hypertables": 1+ - } -(1 row) - --- Create a continuous aggregate on the distributed hypertable -CREATE MATERIALIZED VIEW distcontagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg" -CREATE MATERIALIZED VIEW distcontagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg_old" -VACUUM; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'continuous_aggregates') AS continuous_aggregates -FROM relations; - continuous_aggregates ------------------------------------------------- - { + - "heap_size": 425984, + - "toast_size": 40960, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 409600, + - "num_children": 8, + - "num_relations": 4, + - "num_reltuples": 2336, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 2, + - "num_caggs_on_distributed_hypertables": 2,+ - "num_caggs_using_real_time_aggregation": 3+ - } -(1 row) - --- check telemetry for fixed schedule jobs works -create or replace procedure job_test_fixed(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_fixed'; -end -$$; -create or replace procedure job_test_drifting(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_drifting'; -end -$$; --- before adding the jobs -select get_telemetry_report()->'num_user_defined_actions_fixed'; - ?column? ----------- - 0 -(1 row) - -select get_telemetry_report()->'num_user_defined_actions'; - ?column? ----------- - 0 -(1 row) - -select add_job('job_test_fixed', '1 week'); - add_job ---------- - 1000 -(1 row) - -select add_job('job_test_drifting', '1 week', fixed_schedule => false); - add_job ---------- - 1001 -(1 row) - --- add continuous aggregate refresh policy for contagg -select add_continuous_aggregate_policy('contagg', interval '3 weeks', NULL, interval '3 weeks'); -- drifting - add_continuous_aggregate_policy ---------------------------------- - 1002 -(1 row) - -select add_continuous_aggregate_policy('contagg_old', interval '3 weeks', NULL, interval '3 weeks', initial_start => now()); -- fixed - add_continuous_aggregate_policy ---------------------------------- - 1003 -(1 row) - --- add retention policy, fixed -select add_retention_policy('hyper', interval '1 year', initial_start => now()); - add_retention_policy ----------------------- - 1004 -(1 row) - --- add compression policy -select add_compression_policy('hyper', interval '3 weeks', initial_start => now()); - add_compression_policy ------------------------- - 1005 -(1 row) - -select r->'num_user_defined_actions_fixed' as UDA_fixed, r->'num_user_defined_actions' AS UDA_drifting FROM get_telemetry_report() r; - uda_fixed | uda_drifting ------------+-------------- - 1 | 1 -(1 row) - -select r->'num_continuous_aggs_policies_fixed' as contagg_fixed, r->'num_continuous_aggs_policies' as contagg_drifting FROM get_telemetry_report() r; - contagg_fixed | contagg_drifting ----------------+------------------ - 1 | 1 -(1 row) - -select r->'num_compression_policies_fixed' as compress_fixed, r->'num_retention_policies_fixed' as retention_fixed FROM get_telemetry_report() r; - compress_fixed | retention_fixed -----------------+----------------- - 1 | 1 -(1 row) - -DELETE FROM _timescaledb_config.bgw_job WHERE id = 2; -TRUNCATE _timescaledb_internal.job_errors; --- create some "errors" for testing -INSERT INTO -_timescaledb_config.bgw_job(id, application_name, schedule_interval, max_runtime, max_retries, retry_period, proc_schema, proc_name) -VALUES (2000, 'User-Defined Action [2000]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_1'), -(2001, 'User-Defined Action [2001]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_2'), -(2002, 'Compression Policy [2002]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_compression'), -(2003, 'Retention Policy [2003]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_retention'), -(2004, 'Refresh Continuous Aggregate Policy [2004]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'), --- user decided to define a custom action in the _timescaledb_functions schema, we group it with the User-defined actions -(2005, 'User-Defined Action [2005]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'); --- create some errors for them -INSERT INTO -_timescaledb_internal.job_errors(job_id, pid, start_time, finish_time, error_data) -values (2000, 12345, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_1"}'), -(2000, 23456, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"ABCDE", "proc_schema": "public", "proc_name": "custom_action_1"}'), -(2001, 54321, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_2"}'), -(2002, 23443, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"JF009", "proc_schema":"_timescaledb_functions", "proc_name": "policy_compression"}'), -(2003, 14567, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_retention"}'), -(2004, 78907, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'), -(2005, 45757, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'); --- we have 3 error records for user-defined actions, and three for policies, so we expect 4 types of jobs -SELECT jsonb_pretty(get_telemetry_report() -> 'errors_by_sqlerrcode'); - jsonb_pretty ----------------------------------------------- - { + - "policy_retention": { + - "P0001": 1 + - }, + - "policy_compression": { + - "JF009": 1 + - }, + - "user_defined_action": { + - "ABCDE": 1, + - "P0001": 2 + - }, + - "policy_refresh_continuous_aggregate": {+ - "P0001": 2 + - } + - } -(1 row) - --- for job statistics, insert some records into bgw_job_stats -INSERT INTO _timescaledb_internal.bgw_job_stat -values -(2000, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2001, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2002, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2003, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2004, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2005, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0); -SELECT jsonb_pretty(get_telemetry_report() -> 'stats_by_job_type'); - jsonb_pretty ------------------------------------------------- - { + - "policy_retention": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_compression": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "user_defined_action": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_refresh_continuous_aggregate": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - } + - } -(1 row) - --- create nested continuous aggregates - copied from cagg_on_cagg_common -CREATE TABLE conditions ( - time timestamptz NOT NULL, - temperature int -); -SELECT create_hypertable('conditions', 'time'); - create_hypertable --------------------------- - (10,public,conditions,t) -(1 row) - -CREATE MATERIALIZED VIEW conditions_summary_hourly_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 hour', "time") AS bucket, - SUM(temperature) AS temperature -FROM conditions -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_daily_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 day', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_hourly_1 -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_weekly_3 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 week', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_daily_2 -GROUP BY 1 -WITH NO DATA; -SELECT jsonb_pretty(get_telemetry_report() -> 'relations' -> 'continuous_aggregates' -> 'num_caggs_nested'); - jsonb_pretty --------------- - 2 -(1 row) - -DROP VIEW relations; -DROP MATERIALIZED VIEW telemetry_report; -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DN_DBNAME_1 WITH (FORCE); -DROP DATABASE :DN_DBNAME_2 WITH (FORCE); diff --git a/tsl/test/expected/telemetry_stats-16.out b/tsl/test/expected/telemetry_stats-16.out deleted file mode 100644 index cf2aa3db3df..00000000000 --- a/tsl/test/expected/telemetry_stats-16.out +++ /dev/null @@ -1,1197 +0,0 @@ --- 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. ---telemetry tests that require a community license -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; --- function call info size is too variable for this test, so disable it -SET timescaledb.telemetry_level='no_functions'; -SELECT setseed(1); - setseed ---------- - -(1 row) - --- Create a materialized view from the telemetry report so that we --- don't regenerate telemetry for every query. Filter heap_size for --- materialized views since PG14 reports a different heap size for --- them compared to earlier PG versions. -CREATE MATERIALIZED VIEW telemetry_report AS -SELECT (r #- '{relations,materialized_views,heap_size}') AS r -FROM get_telemetry_report() r; -CREATE VIEW relations AS -SELECT r -> 'relations' AS rels -FROM telemetry_report; -SELECT rels -> 'continuous_aggregates' -> 'num_relations' AS num_continuous_aggs, - rels -> 'hypertables' -> 'num_relations' AS num_hypertables -FROM relations; - num_continuous_aggs | num_hypertables ----------------------+----------------- - 0 | 0 -(1 row) - --- check telemetry picks up flagged content from metadata -SELECT r -> 'db_metadata' AS db_metadata -FROM telemetry_report; - db_metadata -------------- - {} -(1 row) - --- check timescaledb_telemetry.cloud -SELECT r -> 'instance_metadata' AS instance_metadata -FROM telemetry_report r; - instance_metadata -------------------- - {"cloud": "ci"} -(1 row) - -CREATE TABLE normal (time timestamptz NOT NULL, device int, temp float); -CREATE TABLE part (time timestamptz NOT NULL, device int, temp float) PARTITION BY RANGE (time); -CREATE TABLE part_t1 PARTITION OF part FOR VALUES FROM ('2018-01-01') TO ('2018-02-01') PARTITION BY HASH (device); -CREATE TABLE part_t2 PARTITION OF part FOR VALUES FROM ('2018-02-01') TO ('2018-03-01') PARTITION BY HASH (device); -CREATE TABLE part_t1_d1 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t1_d2 PARTITION OF part_t1 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE part_t2_d1 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 0); -CREATE TABLE part_t2_d2 PARTITION OF part_t2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -CREATE TABLE hyper (LIKE normal); -SELECT table_name FROM create_hypertable('hyper', 'time'); - table_name ------------- - hyper -(1 row) - -CREATE MATERIALIZED VIEW contagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg" is already up-to-date -CREATE MATERIALIZED VIEW contagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - hyper -GROUP BY hour, device; -NOTICE: continuous aggregate "contagg_old" is already up-to-date --- Create another view (already have the "relations" view) -CREATE VIEW devices AS -SELECT DISTINCT ON (device) device -FROM hyper; --- Show relations with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 0, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 0 + - }, + - "hypertables": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 8192, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 0, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "continuous_aggregates": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Insert data -INSERT INTO normal -SELECT t, ceil(random() * 10)::int, random() * 30 -FROM generate_series('2018-01-01'::timestamptz, '2018-02-28', '2h') t; -INSERT INTO hyper -SELECT * FROM normal; -INSERT INTO part -SELECT * FROM normal; -CALL refresh_continuous_aggregate('contagg', NULL, NULL); -CALL refresh_continuous_aggregate('contagg_old', NULL, NULL); --- ANALYZE to get updated reltuples stats -ANALYZE normal, hyper, part; -SELECT count(c) FROM show_chunks('hyper') c; - count -------- - 9 -(1 row) - -SELECT count(c) FROM show_chunks('contagg') c; - count -------- - 2 -(1 row) - -SELECT count(c) FROM show_chunks('contagg_old') c; - count -------- - 2 -(1 row) - --- Update and show the telemetry report -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 155648, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 188416, + - "toast_size": 16384, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "num_compressed_caggs": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0 + - }, + - "indexes_size": 229376, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 2 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Actual row count should be the same as reltuples stats for all tables -SELECT (SELECT count(*) FROM normal) num_inserted_rows, - (SELECT rels -> 'tables' -> 'num_reltuples' FROM relations) normal_reltuples, - (SELECT rels -> 'hypertables' -> 'num_reltuples' FROM relations) hyper_reltuples, - (SELECT rels -> 'partitioned_tables' -> 'num_reltuples' FROM relations) part_reltuples; - num_inserted_rows | normal_reltuples | hyper_reltuples | part_reltuples --------------------+------------------+-----------------+---------------- - 697 | 697 | 697 | 697 -(1 row) - --- Add compression -ALTER TABLE hyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('hyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------- - _timescaledb_internal._hyper_1_1_chunk - _timescaledb_internal._hyper_1_2_chunk - _timescaledb_internal._hyper_1_3_chunk - _timescaledb_internal._hyper_1_4_chunk -(4 rows) - -ALTER MATERIALIZED VIEW contagg SET (timescaledb.compress); -NOTICE: defaulting compress_segmentby to device -NOTICE: defaulting compress_orderby to hour -SELECT compress_chunk(c) -FROM show_chunks('contagg') c ORDER BY c LIMIT 1; - compress_chunk ------------------------------------------ - _timescaledb_internal._hyper_2_10_chunk -(1 row) - --- Turn of real-time aggregation -ALTER MATERIALIZED VIEW contagg SET (timescaledb.materialized_only = true); -ANALYZE normal, hyper, part; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT jsonb_pretty(rels) AS relations FROM relations; - relations ------------------------------------------------------ - { + - "views": { + - "num_relations": 2 + - }, + - "tables": { + - "heap_size": 65536, + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 2, + - "num_reltuples": 697 + - }, + - "hypertables": { + - "heap_size": 73728, + - "toast_size": 32768, + - "compression": { + - "compressed_heap_size": 32768, + - "compressed_row_count": 4, + - "compressed_toast_size": 32768, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 32768, + - "uncompressed_row_count": 284, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 65536, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 122880, + - "num_children": 9, + - "num_relations": 1, + - "num_reltuples": 413 + - }, + - "materialized_views": { + - "toast_size": 8192, + - "indexes_size": 0, + - "num_relations": 1, + - "num_reltuples": 0 + - }, + - "partitioned_tables": { + - "heap_size": 98304, + - "toast_size": 0, + - "indexes_size": 0, + - "num_children": 6, + - "num_relations": 1, + - "num_reltuples": 697 + - }, + - "continuous_aggregates": { + - "heap_size": 180224, + - "toast_size": 24576, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 180224, + - "num_children": 4, + - "num_relations": 2, + - "num_reltuples": 0, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 1, + - "num_caggs_on_distributed_hypertables": 0, + - "num_caggs_using_real_time_aggregation": 1 + - }, + - "distributed_hypertables_data_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - }, + - "distributed_hypertables_access_node": { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } + - } -(1 row) - --- Add distributed hypertables -\set DN_DBNAME_1 :TEST_DBNAME _1 -\set DN_DBNAME_2 :TEST_DBNAME _2 --- Not an access node or data node -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - | "none" -(1 row) - --- Become an access node by adding a data node -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_1', host => 'localhost', database => :'DN_DBNAME_1'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_1 | db_telemetry_stats_1 | t | t | t -(1 row) - --- Telemetry should show one data node and "acces node" status -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - 1 | "access node" -(1 row) - --- See telemetry report from a data node -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT test.remote_exec(NULL, $$ - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -num_data_nodes|distributed_member ---------------+------------------ - |"data node" -(1 row) - - - remote_exec -------------- - -(1 row) - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_2', host => 'localhost', database => :'DN_DBNAME_2'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_2 | db_telemetry_stats_2 | t | t | t -(1 row) - -CREATE TABLE disthyper (LIKE normal); -SELECT create_distributed_hypertable('disthyper', 'time', 'device'); - create_distributed_hypertable -------------------------------- - (6,public,disthyper,t) -(1 row) - --- Show distributed hypertables stats with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- No datanode-related stats on the access node -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn -FROM relations; - distributed_hypertables_dn ------------------------------------------ - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0,+ - "num_compressed_hypertables": 0+ - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - } -(1 row) - --- Insert data into the distributed hypertable -INSERT INTO disthyper -SELECT * FROM normal; --- Update telemetry stats and show output on access node and data --- nodes. Note that the access node doesn't store data so shows --- zero. It should have stats from ANALYZE, though, like --- num_reltuples. -ANALYZE disthyper; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 697, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 368 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 329 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Add compression -ALTER TABLE disthyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('disthyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------------- - _timescaledb_internal._dist_hyper_6_19_chunk - _timescaledb_internal._dist_hyper_6_20_chunk - _timescaledb_internal._dist_hyper_6_21_chunk - _timescaledb_internal._dist_hyper_6_22_chunk -(4 rows) - -ANALYZE disthyper; --- Update telemetry stats and show updated compression stats -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 581, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 56, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 312 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 60, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 269 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Create a replicated distributed hypertable and show replication stats -CREATE TABLE disthyper_repl (LIKE normal); -SELECT create_distributed_hypertable('disthyper_repl', 'time', 'device', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (7,public,disthyper_repl,t) -(1 row) - -INSERT INTO disthyper_repl -SELECT * FROM normal; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 36, + - "num_relations": 2, + - "num_reltuples": 581, + - "num_replica_chunks": 18, + - "num_replicated_distributed_hypertables": 1+ - } -(1 row) - --- Create a continuous aggregate on the distributed hypertable -CREATE MATERIALIZED VIEW distcontagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg" -CREATE MATERIALIZED VIEW distcontagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg_old" -VACUUM; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'continuous_aggregates') AS continuous_aggregates -FROM relations; - continuous_aggregates ------------------------------------------------- - { + - "heap_size": 425984, + - "toast_size": 40960, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 409600, + - "num_children": 8, + - "num_relations": 4, + - "num_reltuples": 2336, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 2, + - "num_caggs_on_distributed_hypertables": 2,+ - "num_caggs_using_real_time_aggregation": 3+ - } -(1 row) - --- check telemetry for fixed schedule jobs works -create or replace procedure job_test_fixed(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_fixed'; -end -$$; -create or replace procedure job_test_drifting(jobid int, config jsonb) language plpgsql as $$ -begin -raise log 'this is job_test_drifting'; -end -$$; --- before adding the jobs -select get_telemetry_report()->'num_user_defined_actions_fixed'; - ?column? ----------- - 0 -(1 row) - -select get_telemetry_report()->'num_user_defined_actions'; - ?column? ----------- - 0 -(1 row) - -select add_job('job_test_fixed', '1 week'); - add_job ---------- - 1000 -(1 row) - -select add_job('job_test_drifting', '1 week', fixed_schedule => false); - add_job ---------- - 1001 -(1 row) - --- add continuous aggregate refresh policy for contagg -select add_continuous_aggregate_policy('contagg', interval '3 weeks', NULL, interval '3 weeks'); -- drifting - add_continuous_aggregate_policy ---------------------------------- - 1002 -(1 row) - -select add_continuous_aggregate_policy('contagg_old', interval '3 weeks', NULL, interval '3 weeks', initial_start => now()); -- fixed - add_continuous_aggregate_policy ---------------------------------- - 1003 -(1 row) - --- add retention policy, fixed -select add_retention_policy('hyper', interval '1 year', initial_start => now()); - add_retention_policy ----------------------- - 1004 -(1 row) - --- add compression policy -select add_compression_policy('hyper', interval '3 weeks', initial_start => now()); - add_compression_policy ------------------------- - 1005 -(1 row) - -select r->'num_user_defined_actions_fixed' as UDA_fixed, r->'num_user_defined_actions' AS UDA_drifting FROM get_telemetry_report() r; - uda_fixed | uda_drifting ------------+-------------- - 1 | 1 -(1 row) - -select r->'num_continuous_aggs_policies_fixed' as contagg_fixed, r->'num_continuous_aggs_policies' as contagg_drifting FROM get_telemetry_report() r; - contagg_fixed | contagg_drifting ----------------+------------------ - 1 | 1 -(1 row) - -select r->'num_compression_policies_fixed' as compress_fixed, r->'num_retention_policies_fixed' as retention_fixed FROM get_telemetry_report() r; - compress_fixed | retention_fixed -----------------+----------------- - 1 | 1 -(1 row) - -DELETE FROM _timescaledb_config.bgw_job WHERE id = 2; -TRUNCATE _timescaledb_internal.job_errors; --- create some "errors" for testing -INSERT INTO -_timescaledb_config.bgw_job(id, application_name, schedule_interval, max_runtime, max_retries, retry_period, proc_schema, proc_name) -VALUES (2000, 'User-Defined Action [2000]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_1'), -(2001, 'User-Defined Action [2001]', interval '3 days', interval '1 hour', 5, interval '5 min', 'public', 'custom_action_2'), -(2002, 'Compression Policy [2002]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_compression'), -(2003, 'Retention Policy [2003]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_retention'), -(2004, 'Refresh Continuous Aggregate Policy [2004]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'), --- user decided to define a custom action in the _timescaledb_functions schema, we group it with the User-defined actions -(2005, 'User-Defined Action [2005]', interval '3 days', interval '1 hour', 5, interval '5 min', '_timescaledb_functions', 'policy_refresh_continuous_aggregate'); --- create some errors for them -INSERT INTO -_timescaledb_internal.job_errors(job_id, pid, start_time, finish_time, error_data) -values (2000, 12345, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_1"}'), -(2000, 23456, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"ABCDE", "proc_schema": "public", "proc_name": "custom_action_1"}'), -(2001, 54321, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"public", "proc_name": "custom_action_2"}'), -(2002, 23443, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"JF009", "proc_schema":"_timescaledb_functions", "proc_name": "policy_compression"}'), -(2003, 14567, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_retention"}'), -(2004, 78907, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'), -(2005, 45757, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '{"sqlerrcode":"P0001", "proc_schema":"_timescaledb_functions", "proc_name": "policy_refresh_continuous_aggregate"}'); --- we have 3 error records for user-defined actions, and three for policies, so we expect 4 types of jobs -SELECT jsonb_pretty(get_telemetry_report() -> 'errors_by_sqlerrcode'); - jsonb_pretty ----------------------------------------------- - { + - "policy_retention": { + - "P0001": 1 + - }, + - "policy_compression": { + - "JF009": 1 + - }, + - "user_defined_action": { + - "ABCDE": 1, + - "P0001": 2 + - }, + - "policy_refresh_continuous_aggregate": {+ - "P0001": 2 + - } + - } -(1 row) - --- for job statistics, insert some records into bgw_job_stats -INSERT INTO _timescaledb_internal.bgw_job_stat -values -(2000, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2001, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2002, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2003, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2004, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0), -(2005, '2040-01-01 00:00:00+00'::timestamptz, '2040-01-01 00:00:01+00'::timestamptz, '-infinity'::timestamptz, '-infinity'::timestamptz, -false, 1, interval '00:00:00', interval '00:00:02', 0, 1, 0, 1, 0); -SELECT jsonb_pretty(get_telemetry_report() -> 'stats_by_job_type'); - jsonb_pretty ------------------------------------------------- - { + - "policy_retention": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_compression": { + - "total_runs": 1, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 1, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 2 secs",+ - "max_consecutive_failures": 1 + - }, + - "user_defined_action": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - }, + - "policy_refresh_continuous_aggregate": { + - "total_runs": 2, + - "total_crashes": 0, + - "total_duration": "@ 0", + - "total_failures": 2, + - "total_successes": 0, + - "max_consecutive_crashes": 0, + - "total_duration_failures": "@ 4 secs",+ - "max_consecutive_failures": 1 + - } + - } -(1 row) - --- create nested continuous aggregates - copied from cagg_on_cagg_common -CREATE TABLE conditions ( - time timestamptz NOT NULL, - temperature int -); -SELECT create_hypertable('conditions', 'time'); - create_hypertable --------------------------- - (10,public,conditions,t) -(1 row) - -CREATE MATERIALIZED VIEW conditions_summary_hourly_1 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 hour', "time") AS bucket, - SUM(temperature) AS temperature -FROM conditions -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_daily_2 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 day', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_hourly_1 -GROUP BY 1 -WITH NO DATA; -CREATE MATERIALIZED VIEW conditions_summary_weekly_3 -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - time_bucket('1 week', "bucket") AS bucket, - SUM(temperature) AS temperature -FROM conditions_summary_daily_2 -GROUP BY 1 -WITH NO DATA; -SELECT jsonb_pretty(get_telemetry_report() -> 'relations' -> 'continuous_aggregates' -> 'num_caggs_nested'); - jsonb_pretty --------------- - 2 -(1 row) - -DROP VIEW relations; -DROP MATERIALIZED VIEW telemetry_report; -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DN_DBNAME_1 WITH (FORCE); -DROP DATABASE :DN_DBNAME_2 WITH (FORCE); diff --git a/tsl/test/expected/telemetry_stats-13.out b/tsl/test/expected/telemetry_stats.out similarity index 62% rename from tsl/test/expected/telemetry_stats-13.out rename to tsl/test/expected/telemetry_stats.out index 095d92d7cd7..39f43f7e6eb 100644 --- a/tsl/test/expected/telemetry_stats-13.out +++ b/tsl/test/expected/telemetry_stats.out @@ -505,464 +505,6 @@ SELECT jsonb_pretty(rels) AS relations FROM relations; } (1 row) --- Add distributed hypertables -\set DN_DBNAME_1 :TEST_DBNAME _1 -\set DN_DBNAME_2 :TEST_DBNAME _2 --- Not an access node or data node -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - | "none" -(1 row) - --- Become an access node by adding a data node -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_1', host => 'localhost', database => :'DN_DBNAME_1'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_1 | db_telemetry_stats_1 | t | t | t -(1 row) - --- Telemetry should show one data node and "acces node" status -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - num_data_nodes | distributed_member -----------------+-------------------- - 1 | "access node" -(1 row) - --- See telemetry report from a data node -\ir include/remote_exec.sql --- 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. -CREATE SCHEMA IF NOT EXISTS test; -psql:include/remote_exec.sql:5: NOTICE: schema "test" already exists, skipping -GRANT USAGE ON SCHEMA test TO PUBLIC; -CREATE OR REPLACE FUNCTION test.remote_exec(srv_name name[], command text) -RETURNS VOID -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec' -LANGUAGE C; -CREATE OR REPLACE FUNCTION test.remote_exec_get_result_strings(srv_name name[], command text) -RETURNS TABLE("table_record" CSTRING[]) -AS :TSL_MODULE_PATHNAME, 'ts_remote_exec_get_result_strings' -LANGUAGE C; -SELECT test.remote_exec(NULL, $$ - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -num_data_nodes|distributed_member ---------------+------------------ - |"data node" -(1 row) - - - remote_exec -------------- - -(1 row) - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_2', host => 'localhost', database => :'DN_DBNAME_2'); - node_name | database | node_created | database_created | extension_created --------------+----------------------+--------------+------------------+------------------- - data_node_2 | db_telemetry_stats_2 | t | t | t -(1 row) - -CREATE TABLE disthyper (LIKE normal); -SELECT create_distributed_hypertable('disthyper', 'time', 'device'); - create_distributed_hypertable -------------------------------- - (6,public,disthyper,t) -(1 row) - --- Show distributed hypertables stats with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 1, + - "num_reltuples": 0, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- No datanode-related stats on the access node -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn -FROM relations; - distributed_hypertables_dn ------------------------------------------ - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0,+ - "num_compressed_hypertables": 0+ - }, + - "indexes_size": 0, + - "num_children": 0, + - "num_relations": 0, + - "num_reltuples": 0 + - } -(1 row) - --- Insert data into the distributed hypertable -INSERT INTO disthyper -SELECT * FROM normal; --- Update telemetry stats and show output on access node and data --- nodes. Note that the access node doesn't store data so shows --- zero. It should have stats from ANALYZE, though, like --- num_reltuples. -ANALYZE disthyper; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 0, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 0 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 697, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 357 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -{ - "heap_size": 73728, - "toast_size": 0, - "compression": { - "compressed_heap_size": 0, - "compressed_row_count": 0, - "compressed_toast_size": 0, - "num_compressed_chunks": 0, - "uncompressed_heap_size": 0, - "uncompressed_row_count": 0, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 0, - "num_compressed_hypertables": 0 - }, - "indexes_size": 311296, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 340 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Add compression -ALTER TABLE disthyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('disthyper') c ORDER BY c LIMIT 4; - compress_chunk ----------------------------------------------- - _timescaledb_internal._dist_hyper_6_19_chunk - _timescaledb_internal._dist_hyper_6_20_chunk - _timescaledb_internal._dist_hyper_6_21_chunk - _timescaledb_internal._dist_hyper_6_22_chunk -(4 rows) - -ANALYZE disthyper; --- Update telemetry stats and show updated compression stats -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 18, + - "num_relations": 1, + - "num_reltuples": 581, + - "num_replica_chunks": 0, + - "num_replicated_distributed_hypertables": 0+ - } -(1 row) - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); -NOTICE: [data_node_1]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_1]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 72, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 285 -} -(1 row) - - -NOTICE: [data_node_2]: - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t -NOTICE: [data_node_2]: -distributed_hypertables_dn ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -{ - "heap_size": 73728, - "toast_size": 16384, - "compression": { - "compressed_heap_size": 16384, - "compressed_row_count": 2, - "compressed_toast_size": 16384, - "num_compressed_chunks": 2, - "uncompressed_heap_size": 16384, - "uncompressed_row_count": 44, - "compressed_indexes_size": 0, - "uncompressed_toast_size": 0, - "uncompressed_indexes_size": 65536, - "num_compressed_hypertables": 1 - }, - "indexes_size": 278528, - "num_children": 9, - "num_relations": 1, - "num_reltuples": 296 -} -(1 row) - - - remote_exec -------------- - -(1 row) - --- Create a replicated distributed hypertable and show replication stats -CREATE TABLE disthyper_repl (LIKE normal); -SELECT create_distributed_hypertable('disthyper_repl', 'time', 'device', replication_factor => 2); - create_distributed_hypertable -------------------------------- - (7,public,disthyper_repl,t) -(1 row) - -INSERT INTO disthyper_repl -SELECT * FROM normal; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - distributed_hypertables_an -------------------------------------------------- - { + - "heap_size": 0, + - "toast_size": 0, + - "compression": { + - "compressed_heap_size": 0, + - "compressed_row_count": 0, + - "compressed_toast_size": 0, + - "num_compressed_chunks": 4, + - "uncompressed_heap_size": 0, + - "uncompressed_row_count": 0, + - "compressed_indexes_size": 0, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 0, + - "num_compressed_hypertables": 1 + - }, + - "indexes_size": 0, + - "num_children": 36, + - "num_relations": 2, + - "num_reltuples": 581, + - "num_replica_chunks": 18, + - "num_replicated_distributed_hypertables": 1+ - } -(1 row) - --- Create a continuous aggregate on the distributed hypertable -CREATE MATERIALIZED VIEW distcontagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg" -CREATE MATERIALIZED VIEW distcontagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; -NOTICE: refreshing continuous aggregate "distcontagg_old" -VACUUM; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'continuous_aggregates') AS continuous_aggregates -FROM relations; - continuous_aggregates ------------------------------------------------- - { + - "heap_size": 425984, + - "toast_size": 40960, + - "compression": { + - "compressed_heap_size": 40960, + - "compressed_row_count": 10, + - "num_compressed_caggs": 1, + - "compressed_toast_size": 8192, + - "num_compressed_chunks": 1, + - "uncompressed_heap_size": 49152, + - "uncompressed_row_count": 452, + - "compressed_indexes_size": 16384, + - "uncompressed_toast_size": 0, + - "uncompressed_indexes_size": 81920 + - }, + - "indexes_size": 409600, + - "num_children": 8, + - "num_relations": 4, + - "num_reltuples": 2336, + - "num_caggs_nested": 0, + - "num_caggs_finalized": 2, + - "num_caggs_on_distributed_hypertables": 2,+ - "num_caggs_using_real_time_aggregation": 3+ - } -(1 row) - -- check telemetry for fixed schedule jobs works create or replace procedure job_test_fixed(jobid int, config jsonb) language plpgsql as $$ begin @@ -1155,9 +697,9 @@ CREATE TABLE conditions ( temperature int ); SELECT create_hypertable('conditions', 'time'); - create_hypertable --------------------------- - (10,public,conditions,t) + create_hypertable +------------------------- + (6,public,conditions,t) (1 row) CREATE MATERIALIZED VIEW conditions_summary_hourly_1 @@ -1192,6 +734,3 @@ SELECT jsonb_pretty(get_telemetry_report() -> 'relations' -> 'continuous_aggrega DROP VIEW relations; DROP MATERIALIZED VIEW telemetry_report; -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DN_DBNAME_1 WITH (FORCE); -DROP DATABASE :DN_DBNAME_2 WITH (FORCE); diff --git a/tsl/test/isolation/specs/CMakeLists.txt b/tsl/test/isolation/specs/CMakeLists.txt index 766f5d22ca5..20f58e018e7 100644 --- a/tsl/test/isolation/specs/CMakeLists.txt +++ b/tsl/test/isolation/specs/CMakeLists.txt @@ -4,7 +4,6 @@ set(TEST_TEMPLATES_MODULE reorder_deadlock.spec.in set(TEST_TEMPLATES_MODULE_DEBUG reorder_vs_insert.spec.in reorder_vs_select.spec.in - dist_su_copy_chunk.spec.in dist_cmd_exec.spec.in decompression_chunk_and_parallel_query.in) list( @@ -18,7 +17,7 @@ list( deadlock_drop_chunks_compress.spec osm_range_updates_iso.spec) -if(ENABLE_MULTINODE_TESTS) +if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") list(APPEND TEST_FILES cagg_concurrent_refresh_dist_ht.spec) endif() @@ -28,7 +27,7 @@ endif() if(CMAKE_BUILD_TYPE MATCHES Debug) list(APPEND TEST_TEMPLATES_MODULE ${TEST_TEMPLATES_MODULE_DEBUG}) - list(APPEND TEST_FILES cagg_drop_chunks_iso.spec compression_chunk_race.spec + list(APPEND TEST_FILES compression_chunk_race.spec compression_merge_race.spec decompression_chunk_and_parallel_query_wo_idx.spec) if(PG_VERSION VERSION_GREATER_EQUAL "14.0") @@ -43,15 +42,19 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) list(APPEND TEST_FILES deadlock_recompress_chunk.spec) endif() - if(ENABLE_MULTINODE_TESTS) + if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") list( APPEND TEST_FILES cagg_multi_dist_ht.spec + cagg_drop_chunks_iso.spec dist_ha_chunk_drop.spec dist_ha_chunk_drop.spec dist_restore_point.spec remote_create_chunk.spec) + + list(APPEND TEST_TEMPLATES_MODULE_DEBUG dist_cmd_exec.spec.in + dist_su_copy_chunk.spec.in) endif() endif(CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/tsl/test/shared/expected/gapfill.out b/tsl/test/shared/expected/gapfill-13.out similarity index 100% rename from tsl/test/shared/expected/gapfill.out rename to tsl/test/shared/expected/gapfill-13.out diff --git a/tsl/test/shared/expected/gapfill-14.out b/tsl/test/shared/expected/gapfill-14.out new file mode 100644 index 00000000000..d2e2e4ec598 --- /dev/null +++ b/tsl/test/shared/expected/gapfill-14.out @@ -0,0 +1,3366 @@ +-- 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. +\set EXPLAIN 'EXPLAIN (COSTS OFF)' +-- we want to see error details in the output +\set VERBOSITY default +CREATE TABLE gapfill_plan_test(time timestamptz NOT NULL, value float); +SELECT table_name FROM create_hypertable('gapfill_plan_test','time',chunk_time_interval=>'4 weeks'::interval); + table_name + gapfill_plan_test +(1 row) + +INSERT INTO gapfill_plan_test SELECT generate_series('2018-01-01'::timestamptz,'2018-04-01'::timestamptz,'1m'::interval), 1.0; +-- simple example +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test sorting +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test sort direction +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1 DESC; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) DESC + -> Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) NULLS FIRST + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now()) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test order by aggregate function +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2,1; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)), (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test query without order by +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test parallel query +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + avg(value) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with locf +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with interpolate +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- make sure we can run gapfill in parallel workers +-- ensure this plan runs in parallel +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; +QUERY PLAN + Limit + -> Sort + Sort Key: (interpolate(avg(gapfill_plan_test.value), NULL::record, NULL::record)) + -> Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(18 rows) + +-- actually run a parallel gapfill +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + time_bucket_gapfill | interpolate +------------------------------+------------- + Mon Jan 01 00:00:00 2018 PST | 1 +(1 row) + +-- test sort optimizations +-- test sort optimization with single member order by, +-- should use index scan (no GapFill node for this one since we're not gapfilling) +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +SET max_parallel_workers_per_gather TO 0; +-- test sort optimizations with locf +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +-- test sort optimizations with interpolate +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +RESET max_parallel_workers_per_gather; +CREATE INDEX gapfill_plan_test_indx ON gapfill_plan_test(value, time); +-- test sort optimization with ordering by multiple columns and time_bucket_gapfill not last, +-- must not use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1,2; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)), _hyper_X_X_chunk.value + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +-- test sort optimization with ordering by multiple columns and time_bucket as last member, +-- should use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 2,1; +QUERY PLAN + Sort + Sort Key: _hyper_X_X_chunk.value, (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +\set METRICS metrics_int +-- All test against table :METRICS first +\set ON_ERROR_STOP 0 +-- inverse of previous test query to confirm an error is actually thrown +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time = 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; +ERROR: division by zero +-- test window functions with multiple column references +SELECT + time_bucket_gapfill(1,time,1,2), + first(min(time),min(time)) OVER () +FROM :METRICS +GROUP BY 1; +ERROR: window functions with multiple column references not supported +-- test with unsupported operator +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time =0 AND time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test with 2 tables and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test inner join and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time +WHERE m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test outer join with constraints in join condition +-- not usable as start/stop +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 LEFT OUTER JOIN :METRICS m2 ON m1.time=m2.time AND m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +\ir include/gapfill_metrics_query.sql +-- 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. +-- test locf lookup query does not trigger when not needed +-- 1/(SELECT 0) will throw an error in the lookup query but in order to not +-- always trigger evaluation it needs to be correlated otherwise postgres will +-- always run it once even if the value is never used +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | locf3 +------+-----------+-----------+------- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +-- test locf with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value)::int,23) AS locf1, + locf(min(value)::int,(SELECT 42)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and "wrong order" +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value),23::float) AS locf1, + locf(min(value),(SELECT 42::float)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 1,2,3; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and window functions +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)), + sum(locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1))) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3; + time | device_id | sensor_id | locf | sum +------+-----------+-----------+------+------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 5 | 10 + 10 | 1 | 1 | 5 | 10 + 0 | 1 | 2 | -100 | -100 + 5 | 1 | 2 | 10 | -90 + 10 | 1 | 2 | 10 | 20 +(6 rows) + +-- test JOINs +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id, + d.name, + sensor_id, + s.name, + avg(m.value) +FROM :METRICS m +INNER JOIN devices d USING(device_id) +INNER JOIN sensors s USING(sensor_id) +WHERE time BETWEEN 0 AND 5 +GROUP BY 1,2,3,4,5; + time | device_id | name | sensor_id | name | avg +------+-----------+----------+-----------+----------+----- + 0 | 1 | Device 1 | 1 | Sensor 1 | 5 + 1 | 1 | Device 1 | 1 | Sensor 1 | + 2 | 1 | Device 1 | 1 | Sensor 1 | + 3 | 1 | Device 1 | 1 | Sensor 1 | + 4 | 1 | Device 1 | 1 | Sensor 1 | + 0 | 1 | Device 1 | 2 | Sensor 2 | + 1 | 1 | Device 1 | 2 | Sensor 2 | + 2 | 1 | Device 1 | 2 | Sensor 2 | + 3 | 1 | Device 1 | 2 | Sensor 2 | + 4 | 1 | Device 1 | 2 | Sensor 2 | + 5 | 1 | Device 1 | 2 | Sensor 2 | 10 +(11 rows) + +-- test interpolate with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + interpolate(min(value)) AS ip, + interpolate(min(value),(-5,-5.0::float),(15,20.0::float)) AS ip1, + interpolate(min(value),(SELECT (-10,-10.0::float)),(SELECT (15,20.0::float))) AS ip2, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) AS ip3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | ip | ip1 | ip2 | ip3 +------+-----------+-----------+-----+----+-----+------------------+------------------ + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | | 10 | 10 | 4.75 + 10 | 1 | 1 | | | 15 | 15 | 4.5 + 0 | 1 | 2 | | | 2.5 | 3.33333333333333 | 4.76190476190476 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | | 15 | 15 | 4.21052631578947 +(6 rows) + +-- test interpolate with correlated subquery and window function +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ), + sum(interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + )) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | interpolate | sum +------+-----------+-----------+------------------+------------------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 4.75 | 9.75 + 10 | 1 | 1 | 4.5 | 9.25 + 0 | 1 | 2 | 4.76190476190476 | 4.76190476190476 + 5 | 1 | 2 | 10 | 14.7619047619048 + 10 | 1 | 2 | 4.21052631578947 | 14.2105263157895 +(6 rows) + +-- test subqueries +-- subqueries will alter the shape of the plan and top-level constraints +-- might not end up in top-level of jointree +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 +WHERE m1.time >=0 AND m1.time < 2 AND device_id IN (SELECT device_id FROM :METRICS) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test inner join with constraints in join condition +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time AND m2.time >=0 AND m2.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test actual table +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time >=0 AND time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with table alias +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS m +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with 2 tables +SELECT + time_bucket_gapfill(1,m.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test prepared statement with locf with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with interpolate with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with variable gapfill arguments +PREPARE prep_gapfill(int,int,int) AS +SELECT + time_bucket_gapfill($1,time,$2,$3) AS time, + device_id, + sensor_id, + min(value) +FROM :METRICS m1 +WHERE time >= $2 AND time < $3 AND device_id=1 AND sensor_id=1 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +DEALLOCATE prep_gapfill; +-- Tests without tables +-- test locf and interpolate call without gapfill +SELECT locf(1); + locf + 1 +(1 row) + +SELECT interpolate(1); + interpolate + 1 +(1 row) + +-- test locf and interpolate call with NULL input +SELECT locf(NULL::int); + locf + +(1 row) + +SELECT interpolate(NULL::bigint); + interpolate + +(1 row) + +\set ON_ERROR_STOP 0 +-- test time_bucket_gapfill not top level function call +SELECT + 1 + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: no top level time_bucket_gapfill in group by clause +-- test locf with treat_null_as_missing not BOOL +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=1) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function locf(integer, treat_null_as_missing => integer) does not exist +LINE 3: locf(min(time),treat_null_as_missing:=1) + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test locf with treat_null_as_missing not literal +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=random()>0) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid locf argument: treat_null_as_missing must be a BOOL literal +-- test interpolate lookup query with 1 element in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT ROW(10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT ROW(10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with 3 elements in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with mismatching time datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10::float,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10::float,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate lookup query with mismatching value datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10::float))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10::float))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate with unsupported datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(text 'text') +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function interpolate(text) does not exist +LINE 3: interpolate(text 'text') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interval '1d') +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: function interpolate(interval) does not exist +LINE 3: interpolate(interval '1d') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test multiple time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time,1,11),time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time_bucket_gapfill(1,time,1,11),1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested locf calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(locf(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test nested interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test mixed locf/interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test window function inside locf +SELECT + time_bucket_gapfill(1,time,1,11), + locf(avg(min(time)) OVER ()) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window functions must not be below locf +-- test nested window functions +-- prevented by postgres +SELECT + time_bucket_gapfill(1,time,1,11), + avg(avg(min(time)) OVER ()) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window function calls cannot be nested +LINE 3: avg(avg(min(time)) OVER ()) OVER () + ^ +-- test multiple window functions in single column +SELECT + time_bucket_gapfill(1,time,1,11), + avg(min(time)) OVER () + avg(min(time)) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple window function calls per column not supported +-- test locf not toplevel +SELECT + time_bucket_gapfill(1,time,1,11), + 1 + locf(min(time)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: locf must be toplevel function call +-- test locf inside aggregate +SELECT + time_bucket_gapfill(1,time,1,11), + min(min(locf(time))) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: aggregate functions must be below locf +-- test NULL args +SELECT + time_bucket_gapfill(NULL,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill(1,NULL,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill(1,time,NULL,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(1,time,1,NULL) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(NULL,time,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill('1day',NULL,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill('1day',time,NULL,'2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: timezone cannot be NULL +-- test 0 bucket_width +SELECT + time_bucket_gapfill(0,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test negative bucket_width +SELECT + time_bucket_gapfill(-1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test subqueries as interval, start and stop (not supported atm) +SELECT + time_bucket_gapfill((SELECT 1),time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +SELECT + time_bucket_gapfill(1,time,(SELECT 1),11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +SELECT + time_bucket_gapfill(1,time,1,(SELECT 11)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +\set ON_ERROR_STOP 1 +-- test time_bucket_gapfill without aggregation +-- this will not trigger gapfilling +SELECT + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill + 1 + 2 +(2 rows) + +SELECT + time_bucket_gapfill(1,time,1,11), + avg(time) OVER () +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill | avg +---------------------+-------------------- + 1 | 1.5000000000000000 + 2 | 1.5000000000000000 +(2 rows) + +-- test int int2/4/8 +SELECT + time_bucket_gapfill(1::int2,time::int2,0::int2,6::int2) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int4,time::int4,0::int4,6::int4) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int8,time::int8,0::int8,6::int8) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +-- test non-aligned bucket start +SELECT + time_bucket_gapfill(10,time,5,40) +FROM (VALUES (11),(22)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 10 + 20 + 30 +(4 rows) + +-- simple gapfill query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + min(value) AS value +FROM (values (-10,1),(10,2),(11,3),(12,4),(22,5),(30,6),(66,7)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + -10 | 1 + 0 | + 10 | 2 + 20 | 5 + 30 | 6 + 40 | + 60 | 7 +(7 rows) + +-- test references to different columns +SELECT + time_bucket_gapfill(1,t,0,5) as t, + min(t),max(t),min(v),max(v) +FROM(VALUES (1,3),(2,5)) tb(t,v) +GROUP BY 1 ORDER BY 1; + t | min | max | min | max +---+-----+-----+-----+----- + 0 | | | | + 1 | 1 | 1 | 3 | 3 + 2 | 2 | 2 | 5 | 5 + 3 | | | | + 4 | | | | +(5 rows) + +-- test passing of values outside boundaries +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (-1),(1),(3),(6)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + -1 | -1 + 0 | + 1 | 1 + 2 | + 3 | 3 + 4 | + 6 | 6 +(7 rows) + +-- test gap fill before first row and after last row +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | 1 + 2 | 2 + 3 | 3 + 4 | +(5 rows) + +-- test gap fill without rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +WHERE false +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | + 2 | + 3 | + 4 | +(5 rows) + +-- test coalesce +SELECT + time_bucket_gapfill(1,time,0,5), + coalesce(min(time),0), + coalesce(min(value),0), + coalesce(min(value),7) +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | coalesce | coalesce | coalesce +---------------------+----------+----------+---------- + 0 | 0 | 0 | 7 + 1 | 1 | 1 | 1 + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 0 | 0 | 7 +(5 rows) + +-- test case +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + CASE WHEN min(time) IS NOT NULL THEN min(time) ELSE -1 END, + CASE WHEN min(time) IS NOT NULL THEN min(time) + 7 ELSE 0 END, + CASE WHEN 1 = 1 THEN 1 ELSE 0 END +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | case | case | case +---------------------+-----+------+------+------ + 0 | | -1 | 0 | 1 + 1 | 1 | 1 | 8 | 1 + 2 | 2 | 2 | 9 | 1 + 3 | 3 | 3 | 10 | 1 + 4 | | -1 | 0 | 1 +(5 rows) + +-- test constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), min(time), 4 as c +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | min | c +---------------------+-----+-----+--- + 0 | | | 4 + 1 | 1 | 1 | 4 + 2 | 2 | 2 | 4 + 3 | 3 | 3 | 4 + 4 | | | 4 +(5 rows) + +-- test column reordering +SELECT + 1 as c1, '2' as c2, + time_bucket_gapfill(1,time,0,5), + 3.0 as c3, + min(time), min(time), 4 as c4 +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 3 ORDER BY 3; + c1 | c2 | time_bucket_gapfill | c3 | min | min | c4 +----+----+---------------------+-----+-----+-----+---- + 1 | 2 | 0 | 3.0 | | | 4 + 1 | 2 | 1 | 3.0 | 1 | 1 | 4 + 1 | 2 | 2 | 3.0 | 2 | 2 | 4 + 1 | 2 | 3 | 3.0 | 3 | 3 | 4 + 1 | 2 | 4 | 3.0 | | | 4 +(5 rows) + +-- test timestamptz +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMPTZ '2000-01-01',TIMESTAMPTZ '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMPTZ '2000-01-01 9:00:00'),(TIMESTAMPTZ '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +------------------------------+------------------------------ + Fri Dec 31 22:00:00 1999 PST | + Sat Jan 01 04:00:00 2000 PST | Sat Jan 01 09:00:00 2000 PST + Sat Jan 01 10:00:00 2000 PST | + Sat Jan 01 16:00:00 2000 PST | Sat Jan 01 18:00:00 2000 PST + Sat Jan 01 22:00:00 2000 PST | +(5 rows) + +-- test timestamp +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMP '2000-01-01',TIMESTAMP '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMP '2000-01-01 9:00:00'),(TIMESTAMP '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +--------------------------+-------------------------- + Sat Jan 01 00:00:00 2000 | + Sat Jan 01 06:00:00 2000 | Sat Jan 01 09:00:00 2000 + Sat Jan 01 12:00:00 2000 | + Sat Jan 01 18:00:00 2000 | Sat Jan 01 18:00:00 2000 +(4 rows) + +-- test date +SELECT + time_bucket_gapfill(INTERVAL '1w',time,DATE '2000-01-01',DATE '2000-02-10'), + min(time) +FROM (VALUES (DATE '2000-01-08'),(DATE '2000-01-22')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+------------ + 12-27-1999 | + 01-03-2000 | 01-08-2000 + 01-10-2000 | + 01-17-2000 | 01-22-2000 + 01-24-2000 | + 01-31-2000 | + 02-07-2000 | +(7 rows) + +-- test grouping by non-time columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test grouping by non-time columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +WHERE false +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- +(0 rows) + +-- test duplicate columns in GROUP BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,2,3 ORDER BY 2,1; + time | id | id | m +------+----+----+--- + 0 | 1 | 1 | + 1 | 1 | 1 | 1 + 2 | 1 | 1 | + 3 | 1 | 1 | + 4 | 1 | 1 | + 0 | 2 | 2 | + 1 | 2 | 2 | + 2 | 2 | 2 | 2 + 3 | 2 | 2 | + 4 | 2 | 2 | +(10 rows) + +-- test grouping by columns not in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY id,1; + time | m +------+--- + 0 | + 1 | 1 + 2 | + 3 | + 4 | + 0 | + 1 | + 2 | 2 + 3 | + 4 | +(10 rows) + +-- test grouping by non-time columns with text columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 1 | blue | 1 + 2 | blue | + 3 | blue | + 4 | blue | + 0 | red | + 1 | red | + 2 | red | 2 + 3 | red | + 4 | red | +(10 rows) + +-- test grouping by non-time columns with text columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +WHERE false +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- +(0 rows) + +--- test insert into SELECT +CREATE TABLE gapfill_insert_test(id INT); +INSERT INTO gapfill_insert_test SELECT time_bucket_gapfill(1,time,1,5) FROM (VALUES (1),(2)) v(time) GROUP BY 1 ORDER BY 1; +SELECT * FROM gapfill_insert_test; + id + 1 + 2 + 3 + 4 +(4 rows) + +-- test join +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (1,'red',1),(2,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | m | m +------+-------+---+--- + 0 | blue | | + 1 | blue | | + 2 | blue | 2 | + 3 | blue | | + 4 | blue | | 2 + 0 | red | | + 1 | red | 1 | + 2 | red | | + 3 | red | | 1 + 4 | red | | +(10 rows) + +-- test join with locf +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as locf + FROM + (VALUES (0,'red',1),(0,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | locf | m +------+-------+------+--- + 0 | blue | 2 | + 1 | blue | 2 | + 2 | blue | 2 | + 3 | blue | 2 | + 4 | blue | 2 | 2 + 0 | red | 1 | + 1 | red | 1 | + 2 | red | 1 | + 3 | red | 1 | 1 + 4 | red | 1 | 1 +(10 rows) + +-- test locf +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=NULL) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in first row of resultset and treat_null_as_missing with lookup query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false, prev := (SELECT 100)) AS v1, + locf(min(value),treat_null_as_missing:=true, prev := (SELECT 100)) AS v2 +FROM (values (0,NULL),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | v1 | v2 +------+----+----- + 0 | | 100 + 10 | | 100 + 20 | | 100 + 30 | | 100 + 40 | | 100 + 50 | 6 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing with resort +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1 DESC; + time | value +------+------- + 50 | 6 + 40 | 3 + 30 | 3 + 20 | 3 + 10 | 9 + 0 | +(6 rows) + +-- test locf with constants +SELECT + time_bucket_gapfill(1,time,0,5), + 2, + locf(min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | ?column? | locf +---------------------+----------+------ + 0 | 2 | 1 + 1 | 2 | 1 + 2 | 2 | 1 + 3 | 2 | 1 + 4 | 2 | 2 +(5 rows) + +-- test expressions inside locf +SELECT + time_bucket_gapfill(1,time,0,5), + locf(min(value)), + locf(4), + locf(4 + min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | locf | locf | locf +---------------------+------+------+------ + 0 | 1 | 4 | 5 + 1 | 1 | 4 | 5 + 2 | 1 | 4 | 5 + 3 | 1 | 4 | 5 + 4 | 2 | 4 | 6 +(5 rows) + +-- test locf with out of boundary lookup +SELECT + time_bucket_gapfill(10,time,0,70) AS time, + locf(min(value),(SELECT 100)) AS value +FROM (values (20,9),(40,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 100 + 10 | 100 + 20 | 9 + 30 | 9 + 40 | 6 + 50 | 6 + 60 | 6 +(7 rows) + +-- test locf with different datatypes +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1)) AS text, + locf(min(v2)) AS "int[]", + locf(min(v3)) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test locf with different datatypes and treat_null_as_missing +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1),treat_null_as_missing:=true) AS text, + locf(min(v2),treat_null_as_missing:=true) AS "int[]", + locf(min(v3),treat_null_as_missing:=true) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (2,NULL,NULL,NULL), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test interpolate +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(value)) AS value +FROM (values (0,1),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 1 + 10 | 2 + 20 | 3 + 30 | 4 + 40 | 5 + 50 | 6 +(6 rows) + +-- test interpolate with NULL values +SELECT + time_bucket_gapfill(1,time,0,5) AS time, + interpolate(avg(temp)) AS temp +FROM (VALUES (0,0),(2,NULL),(5,5)) v(time,temp) +GROUP BY 1; + time | temp +------+------ + 0 | 0 + 1 | + 2 | + 3 | + 4 | + 5 | 5 +(6 rows) + +-- test interpolate datatypes +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (0,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(50,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + 0 | -3 | -3 | -3 | -3 | -3 + 10 | -2 | -2 | -2 | -1.8 | -1.8 + 20 | -1 | -1 | -1 | -0.6 | -0.6 + 30 | 1 | 1 | 1 | 0.6 | 0.6 + 40 | 2 | 2 | 2 | 1.8 | 1.8 + 50 | 3 | 3 | 3 | 3 | 3 +(6 rows) + +-- test interpolate datatypes with negative time +SELECT + time_bucket_gapfill(10,time,-40,30) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (-40,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(20,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + -40 | -3 | -3 | -3 | -3 | -3 + -30 | -2 | -2 | -2 | -2 | -2 + -20 | -1 | -1 | -1 | -1 | -1 + -10 | 0 | 0 | 0 | 0 | 0 + 0 | 1 | 1 | 1 | 1 | 1 + 10 | 2 | 2 | 2 | 2 | 2 + 20 | 3 | 3 | 3 | 3 | 3 +(7 rows) + +-- test interpolate with multiple groupings +SELECT + time_bucket_gapfill(5,time,0,11), + device, + interpolate(min(v1),(SELECT (-10,-10)),(SELECT (20,10))) +FROM (VALUES (5,1,0),(5,2,0)) as v(time,device,v1) +GROUP BY 1,2 ORDER BY 2,1; + time_bucket_gapfill | device | interpolate +---------------------+--------+------------- + 0 | 1 | -3 + 5 | 1 | 0 + 10 | 1 | 3 + 0 | 2 | -3 + 5 | 2 | 0 + 10 | 2 | 3 +(6 rows) + +-- test cte with gap filling in outer query +WITH data AS ( + SELECT * FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +) +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM data +GROUP BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test cte with gap filling in inner query +WITH gapfill AS ( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m + FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) + GROUP BY 1,id +) +SELECT * FROM gapfill; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test window functions +SELECT + time_bucket_gapfill(10,time,0,60), + interpolate(min(time)), + lag(min(time)) OVER () +FROM (VALUES (0),(50)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | lag +---------------------+-------------+----- + 0 | 0 | + 10 | 10 | 0 + 20 | 20 | + 30 | 30 | + 40 | 40 | + 50 | 50 | +(6 rows) + +-- test window functions with multiple windows +SELECT + time_bucket_gapfill(1,time,0,10), + interpolate(min(time)), + row_number() OVER (), + locf(min(time)), + sum(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 2 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 3 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 4 PRECEDING) +FROM (VALUES (0),(9)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | row_number | locf | sum | sum | sum | sum +---------------------+-------------+------------+------+-----+-----+-----+----- + 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + 1 | 1 | 2 | 0 | 1 | 1 | 1 | 1 + 2 | 2 | 3 | 0 | 3 | 3 | 3 | 3 + 3 | 3 | 4 | 0 | 5 | 6 | 6 | 6 + 4 | 4 | 5 | 0 | 7 | 9 | 10 | 10 + 5 | 5 | 6 | 0 | 9 | 12 | 14 | 15 + 6 | 6 | 7 | 0 | 11 | 15 | 18 | 20 + 7 | 7 | 8 | 0 | 13 | 18 | 22 | 25 + 8 | 8 | 9 | 0 | 15 | 21 | 26 | 30 + 9 | 9 | 10 | 9 | 17 | 24 | 30 | 35 +(10 rows) + +-- test window functions with constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + 4 as c, + lag(min(time)) OVER () +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | c | lag +---------------------+-----+---+----- + 0 | | 4 | + 1 | 1 | 4 | + 2 | 2 | 4 | 1 + 3 | 3 | 4 | 2 + 4 | | 4 | 3 +(5 rows) + +--test window functions with locf +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + locf(min(time)) AS locf, + lag(locf(min(time))) over () AS lag_locf, + lead(locf(min(time))) over () AS lead_locf +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | locf | lag_locf | lead_locf +---------------------+-----+---------+----------+------+----------+----------- + 0 | | | 1 | | | 1 + 1 | 1 | | 2 | 1 | | 2 + 2 | 2 | 1 | | 2 | 1 | 2 + 3 | | 2 | | 2 | 2 | 2 + 4 | | | | 2 | 2 | +(5 rows) + +--test window functions with interpolate +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + lead(interpolate(min(time))) over () AS lead_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | interpolate | lag_interpolate | lead_interpolate +---------------------+-----+---------+----------+-------------+-----------------+------------------ + 0 | | | 1 | | | 1 + 1 | 1 | | | 1 | | 2 + 2 | | 1 | 3 | 2 | 1 | 3 + 3 | 3 | | | 3 | 2 | + 4 | | 3 | | | 3 | +(5 rows) + +--test window functions with expressions +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + 1 + lag(min(time)) over () AS lag_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + 1 + lag(interpolate(min(time))) over () AS lag_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lag_min | interpolate | lag_interpolate | lag_interpolate +---------------------+-----+---------+---------+-------------+-----------------+----------------- + 0 | | | | | | + 1 | 1 | | | 1 | | + 2 | | 1 | 2 | 2 | 1 | 2 + 3 | 3 | | | 3 | 2 | 3 + 4 | | 3 | 4 | | 3 | 4 +(5 rows) + +--test row_number/rank/percent_rank/... window functions with gapfill reference +SELECT + time_bucket_gapfill(1,time,0,5), + ntile(2) OVER () AS ntile_2, + ntile(3) OVER () AS ntile_3, + ntile(5) OVER () AS ntile_5, + row_number() OVER (), + cume_dist() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + rank() OVER (), + rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + percent_rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)) +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | ntile_2 | ntile_3 | ntile_5 | row_number | cume_dist | rank | rank | percent_rank +---------------------+---------+---------+---------+------------+-----------+------+------+-------------- + 0 | 1 | 1 | 1 | 1 | 0.2 | 1 | 1 | 0 + 1 | 1 | 1 | 2 | 2 | 0.4 | 1 | 2 | 0.25 + 2 | 1 | 2 | 3 | 3 | 0.6 | 1 | 3 | 0.5 + 3 | 2 | 2 | 4 | 4 | 0.8 | 1 | 4 | 0.75 + 4 | 2 | 3 | 5 | 5 | 1 | 1 | 5 | 1 +(5 rows) + +-- test first_value/last_value/nth_value +SELECT + time_bucket_gapfill(1,time,0,5), + first_value(min(time)) OVER (), + nth_value(min(time),3) OVER (), + last_value(min(time)) OVER () +FROM (VALUES (0),(2),(5)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | nth_value | last_value +---------------------+-------------+-----------+------------ + 0 | 0 | 2 | 5 + 1 | 0 | 2 | 5 + 2 | 0 | 2 | 5 + 3 | 0 | 2 | 5 + 4 | 0 | 2 | 5 + 5 | 0 | 2 | 5 +(6 rows) + +-- test window functions with PARTITION BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (), + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | row_number | row_number +------+-------+------------+------------ + 0 | blue | 1 | 1 + 1 | blue | 2 | 2 + 2 | blue | 3 | 3 + 3 | blue | 4 | 4 + 4 | blue | 5 | 5 + 0 | red | 6 | 1 + 1 | red | 7 | 2 + 2 | red | 8 | 3 + 3 | red | 9 | 4 + 4 | red | 10 | 5 +(10 rows) + +-- test multiple windows +\set ON_ERROR_STOP 0 +SELECT + time_bucket_gapfill(1,time,0,11), + first_value(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + interpolate(min(time)), + last_value(interpolate(min(time))) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +FROM (VALUES (0),(10)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | interpolate | last_value +---------------------+-------------+-------------+------------ + 0 | 0 | 0 | 1 + 1 | 0 | 1 | 2 + 2 | 1 | 2 | 3 + 3 | 2 | 3 | 4 + 4 | 3 | 4 | 5 + 5 | 4 | 5 | 6 + 6 | 5 | 6 | 7 + 7 | 6 | 7 | 8 + 8 | 7 | 8 | 9 + 9 | 8 | 9 | 10 + 10 | 9 | 10 | 10 +(11 rows) + +-- test reorder +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM + (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 0 | 2 | + 1 | 1 | 1 + 1 | 2 | + 2 | 1 | + 2 | 2 | 2 + 3 | 1 | + 3 | 2 | + 4 | 1 | + 4 | 2 | +(10 rows) + +-- test order by locf +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | locf +---------------------+------ + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 + 1 | +(5 rows) + +-- test order by interpolate +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 4 | + 5 | + 1 | 1 + 2 | 2 + 3 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +-- test queries on hypertable +-- test locf and interpolate together +SELECT + time_bucket_gapfill(interval '1h',time,timestamptz '2018-01-01 05:00:00-8', timestamptz '2018-01-01 07:00:00-8'), + device_id, + locf(avg(v1)) AS locf_v1, + locf(min(v2)) AS locf_v2, + interpolate(avg(v1)) AS interpolate_v1, + interpolate(avg(v2)) AS interpolate_v2 +FROM metrics_tstz +GROUP BY 1,2 +ORDER BY 1,2; + time_bucket_gapfill | device_id | locf_v1 | locf_v2 | interpolate_v1 | interpolate_v2 +------------------------------+-----------+---------+---------+----------------+---------------- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.5 | 10 | 0.25 | 5 + Mon Jan 01 06:00:00 2018 PST | 2 | 0.7 | 20 | 1.05 | 30 + Mon Jan 01 06:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 +(9 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz) < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +-- interpolation with correlated subquery lookup before interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 3:00 PST'::timestamptz, '2018-01-01 8:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + (SELECT (time,0.5::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time DESC LIMIT 1) + ), + avg(v1) +FROM metrics_tstz m1 +WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 03:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 04:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 +(5 rows) + +-- interpolation with correlated subquery lookup after interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 5:00 PST'::timestamptz, '2018-01-01 9:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + next=>(SELECT (time,v2::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time LIMIT 1) + ),avg(v1) +FROM metrics_tstz m1 WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 08:00:00 2018 PST | 1 | -5 | +(4 rows) + +\set ON_ERROR_STOP 0 +-- bucket_width non simple expression +SELECT + time_bucket_gapfill(t,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +-- no start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- no start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,finish:=1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +-- NULL start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +-- no finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with column reference on right side +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE t > t AND t < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with cast +SELECT + time_bucket_gapfill(1,t1::int8) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 >= 1 AND t1 <= 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with multiple column references +SELECT + time_bucket_gapfill(1,t1+t2) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 1 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL start in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END AND t1 < 4 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL finish in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as start argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as finish argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,NULL,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- time_bucket_gapfill with constraints ORed +SELECT + time_bucket_gapfill(1::int8,t::int8) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 OR t < 3 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +-- int32 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- same query with less or equal as finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t <= 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 + 3 +(5 rows) + +-- int32 time_bucket_gapfill with start column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + -1 < t AND t < 3 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 +(3 rows) + +-- int32 time_bucket_gapfill with finish column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= 0 AND 3 >= t +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 +(4 rows) + +-- int16 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int2,t) +FROM (VALUES (1::int2),(2::int2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- int64 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int8,t) +FROM (VALUES (1::int8),(2::int8)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- date time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('1d'::interval,t) +FROM (VALUES ('1999-12-30'::date),('2000-01-01'::date)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + 12-29-1999 + 12-30-1999 + 12-31-1999 + 01-01-2000 + 01-02-2000 +(5 rows) + +-- timestamp time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamp),('2000-01-01'::timestamp)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 00:00:00 1999 + Wed Dec 29 12:00:00 1999 + Thu Dec 30 00:00:00 1999 + Thu Dec 30 12:00:00 1999 + Fri Dec 31 00:00:00 1999 + Fri Dec 31 12:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sat Jan 01 12:00:00 2000 + Sun Jan 02 00:00:00 2000 + Sun Jan 02 12:00:00 2000 +(10 rows) + +-- timestamptz time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Tue Dec 28 16:00:00 1999 PST + Wed Dec 29 04:00:00 1999 PST + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(11 rows) + +-- timestamptz time_bucket_gapfill with more complex expression +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- timestamptz time_bucket_gapfill with different datatype in finish constraint +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03'::date +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- time_bucket_gapfill with now() as start +SELECT + time_bucket_gapfill('1h'::interval,t) +FROM (VALUES (now()),(now())) v(t) +WHERE + t >= now() AND t < now() - '1h'::interval +GROUP BY 1; + time_bucket_gapfill +(0 rows) + +-- time_bucket_gapfill with multiple constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 and t>1 AND t <=4 AND length(version()) > 0 +GROUP BY 1; + time_bucket_gapfill + 2 +(1 row) + +-- int32 time_bucket_gapfill with greater for start +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t > -2 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- test DISTINCT +SELECT DISTINCT ON (color) + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 0 | red | +(2 rows) + +-- test DISTINCT with window functions +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER () +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 6 + 1 | red | 7 + 2 | red | 8 + 3 | red | 9 + 4 | red | 10 +(10 rows) + +-- test DISTINCT with window functions and PARTITION BY +SELECT DISTINCT ON (color,row_number() OVER (PARTITION BY color)) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test DISTINCT with window functions not in targetlist +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test column references +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test with Nested Loop +SELECT l.id, bucket, data_value FROM + (VALUES (1), (2), (3), (4)) a(id) + INNER JOIN LATERAL ( + SELECT b.id id, time_bucket_gapfill('1'::int, time, start=>'1'::int, finish=> '5'::int) bucket, locf(last(data, time)) data_value + FROM (VALUES (1, 1, 1), (1, 4, 4), (2, 1, -1), (2, 4, -4)) b(id, time, data) + WHERE a.id = b.id + GROUP BY b.id, bucket + ) as l on (true); + id | bucket | data_value +----+--------+------------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | 4 | 4 + 2 | 1 | -1 + 2 | 2 | -1 + 2 | 3 | -1 + 2 | 4 | -4 +(8 rows) + +-- test prepared statement +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(value)) +FROM (VALUES (1,1),(2,2)) v(time,value) +GROUP BY 1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +DEALLOCATE prep_gapfill; +-- test column references with TIME_COLUMN last +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test expressions on GROUP BY columns +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + length(color), + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,5; + row_number | locf | color | length | time +------------+------+-------+--------+------ + 1 | | blue | 4 | 0 + 2 | 1 | blue | 4 | 1 + 3 | 1 | blue | 4 | 2 + 4 | 1 | blue | 4 | 3 + 5 | 1 | blue | 4 | 4 + 1 | | red | 3 | 0 + 2 | | red | 3 | 1 + 3 | 2 | red | 3 | 2 + 4 | 2 | red | 3 | 3 + 5 | 2 | red | 3 | 4 +(10 rows) + +-- test columns derived from GROUP BY columns with cast +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | device_id +------+----------- + 0 | 1 + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 1 + 0 | 2 + 1 | 2 + 2 | 2 + 3 | 2 + 4 | 2 +(10 rows) + +-- test columns derived from GROUP BY columns with expression +SELECT + time_bucket_gapfill(1,time,0,5) as time, + 'Device ' || device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | ?column? +------+---------- + 0 | Device 1 + 1 | Device 1 + 2 | Device 1 + 3 | Device 1 + 4 | Device 1 + 0 | Device 2 + 1 | Device 2 + 2 | Device 2 + 3 | Device 2 + 4 | Device 2 +(10 rows) + +--test interpolation with big differences in values (test overflows in calculations) +--we use the biggest possible difference in time(x) and the value(y). +--For bigints we also test values of smaller than bigintmax/min to avoid +--the symmetry where x=y (which catches more errors) +SELECT 9223372036854775807 as big_int_max \gset +SELECT -9223372036854775808 as big_int_min \gset +SELECT + time_bucket_gapfill(1,time,0,1) AS time, + interpolate(min(s)) AS "smallint", + interpolate(min(i)) AS "int", + interpolate(min(b)) AS "bigint", + interpolate(min(b2)) AS "bigint2", + interpolate(min(d)) AS "double" +FROM (values (:big_int_min,(-32768)::smallint,(-2147483648)::int,:big_int_min,-2147483648::bigint, '-Infinity'::double precision), + (:big_int_max, 32767::smallint, 2147483647::int,:big_int_max, 2147483647::bigint, 'Infinity'::double precision)) v(time,s,i,b,b2,d) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | bigint2 | double +----------------------+----------+-------------+----------------------+-------------+----------- + -9223372036854775808 | -32768 | -2147483648 | -9223372036854775808 | -2147483648 | -Infinity + 0 | 0 | 0 | 0 | 0 | Infinity + 9223372036854775807 | 32767 | 2147483647 | 9223372036854775807 | 2147483647 | Infinity +(3 rows) + +-- issue #2232: This query used to trigger error "could not find +-- pathkey item to sort" due to a corrupt query plan +SELECT time_bucket_gapfill('1 h', time) AS time, + locf(sum(v1)) AS v1_sum, + interpolate(sum(v2)) AS v2_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v2_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 06:00:00 2018 PST | 2.1 | 65 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 + Mon Jan 01 04:00:00 2018 PST | | +(4 rows) + +-- query without gapfill: +SELECT time_bucket('1 h', time) AS time, + sum(v1) AS v1_sum, + sum(v2) AS v1_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v1_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 +(2 rows) + +-- query to show original data +SELECT * FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +ORDER BY 1 DESC, 2; + time | device_id | v1 | v2 +------------------------------+-----------+-----+---- + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 +(6 rows) + +-- issue #3048 +-- test gapfill/hashagg planner interaction +-- this used to produce a plan without gapfill node +EXPLAIN (costs off) SELECT time_bucket_gapfill('52w', time, start:='2000-01-01', finish:='2000-01-10') AS time, + sum(v1) AS v1_sum +FROM metrics +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(10 rows) + +-- issue #3834 +-- test projection handling in gapfill +CREATE TABLE i3834(time timestamptz NOT NULL, ship_id int, value float); +SELECT table_name FROM create_hypertable('i3834','time'); + table_name + i3834 +(1 row) + +INSERT INTO i3834 VALUES ('2020-12-01 14:05:00+01',1,3.123), ('2020-12-01 14:05:00+01',2,4.123), ('2020-12-01 14:05:00+01',3,5.123); +SELECT + time_bucket_gapfill('30000 ms'::interval, time) AS time, + ship_id, + interpolate (avg(value)), + 'speedlog' AS source +FROM + i3834 +WHERE + ship_id IN (1, 2) + AND time >= '2020-12-01 14:05:00+01' + AND time < '2020-12-01 14:10:00+01' +GROUP BY 1,2; + time | ship_id | interpolate | source +------------------------------+---------+-------------+---------- + Tue Dec 01 05:05:00 2020 PST | 1 | 3.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:05:00 2020 PST | 2 | 4.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 2 | | speedlog +(20 rows) + +DROP TABLE i3834; +-- issue #1528 +-- test float rounding for certain float values when start and end are identical +SELECT + time_bucket_gapfill('1min'::interval, ts::timestamptz, start:='2019-11-05 2:20', finish:='2019-11-05 2:30'), + interpolate(avg(20266.959547::float4)) AS float4, + interpolate(avg(20266.959547::float8)) AS float8 +FROM (VALUES ('2019-11-05 2:20'), ('2019-11-05 2:30')) v (ts) +GROUP BY 1; + time_bucket_gapfill | float4 | float8 +------------------------------+-----------------+-------------- + Tue Nov 05 02:20:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:21:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:22:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:23:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:24:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:25:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:26:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:27:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:28:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:29:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:30:00 2019 PST | 20266.958984375 | 20266.959547 +(11 rows) + +-- check gapfill group change detection with TOASTed values +CREATE TABLE gapfill_group_toast(time timestamptz NOT NULL, device text, value float); +SELECT table_name FROM create_hypertable('gapfill_group_toast', 'time'); + table_name + gapfill_group_toast +(1 row) + +INSERT INTO gapfill_group_toast +SELECT + generate_series('2022-06-01'::timestamptz, '2022-06-03'::timestamptz, '1min'::interval), + '4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43', + random(); +ALTER TABLE gapfill_group_toast SET(timescaledb.compress, timescaledb.compress_segmentby = 'device'); +SELECT count(compress_chunk(c)) FROM show_chunks('gapfill_group_toast') c; + count + 2 +(1 row) + +SELECT + time_bucket_gapfill('1 day', time), device +FROM gapfill_group_toast +WHERE time >= '2022-06-01' AND time <= '2022-06-02' +GROUP BY 1,2; + time_bucket_gapfill | device +------------------------------+------------------------------------------------------------------ + Tue May 31 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 + Wed Jun 01 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 +(2 rows) + +DROP TABLE gapfill_group_toast; +-- test bucketing by month +SELECT time_bucket_gapfill('2 month'::interval, ts, '2000-01-01'::timestamptz,'2001-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sun Apr 30 17:00:00 2000 PDT + Fri Jun 30 17:00:00 2000 PDT + Thu Aug 31 17:00:00 2000 PDT + Tue Oct 31 16:00:00 2000 PST + Sun Dec 31 16:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('1 year'::interval, ts, '2000-01-01'::timestamptz,'2003-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Sun Dec 31 16:00:00 2000 PST + Mon Dec 31 16:00:00 2001 PST + Tue Dec 31 16:00:00 2002 PST +(4 rows) + +SELECT time_bucket_gapfill('1 century'::interval, ts, '1900-01-01'::timestamptz,'2103-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sun Dec 31 16:00:00 1899 PST + Fri Dec 31 16:00:00 1999 PST + Thu Dec 31 16:00:00 2099 PST +(3 rows) + +-- test bucketing with timezone +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 15:00:00 1999 PST + Tue Feb 29 15:00:00 2000 PST + Sat Apr 29 15:00:00 2000 PDT + Thu Jun 29 15:00:00 2000 PDT + Tue Aug 29 15:00:00 2000 PDT + Sun Oct 29 15:00:00 2000 PST + Fri Dec 29 15:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, current_setting('timezone'), '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 PST + Wed Mar 01 00:00:00 2000 PST + Mon May 01 00:00:00 2000 PDT + Sat Jul 01 00:00:00 2000 PDT + Fri Sep 01 00:00:00 2000 PDT + Wed Nov 01 00:00:00 2000 PST +(6 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, 'UTC', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sat Apr 29 16:00:00 2000 PDT + Thu Jun 29 16:00:00 2000 PDT + Tue Aug 29 16:00:00 2000 PDT + Sun Oct 29 16:00:00 2000 PST + Fri Dec 29 16:00:00 2000 PST +(7 rows) + +SET timezone TO 'Europe/Berlin'; +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 CET + Wed Mar 01 00:00:00 2000 CET + Mon May 01 00:00:00 2000 CEST + Sat Jul 01 00:00:00 2000 CEST + Fri Sep 01 00:00:00 2000 CEST + Wed Nov 01 00:00:00 2000 CET +(6 rows) + +RESET timezone; +DROP INDEX gapfill_plan_test_indx; +-- Test gapfill with arrays (#5981) +SELECT time_bucket_gapfill(5, ts, 1, 100) as ts, int_arr, locf(last(value, ts)) +FROM ( + SELECT ARRAY[1,2,3,4]::int[] as int_arr, x as ts, x+500000 as value + FROM generate_series(1, 10, 100) as x + ) t +GROUP BY 1, 2 + ts | int_arr | locf +----+-----------+-------- + 0 | {1,2,3,4} | 500001 + 5 | {1,2,3,4} | 500001 + 10 | {1,2,3,4} | 500001 + 15 | {1,2,3,4} | 500001 + 20 | {1,2,3,4} | 500001 + 25 | {1,2,3,4} | 500001 + 30 | {1,2,3,4} | 500001 + 35 | {1,2,3,4} | 500001 + 40 | {1,2,3,4} | 500001 + 45 | {1,2,3,4} | 500001 + 50 | {1,2,3,4} | 500001 + 55 | {1,2,3,4} | 500001 + 60 | {1,2,3,4} | 500001 + 65 | {1,2,3,4} | 500001 + 70 | {1,2,3,4} | 500001 + 75 | {1,2,3,4} | 500001 + 80 | {1,2,3,4} | 500001 + 85 | {1,2,3,4} | 500001 + 90 | {1,2,3,4} | 500001 + 95 | {1,2,3,4} | 500001 +(20 rows) + diff --git a/tsl/test/shared/expected/gapfill-15.out b/tsl/test/shared/expected/gapfill-15.out new file mode 100644 index 00000000000..d2e2e4ec598 --- /dev/null +++ b/tsl/test/shared/expected/gapfill-15.out @@ -0,0 +1,3366 @@ +-- 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. +\set EXPLAIN 'EXPLAIN (COSTS OFF)' +-- we want to see error details in the output +\set VERBOSITY default +CREATE TABLE gapfill_plan_test(time timestamptz NOT NULL, value float); +SELECT table_name FROM create_hypertable('gapfill_plan_test','time',chunk_time_interval=>'4 weeks'::interval); + table_name + gapfill_plan_test +(1 row) + +INSERT INTO gapfill_plan_test SELECT generate_series('2018-01-01'::timestamptz,'2018-04-01'::timestamptz,'1m'::interval), 1.0; +-- simple example +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test sorting +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test sort direction +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1 DESC; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) DESC + -> Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) NULLS FIRST + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now()) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test order by aggregate function +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2,1; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)), (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test query without order by +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test parallel query +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + avg(value) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with locf +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with interpolate +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- make sure we can run gapfill in parallel workers +-- ensure this plan runs in parallel +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; +QUERY PLAN + Limit + -> Sort + Sort Key: (interpolate(avg(gapfill_plan_test.value), NULL::record, NULL::record)) + -> Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(18 rows) + +-- actually run a parallel gapfill +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + time_bucket_gapfill | interpolate +------------------------------+------------- + Mon Jan 01 00:00:00 2018 PST | 1 +(1 row) + +-- test sort optimizations +-- test sort optimization with single member order by, +-- should use index scan (no GapFill node for this one since we're not gapfilling) +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +SET max_parallel_workers_per_gather TO 0; +-- test sort optimizations with locf +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +-- test sort optimizations with interpolate +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +RESET max_parallel_workers_per_gather; +CREATE INDEX gapfill_plan_test_indx ON gapfill_plan_test(value, time); +-- test sort optimization with ordering by multiple columns and time_bucket_gapfill not last, +-- must not use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1,2; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)), _hyper_X_X_chunk.value + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +-- test sort optimization with ordering by multiple columns and time_bucket as last member, +-- should use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 2,1; +QUERY PLAN + Sort + Sort Key: _hyper_X_X_chunk.value, (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +\set METRICS metrics_int +-- All test against table :METRICS first +\set ON_ERROR_STOP 0 +-- inverse of previous test query to confirm an error is actually thrown +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time = 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; +ERROR: division by zero +-- test window functions with multiple column references +SELECT + time_bucket_gapfill(1,time,1,2), + first(min(time),min(time)) OVER () +FROM :METRICS +GROUP BY 1; +ERROR: window functions with multiple column references not supported +-- test with unsupported operator +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time =0 AND time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test with 2 tables and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test inner join and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time +WHERE m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test outer join with constraints in join condition +-- not usable as start/stop +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 LEFT OUTER JOIN :METRICS m2 ON m1.time=m2.time AND m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +\ir include/gapfill_metrics_query.sql +-- 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. +-- test locf lookup query does not trigger when not needed +-- 1/(SELECT 0) will throw an error in the lookup query but in order to not +-- always trigger evaluation it needs to be correlated otherwise postgres will +-- always run it once even if the value is never used +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | locf3 +------+-----------+-----------+------- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +-- test locf with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value)::int,23) AS locf1, + locf(min(value)::int,(SELECT 42)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and "wrong order" +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value),23::float) AS locf1, + locf(min(value),(SELECT 42::float)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 1,2,3; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and window functions +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)), + sum(locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1))) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3; + time | device_id | sensor_id | locf | sum +------+-----------+-----------+------+------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 5 | 10 + 10 | 1 | 1 | 5 | 10 + 0 | 1 | 2 | -100 | -100 + 5 | 1 | 2 | 10 | -90 + 10 | 1 | 2 | 10 | 20 +(6 rows) + +-- test JOINs +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id, + d.name, + sensor_id, + s.name, + avg(m.value) +FROM :METRICS m +INNER JOIN devices d USING(device_id) +INNER JOIN sensors s USING(sensor_id) +WHERE time BETWEEN 0 AND 5 +GROUP BY 1,2,3,4,5; + time | device_id | name | sensor_id | name | avg +------+-----------+----------+-----------+----------+----- + 0 | 1 | Device 1 | 1 | Sensor 1 | 5 + 1 | 1 | Device 1 | 1 | Sensor 1 | + 2 | 1 | Device 1 | 1 | Sensor 1 | + 3 | 1 | Device 1 | 1 | Sensor 1 | + 4 | 1 | Device 1 | 1 | Sensor 1 | + 0 | 1 | Device 1 | 2 | Sensor 2 | + 1 | 1 | Device 1 | 2 | Sensor 2 | + 2 | 1 | Device 1 | 2 | Sensor 2 | + 3 | 1 | Device 1 | 2 | Sensor 2 | + 4 | 1 | Device 1 | 2 | Sensor 2 | + 5 | 1 | Device 1 | 2 | Sensor 2 | 10 +(11 rows) + +-- test interpolate with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + interpolate(min(value)) AS ip, + interpolate(min(value),(-5,-5.0::float),(15,20.0::float)) AS ip1, + interpolate(min(value),(SELECT (-10,-10.0::float)),(SELECT (15,20.0::float))) AS ip2, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) AS ip3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | ip | ip1 | ip2 | ip3 +------+-----------+-----------+-----+----+-----+------------------+------------------ + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | | 10 | 10 | 4.75 + 10 | 1 | 1 | | | 15 | 15 | 4.5 + 0 | 1 | 2 | | | 2.5 | 3.33333333333333 | 4.76190476190476 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | | 15 | 15 | 4.21052631578947 +(6 rows) + +-- test interpolate with correlated subquery and window function +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ), + sum(interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + )) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | interpolate | sum +------+-----------+-----------+------------------+------------------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 4.75 | 9.75 + 10 | 1 | 1 | 4.5 | 9.25 + 0 | 1 | 2 | 4.76190476190476 | 4.76190476190476 + 5 | 1 | 2 | 10 | 14.7619047619048 + 10 | 1 | 2 | 4.21052631578947 | 14.2105263157895 +(6 rows) + +-- test subqueries +-- subqueries will alter the shape of the plan and top-level constraints +-- might not end up in top-level of jointree +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 +WHERE m1.time >=0 AND m1.time < 2 AND device_id IN (SELECT device_id FROM :METRICS) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test inner join with constraints in join condition +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time AND m2.time >=0 AND m2.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test actual table +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time >=0 AND time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with table alias +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS m +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with 2 tables +SELECT + time_bucket_gapfill(1,m.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test prepared statement with locf with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with interpolate with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with variable gapfill arguments +PREPARE prep_gapfill(int,int,int) AS +SELECT + time_bucket_gapfill($1,time,$2,$3) AS time, + device_id, + sensor_id, + min(value) +FROM :METRICS m1 +WHERE time >= $2 AND time < $3 AND device_id=1 AND sensor_id=1 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +DEALLOCATE prep_gapfill; +-- Tests without tables +-- test locf and interpolate call without gapfill +SELECT locf(1); + locf + 1 +(1 row) + +SELECT interpolate(1); + interpolate + 1 +(1 row) + +-- test locf and interpolate call with NULL input +SELECT locf(NULL::int); + locf + +(1 row) + +SELECT interpolate(NULL::bigint); + interpolate + +(1 row) + +\set ON_ERROR_STOP 0 +-- test time_bucket_gapfill not top level function call +SELECT + 1 + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: no top level time_bucket_gapfill in group by clause +-- test locf with treat_null_as_missing not BOOL +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=1) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function locf(integer, treat_null_as_missing => integer) does not exist +LINE 3: locf(min(time),treat_null_as_missing:=1) + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test locf with treat_null_as_missing not literal +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=random()>0) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid locf argument: treat_null_as_missing must be a BOOL literal +-- test interpolate lookup query with 1 element in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT ROW(10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT ROW(10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with 3 elements in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with mismatching time datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10::float,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10::float,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate lookup query with mismatching value datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10::float))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10::float))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate with unsupported datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(text 'text') +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function interpolate(text) does not exist +LINE 3: interpolate(text 'text') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interval '1d') +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: function interpolate(interval) does not exist +LINE 3: interpolate(interval '1d') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test multiple time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time,1,11),time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time_bucket_gapfill(1,time,1,11),1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested locf calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(locf(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test nested interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test mixed locf/interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test window function inside locf +SELECT + time_bucket_gapfill(1,time,1,11), + locf(avg(min(time)) OVER ()) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window functions must not be below locf +-- test nested window functions +-- prevented by postgres +SELECT + time_bucket_gapfill(1,time,1,11), + avg(avg(min(time)) OVER ()) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window function calls cannot be nested +LINE 3: avg(avg(min(time)) OVER ()) OVER () + ^ +-- test multiple window functions in single column +SELECT + time_bucket_gapfill(1,time,1,11), + avg(min(time)) OVER () + avg(min(time)) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple window function calls per column not supported +-- test locf not toplevel +SELECT + time_bucket_gapfill(1,time,1,11), + 1 + locf(min(time)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: locf must be toplevel function call +-- test locf inside aggregate +SELECT + time_bucket_gapfill(1,time,1,11), + min(min(locf(time))) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: aggregate functions must be below locf +-- test NULL args +SELECT + time_bucket_gapfill(NULL,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill(1,NULL,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill(1,time,NULL,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(1,time,1,NULL) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(NULL,time,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill('1day',NULL,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill('1day',time,NULL,'2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: timezone cannot be NULL +-- test 0 bucket_width +SELECT + time_bucket_gapfill(0,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test negative bucket_width +SELECT + time_bucket_gapfill(-1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test subqueries as interval, start and stop (not supported atm) +SELECT + time_bucket_gapfill((SELECT 1),time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +SELECT + time_bucket_gapfill(1,time,(SELECT 1),11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +SELECT + time_bucket_gapfill(1,time,1,(SELECT 11)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +\set ON_ERROR_STOP 1 +-- test time_bucket_gapfill without aggregation +-- this will not trigger gapfilling +SELECT + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill + 1 + 2 +(2 rows) + +SELECT + time_bucket_gapfill(1,time,1,11), + avg(time) OVER () +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill | avg +---------------------+-------------------- + 1 | 1.5000000000000000 + 2 | 1.5000000000000000 +(2 rows) + +-- test int int2/4/8 +SELECT + time_bucket_gapfill(1::int2,time::int2,0::int2,6::int2) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int4,time::int4,0::int4,6::int4) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int8,time::int8,0::int8,6::int8) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +-- test non-aligned bucket start +SELECT + time_bucket_gapfill(10,time,5,40) +FROM (VALUES (11),(22)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 10 + 20 + 30 +(4 rows) + +-- simple gapfill query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + min(value) AS value +FROM (values (-10,1),(10,2),(11,3),(12,4),(22,5),(30,6),(66,7)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + -10 | 1 + 0 | + 10 | 2 + 20 | 5 + 30 | 6 + 40 | + 60 | 7 +(7 rows) + +-- test references to different columns +SELECT + time_bucket_gapfill(1,t,0,5) as t, + min(t),max(t),min(v),max(v) +FROM(VALUES (1,3),(2,5)) tb(t,v) +GROUP BY 1 ORDER BY 1; + t | min | max | min | max +---+-----+-----+-----+----- + 0 | | | | + 1 | 1 | 1 | 3 | 3 + 2 | 2 | 2 | 5 | 5 + 3 | | | | + 4 | | | | +(5 rows) + +-- test passing of values outside boundaries +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (-1),(1),(3),(6)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + -1 | -1 + 0 | + 1 | 1 + 2 | + 3 | 3 + 4 | + 6 | 6 +(7 rows) + +-- test gap fill before first row and after last row +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | 1 + 2 | 2 + 3 | 3 + 4 | +(5 rows) + +-- test gap fill without rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +WHERE false +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | + 2 | + 3 | + 4 | +(5 rows) + +-- test coalesce +SELECT + time_bucket_gapfill(1,time,0,5), + coalesce(min(time),0), + coalesce(min(value),0), + coalesce(min(value),7) +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | coalesce | coalesce | coalesce +---------------------+----------+----------+---------- + 0 | 0 | 0 | 7 + 1 | 1 | 1 | 1 + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 0 | 0 | 7 +(5 rows) + +-- test case +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + CASE WHEN min(time) IS NOT NULL THEN min(time) ELSE -1 END, + CASE WHEN min(time) IS NOT NULL THEN min(time) + 7 ELSE 0 END, + CASE WHEN 1 = 1 THEN 1 ELSE 0 END +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | case | case | case +---------------------+-----+------+------+------ + 0 | | -1 | 0 | 1 + 1 | 1 | 1 | 8 | 1 + 2 | 2 | 2 | 9 | 1 + 3 | 3 | 3 | 10 | 1 + 4 | | -1 | 0 | 1 +(5 rows) + +-- test constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), min(time), 4 as c +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | min | c +---------------------+-----+-----+--- + 0 | | | 4 + 1 | 1 | 1 | 4 + 2 | 2 | 2 | 4 + 3 | 3 | 3 | 4 + 4 | | | 4 +(5 rows) + +-- test column reordering +SELECT + 1 as c1, '2' as c2, + time_bucket_gapfill(1,time,0,5), + 3.0 as c3, + min(time), min(time), 4 as c4 +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 3 ORDER BY 3; + c1 | c2 | time_bucket_gapfill | c3 | min | min | c4 +----+----+---------------------+-----+-----+-----+---- + 1 | 2 | 0 | 3.0 | | | 4 + 1 | 2 | 1 | 3.0 | 1 | 1 | 4 + 1 | 2 | 2 | 3.0 | 2 | 2 | 4 + 1 | 2 | 3 | 3.0 | 3 | 3 | 4 + 1 | 2 | 4 | 3.0 | | | 4 +(5 rows) + +-- test timestamptz +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMPTZ '2000-01-01',TIMESTAMPTZ '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMPTZ '2000-01-01 9:00:00'),(TIMESTAMPTZ '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +------------------------------+------------------------------ + Fri Dec 31 22:00:00 1999 PST | + Sat Jan 01 04:00:00 2000 PST | Sat Jan 01 09:00:00 2000 PST + Sat Jan 01 10:00:00 2000 PST | + Sat Jan 01 16:00:00 2000 PST | Sat Jan 01 18:00:00 2000 PST + Sat Jan 01 22:00:00 2000 PST | +(5 rows) + +-- test timestamp +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMP '2000-01-01',TIMESTAMP '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMP '2000-01-01 9:00:00'),(TIMESTAMP '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +--------------------------+-------------------------- + Sat Jan 01 00:00:00 2000 | + Sat Jan 01 06:00:00 2000 | Sat Jan 01 09:00:00 2000 + Sat Jan 01 12:00:00 2000 | + Sat Jan 01 18:00:00 2000 | Sat Jan 01 18:00:00 2000 +(4 rows) + +-- test date +SELECT + time_bucket_gapfill(INTERVAL '1w',time,DATE '2000-01-01',DATE '2000-02-10'), + min(time) +FROM (VALUES (DATE '2000-01-08'),(DATE '2000-01-22')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+------------ + 12-27-1999 | + 01-03-2000 | 01-08-2000 + 01-10-2000 | + 01-17-2000 | 01-22-2000 + 01-24-2000 | + 01-31-2000 | + 02-07-2000 | +(7 rows) + +-- test grouping by non-time columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test grouping by non-time columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +WHERE false +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- +(0 rows) + +-- test duplicate columns in GROUP BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,2,3 ORDER BY 2,1; + time | id | id | m +------+----+----+--- + 0 | 1 | 1 | + 1 | 1 | 1 | 1 + 2 | 1 | 1 | + 3 | 1 | 1 | + 4 | 1 | 1 | + 0 | 2 | 2 | + 1 | 2 | 2 | + 2 | 2 | 2 | 2 + 3 | 2 | 2 | + 4 | 2 | 2 | +(10 rows) + +-- test grouping by columns not in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY id,1; + time | m +------+--- + 0 | + 1 | 1 + 2 | + 3 | + 4 | + 0 | + 1 | + 2 | 2 + 3 | + 4 | +(10 rows) + +-- test grouping by non-time columns with text columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 1 | blue | 1 + 2 | blue | + 3 | blue | + 4 | blue | + 0 | red | + 1 | red | + 2 | red | 2 + 3 | red | + 4 | red | +(10 rows) + +-- test grouping by non-time columns with text columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +WHERE false +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- +(0 rows) + +--- test insert into SELECT +CREATE TABLE gapfill_insert_test(id INT); +INSERT INTO gapfill_insert_test SELECT time_bucket_gapfill(1,time,1,5) FROM (VALUES (1),(2)) v(time) GROUP BY 1 ORDER BY 1; +SELECT * FROM gapfill_insert_test; + id + 1 + 2 + 3 + 4 +(4 rows) + +-- test join +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (1,'red',1),(2,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | m | m +------+-------+---+--- + 0 | blue | | + 1 | blue | | + 2 | blue | 2 | + 3 | blue | | + 4 | blue | | 2 + 0 | red | | + 1 | red | 1 | + 2 | red | | + 3 | red | | 1 + 4 | red | | +(10 rows) + +-- test join with locf +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as locf + FROM + (VALUES (0,'red',1),(0,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | locf | m +------+-------+------+--- + 0 | blue | 2 | + 1 | blue | 2 | + 2 | blue | 2 | + 3 | blue | 2 | + 4 | blue | 2 | 2 + 0 | red | 1 | + 1 | red | 1 | + 2 | red | 1 | + 3 | red | 1 | 1 + 4 | red | 1 | 1 +(10 rows) + +-- test locf +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=NULL) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in first row of resultset and treat_null_as_missing with lookup query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false, prev := (SELECT 100)) AS v1, + locf(min(value),treat_null_as_missing:=true, prev := (SELECT 100)) AS v2 +FROM (values (0,NULL),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | v1 | v2 +------+----+----- + 0 | | 100 + 10 | | 100 + 20 | | 100 + 30 | | 100 + 40 | | 100 + 50 | 6 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing with resort +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1 DESC; + time | value +------+------- + 50 | 6 + 40 | 3 + 30 | 3 + 20 | 3 + 10 | 9 + 0 | +(6 rows) + +-- test locf with constants +SELECT + time_bucket_gapfill(1,time,0,5), + 2, + locf(min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | ?column? | locf +---------------------+----------+------ + 0 | 2 | 1 + 1 | 2 | 1 + 2 | 2 | 1 + 3 | 2 | 1 + 4 | 2 | 2 +(5 rows) + +-- test expressions inside locf +SELECT + time_bucket_gapfill(1,time,0,5), + locf(min(value)), + locf(4), + locf(4 + min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | locf | locf | locf +---------------------+------+------+------ + 0 | 1 | 4 | 5 + 1 | 1 | 4 | 5 + 2 | 1 | 4 | 5 + 3 | 1 | 4 | 5 + 4 | 2 | 4 | 6 +(5 rows) + +-- test locf with out of boundary lookup +SELECT + time_bucket_gapfill(10,time,0,70) AS time, + locf(min(value),(SELECT 100)) AS value +FROM (values (20,9),(40,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 100 + 10 | 100 + 20 | 9 + 30 | 9 + 40 | 6 + 50 | 6 + 60 | 6 +(7 rows) + +-- test locf with different datatypes +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1)) AS text, + locf(min(v2)) AS "int[]", + locf(min(v3)) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test locf with different datatypes and treat_null_as_missing +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1),treat_null_as_missing:=true) AS text, + locf(min(v2),treat_null_as_missing:=true) AS "int[]", + locf(min(v3),treat_null_as_missing:=true) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (2,NULL,NULL,NULL), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test interpolate +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(value)) AS value +FROM (values (0,1),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 1 + 10 | 2 + 20 | 3 + 30 | 4 + 40 | 5 + 50 | 6 +(6 rows) + +-- test interpolate with NULL values +SELECT + time_bucket_gapfill(1,time,0,5) AS time, + interpolate(avg(temp)) AS temp +FROM (VALUES (0,0),(2,NULL),(5,5)) v(time,temp) +GROUP BY 1; + time | temp +------+------ + 0 | 0 + 1 | + 2 | + 3 | + 4 | + 5 | 5 +(6 rows) + +-- test interpolate datatypes +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (0,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(50,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + 0 | -3 | -3 | -3 | -3 | -3 + 10 | -2 | -2 | -2 | -1.8 | -1.8 + 20 | -1 | -1 | -1 | -0.6 | -0.6 + 30 | 1 | 1 | 1 | 0.6 | 0.6 + 40 | 2 | 2 | 2 | 1.8 | 1.8 + 50 | 3 | 3 | 3 | 3 | 3 +(6 rows) + +-- test interpolate datatypes with negative time +SELECT + time_bucket_gapfill(10,time,-40,30) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (-40,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(20,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + -40 | -3 | -3 | -3 | -3 | -3 + -30 | -2 | -2 | -2 | -2 | -2 + -20 | -1 | -1 | -1 | -1 | -1 + -10 | 0 | 0 | 0 | 0 | 0 + 0 | 1 | 1 | 1 | 1 | 1 + 10 | 2 | 2 | 2 | 2 | 2 + 20 | 3 | 3 | 3 | 3 | 3 +(7 rows) + +-- test interpolate with multiple groupings +SELECT + time_bucket_gapfill(5,time,0,11), + device, + interpolate(min(v1),(SELECT (-10,-10)),(SELECT (20,10))) +FROM (VALUES (5,1,0),(5,2,0)) as v(time,device,v1) +GROUP BY 1,2 ORDER BY 2,1; + time_bucket_gapfill | device | interpolate +---------------------+--------+------------- + 0 | 1 | -3 + 5 | 1 | 0 + 10 | 1 | 3 + 0 | 2 | -3 + 5 | 2 | 0 + 10 | 2 | 3 +(6 rows) + +-- test cte with gap filling in outer query +WITH data AS ( + SELECT * FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +) +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM data +GROUP BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test cte with gap filling in inner query +WITH gapfill AS ( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m + FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) + GROUP BY 1,id +) +SELECT * FROM gapfill; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test window functions +SELECT + time_bucket_gapfill(10,time,0,60), + interpolate(min(time)), + lag(min(time)) OVER () +FROM (VALUES (0),(50)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | lag +---------------------+-------------+----- + 0 | 0 | + 10 | 10 | 0 + 20 | 20 | + 30 | 30 | + 40 | 40 | + 50 | 50 | +(6 rows) + +-- test window functions with multiple windows +SELECT + time_bucket_gapfill(1,time,0,10), + interpolate(min(time)), + row_number() OVER (), + locf(min(time)), + sum(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 2 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 3 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 4 PRECEDING) +FROM (VALUES (0),(9)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | row_number | locf | sum | sum | sum | sum +---------------------+-------------+------------+------+-----+-----+-----+----- + 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + 1 | 1 | 2 | 0 | 1 | 1 | 1 | 1 + 2 | 2 | 3 | 0 | 3 | 3 | 3 | 3 + 3 | 3 | 4 | 0 | 5 | 6 | 6 | 6 + 4 | 4 | 5 | 0 | 7 | 9 | 10 | 10 + 5 | 5 | 6 | 0 | 9 | 12 | 14 | 15 + 6 | 6 | 7 | 0 | 11 | 15 | 18 | 20 + 7 | 7 | 8 | 0 | 13 | 18 | 22 | 25 + 8 | 8 | 9 | 0 | 15 | 21 | 26 | 30 + 9 | 9 | 10 | 9 | 17 | 24 | 30 | 35 +(10 rows) + +-- test window functions with constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + 4 as c, + lag(min(time)) OVER () +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | c | lag +---------------------+-----+---+----- + 0 | | 4 | + 1 | 1 | 4 | + 2 | 2 | 4 | 1 + 3 | 3 | 4 | 2 + 4 | | 4 | 3 +(5 rows) + +--test window functions with locf +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + locf(min(time)) AS locf, + lag(locf(min(time))) over () AS lag_locf, + lead(locf(min(time))) over () AS lead_locf +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | locf | lag_locf | lead_locf +---------------------+-----+---------+----------+------+----------+----------- + 0 | | | 1 | | | 1 + 1 | 1 | | 2 | 1 | | 2 + 2 | 2 | 1 | | 2 | 1 | 2 + 3 | | 2 | | 2 | 2 | 2 + 4 | | | | 2 | 2 | +(5 rows) + +--test window functions with interpolate +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + lead(interpolate(min(time))) over () AS lead_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | interpolate | lag_interpolate | lead_interpolate +---------------------+-----+---------+----------+-------------+-----------------+------------------ + 0 | | | 1 | | | 1 + 1 | 1 | | | 1 | | 2 + 2 | | 1 | 3 | 2 | 1 | 3 + 3 | 3 | | | 3 | 2 | + 4 | | 3 | | | 3 | +(5 rows) + +--test window functions with expressions +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + 1 + lag(min(time)) over () AS lag_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + 1 + lag(interpolate(min(time))) over () AS lag_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lag_min | interpolate | lag_interpolate | lag_interpolate +---------------------+-----+---------+---------+-------------+-----------------+----------------- + 0 | | | | | | + 1 | 1 | | | 1 | | + 2 | | 1 | 2 | 2 | 1 | 2 + 3 | 3 | | | 3 | 2 | 3 + 4 | | 3 | 4 | | 3 | 4 +(5 rows) + +--test row_number/rank/percent_rank/... window functions with gapfill reference +SELECT + time_bucket_gapfill(1,time,0,5), + ntile(2) OVER () AS ntile_2, + ntile(3) OVER () AS ntile_3, + ntile(5) OVER () AS ntile_5, + row_number() OVER (), + cume_dist() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + rank() OVER (), + rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + percent_rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)) +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | ntile_2 | ntile_3 | ntile_5 | row_number | cume_dist | rank | rank | percent_rank +---------------------+---------+---------+---------+------------+-----------+------+------+-------------- + 0 | 1 | 1 | 1 | 1 | 0.2 | 1 | 1 | 0 + 1 | 1 | 1 | 2 | 2 | 0.4 | 1 | 2 | 0.25 + 2 | 1 | 2 | 3 | 3 | 0.6 | 1 | 3 | 0.5 + 3 | 2 | 2 | 4 | 4 | 0.8 | 1 | 4 | 0.75 + 4 | 2 | 3 | 5 | 5 | 1 | 1 | 5 | 1 +(5 rows) + +-- test first_value/last_value/nth_value +SELECT + time_bucket_gapfill(1,time,0,5), + first_value(min(time)) OVER (), + nth_value(min(time),3) OVER (), + last_value(min(time)) OVER () +FROM (VALUES (0),(2),(5)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | nth_value | last_value +---------------------+-------------+-----------+------------ + 0 | 0 | 2 | 5 + 1 | 0 | 2 | 5 + 2 | 0 | 2 | 5 + 3 | 0 | 2 | 5 + 4 | 0 | 2 | 5 + 5 | 0 | 2 | 5 +(6 rows) + +-- test window functions with PARTITION BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (), + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | row_number | row_number +------+-------+------------+------------ + 0 | blue | 1 | 1 + 1 | blue | 2 | 2 + 2 | blue | 3 | 3 + 3 | blue | 4 | 4 + 4 | blue | 5 | 5 + 0 | red | 6 | 1 + 1 | red | 7 | 2 + 2 | red | 8 | 3 + 3 | red | 9 | 4 + 4 | red | 10 | 5 +(10 rows) + +-- test multiple windows +\set ON_ERROR_STOP 0 +SELECT + time_bucket_gapfill(1,time,0,11), + first_value(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + interpolate(min(time)), + last_value(interpolate(min(time))) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +FROM (VALUES (0),(10)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | interpolate | last_value +---------------------+-------------+-------------+------------ + 0 | 0 | 0 | 1 + 1 | 0 | 1 | 2 + 2 | 1 | 2 | 3 + 3 | 2 | 3 | 4 + 4 | 3 | 4 | 5 + 5 | 4 | 5 | 6 + 6 | 5 | 6 | 7 + 7 | 6 | 7 | 8 + 8 | 7 | 8 | 9 + 9 | 8 | 9 | 10 + 10 | 9 | 10 | 10 +(11 rows) + +-- test reorder +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM + (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 0 | 2 | + 1 | 1 | 1 + 1 | 2 | + 2 | 1 | + 2 | 2 | 2 + 3 | 1 | + 3 | 2 | + 4 | 1 | + 4 | 2 | +(10 rows) + +-- test order by locf +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | locf +---------------------+------ + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 + 1 | +(5 rows) + +-- test order by interpolate +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 4 | + 5 | + 1 | 1 + 2 | 2 + 3 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +-- test queries on hypertable +-- test locf and interpolate together +SELECT + time_bucket_gapfill(interval '1h',time,timestamptz '2018-01-01 05:00:00-8', timestamptz '2018-01-01 07:00:00-8'), + device_id, + locf(avg(v1)) AS locf_v1, + locf(min(v2)) AS locf_v2, + interpolate(avg(v1)) AS interpolate_v1, + interpolate(avg(v2)) AS interpolate_v2 +FROM metrics_tstz +GROUP BY 1,2 +ORDER BY 1,2; + time_bucket_gapfill | device_id | locf_v1 | locf_v2 | interpolate_v1 | interpolate_v2 +------------------------------+-----------+---------+---------+----------------+---------------- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.5 | 10 | 0.25 | 5 + Mon Jan 01 06:00:00 2018 PST | 2 | 0.7 | 20 | 1.05 | 30 + Mon Jan 01 06:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 +(9 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz) < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +-- interpolation with correlated subquery lookup before interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 3:00 PST'::timestamptz, '2018-01-01 8:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + (SELECT (time,0.5::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time DESC LIMIT 1) + ), + avg(v1) +FROM metrics_tstz m1 +WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 03:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 04:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 +(5 rows) + +-- interpolation with correlated subquery lookup after interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 5:00 PST'::timestamptz, '2018-01-01 9:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + next=>(SELECT (time,v2::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time LIMIT 1) + ),avg(v1) +FROM metrics_tstz m1 WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 08:00:00 2018 PST | 1 | -5 | +(4 rows) + +\set ON_ERROR_STOP 0 +-- bucket_width non simple expression +SELECT + time_bucket_gapfill(t,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +-- no start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- no start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,finish:=1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +-- NULL start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +-- no finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with column reference on right side +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE t > t AND t < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with cast +SELECT + time_bucket_gapfill(1,t1::int8) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 >= 1 AND t1 <= 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with multiple column references +SELECT + time_bucket_gapfill(1,t1+t2) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 1 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL start in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END AND t1 < 4 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL finish in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as start argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as finish argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,NULL,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- time_bucket_gapfill with constraints ORed +SELECT + time_bucket_gapfill(1::int8,t::int8) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 OR t < 3 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +-- int32 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- same query with less or equal as finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t <= 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 + 3 +(5 rows) + +-- int32 time_bucket_gapfill with start column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + -1 < t AND t < 3 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 +(3 rows) + +-- int32 time_bucket_gapfill with finish column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= 0 AND 3 >= t +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 +(4 rows) + +-- int16 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int2,t) +FROM (VALUES (1::int2),(2::int2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- int64 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int8,t) +FROM (VALUES (1::int8),(2::int8)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- date time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('1d'::interval,t) +FROM (VALUES ('1999-12-30'::date),('2000-01-01'::date)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + 12-29-1999 + 12-30-1999 + 12-31-1999 + 01-01-2000 + 01-02-2000 +(5 rows) + +-- timestamp time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamp),('2000-01-01'::timestamp)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 00:00:00 1999 + Wed Dec 29 12:00:00 1999 + Thu Dec 30 00:00:00 1999 + Thu Dec 30 12:00:00 1999 + Fri Dec 31 00:00:00 1999 + Fri Dec 31 12:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sat Jan 01 12:00:00 2000 + Sun Jan 02 00:00:00 2000 + Sun Jan 02 12:00:00 2000 +(10 rows) + +-- timestamptz time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Tue Dec 28 16:00:00 1999 PST + Wed Dec 29 04:00:00 1999 PST + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(11 rows) + +-- timestamptz time_bucket_gapfill with more complex expression +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- timestamptz time_bucket_gapfill with different datatype in finish constraint +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03'::date +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- time_bucket_gapfill with now() as start +SELECT + time_bucket_gapfill('1h'::interval,t) +FROM (VALUES (now()),(now())) v(t) +WHERE + t >= now() AND t < now() - '1h'::interval +GROUP BY 1; + time_bucket_gapfill +(0 rows) + +-- time_bucket_gapfill with multiple constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 and t>1 AND t <=4 AND length(version()) > 0 +GROUP BY 1; + time_bucket_gapfill + 2 +(1 row) + +-- int32 time_bucket_gapfill with greater for start +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t > -2 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- test DISTINCT +SELECT DISTINCT ON (color) + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 0 | red | +(2 rows) + +-- test DISTINCT with window functions +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER () +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 6 + 1 | red | 7 + 2 | red | 8 + 3 | red | 9 + 4 | red | 10 +(10 rows) + +-- test DISTINCT with window functions and PARTITION BY +SELECT DISTINCT ON (color,row_number() OVER (PARTITION BY color)) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test DISTINCT with window functions not in targetlist +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test column references +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test with Nested Loop +SELECT l.id, bucket, data_value FROM + (VALUES (1), (2), (3), (4)) a(id) + INNER JOIN LATERAL ( + SELECT b.id id, time_bucket_gapfill('1'::int, time, start=>'1'::int, finish=> '5'::int) bucket, locf(last(data, time)) data_value + FROM (VALUES (1, 1, 1), (1, 4, 4), (2, 1, -1), (2, 4, -4)) b(id, time, data) + WHERE a.id = b.id + GROUP BY b.id, bucket + ) as l on (true); + id | bucket | data_value +----+--------+------------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | 4 | 4 + 2 | 1 | -1 + 2 | 2 | -1 + 2 | 3 | -1 + 2 | 4 | -4 +(8 rows) + +-- test prepared statement +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(value)) +FROM (VALUES (1,1),(2,2)) v(time,value) +GROUP BY 1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +DEALLOCATE prep_gapfill; +-- test column references with TIME_COLUMN last +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test expressions on GROUP BY columns +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + length(color), + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,5; + row_number | locf | color | length | time +------------+------+-------+--------+------ + 1 | | blue | 4 | 0 + 2 | 1 | blue | 4 | 1 + 3 | 1 | blue | 4 | 2 + 4 | 1 | blue | 4 | 3 + 5 | 1 | blue | 4 | 4 + 1 | | red | 3 | 0 + 2 | | red | 3 | 1 + 3 | 2 | red | 3 | 2 + 4 | 2 | red | 3 | 3 + 5 | 2 | red | 3 | 4 +(10 rows) + +-- test columns derived from GROUP BY columns with cast +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | device_id +------+----------- + 0 | 1 + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 1 + 0 | 2 + 1 | 2 + 2 | 2 + 3 | 2 + 4 | 2 +(10 rows) + +-- test columns derived from GROUP BY columns with expression +SELECT + time_bucket_gapfill(1,time,0,5) as time, + 'Device ' || device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | ?column? +------+---------- + 0 | Device 1 + 1 | Device 1 + 2 | Device 1 + 3 | Device 1 + 4 | Device 1 + 0 | Device 2 + 1 | Device 2 + 2 | Device 2 + 3 | Device 2 + 4 | Device 2 +(10 rows) + +--test interpolation with big differences in values (test overflows in calculations) +--we use the biggest possible difference in time(x) and the value(y). +--For bigints we also test values of smaller than bigintmax/min to avoid +--the symmetry where x=y (which catches more errors) +SELECT 9223372036854775807 as big_int_max \gset +SELECT -9223372036854775808 as big_int_min \gset +SELECT + time_bucket_gapfill(1,time,0,1) AS time, + interpolate(min(s)) AS "smallint", + interpolate(min(i)) AS "int", + interpolate(min(b)) AS "bigint", + interpolate(min(b2)) AS "bigint2", + interpolate(min(d)) AS "double" +FROM (values (:big_int_min,(-32768)::smallint,(-2147483648)::int,:big_int_min,-2147483648::bigint, '-Infinity'::double precision), + (:big_int_max, 32767::smallint, 2147483647::int,:big_int_max, 2147483647::bigint, 'Infinity'::double precision)) v(time,s,i,b,b2,d) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | bigint2 | double +----------------------+----------+-------------+----------------------+-------------+----------- + -9223372036854775808 | -32768 | -2147483648 | -9223372036854775808 | -2147483648 | -Infinity + 0 | 0 | 0 | 0 | 0 | Infinity + 9223372036854775807 | 32767 | 2147483647 | 9223372036854775807 | 2147483647 | Infinity +(3 rows) + +-- issue #2232: This query used to trigger error "could not find +-- pathkey item to sort" due to a corrupt query plan +SELECT time_bucket_gapfill('1 h', time) AS time, + locf(sum(v1)) AS v1_sum, + interpolate(sum(v2)) AS v2_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v2_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 06:00:00 2018 PST | 2.1 | 65 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 + Mon Jan 01 04:00:00 2018 PST | | +(4 rows) + +-- query without gapfill: +SELECT time_bucket('1 h', time) AS time, + sum(v1) AS v1_sum, + sum(v2) AS v1_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v1_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 +(2 rows) + +-- query to show original data +SELECT * FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +ORDER BY 1 DESC, 2; + time | device_id | v1 | v2 +------------------------------+-----------+-----+---- + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 +(6 rows) + +-- issue #3048 +-- test gapfill/hashagg planner interaction +-- this used to produce a plan without gapfill node +EXPLAIN (costs off) SELECT time_bucket_gapfill('52w', time, start:='2000-01-01', finish:='2000-01-10') AS time, + sum(v1) AS v1_sum +FROM metrics +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(10 rows) + +-- issue #3834 +-- test projection handling in gapfill +CREATE TABLE i3834(time timestamptz NOT NULL, ship_id int, value float); +SELECT table_name FROM create_hypertable('i3834','time'); + table_name + i3834 +(1 row) + +INSERT INTO i3834 VALUES ('2020-12-01 14:05:00+01',1,3.123), ('2020-12-01 14:05:00+01',2,4.123), ('2020-12-01 14:05:00+01',3,5.123); +SELECT + time_bucket_gapfill('30000 ms'::interval, time) AS time, + ship_id, + interpolate (avg(value)), + 'speedlog' AS source +FROM + i3834 +WHERE + ship_id IN (1, 2) + AND time >= '2020-12-01 14:05:00+01' + AND time < '2020-12-01 14:10:00+01' +GROUP BY 1,2; + time | ship_id | interpolate | source +------------------------------+---------+-------------+---------- + Tue Dec 01 05:05:00 2020 PST | 1 | 3.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:05:00 2020 PST | 2 | 4.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 2 | | speedlog +(20 rows) + +DROP TABLE i3834; +-- issue #1528 +-- test float rounding for certain float values when start and end are identical +SELECT + time_bucket_gapfill('1min'::interval, ts::timestamptz, start:='2019-11-05 2:20', finish:='2019-11-05 2:30'), + interpolate(avg(20266.959547::float4)) AS float4, + interpolate(avg(20266.959547::float8)) AS float8 +FROM (VALUES ('2019-11-05 2:20'), ('2019-11-05 2:30')) v (ts) +GROUP BY 1; + time_bucket_gapfill | float4 | float8 +------------------------------+-----------------+-------------- + Tue Nov 05 02:20:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:21:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:22:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:23:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:24:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:25:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:26:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:27:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:28:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:29:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:30:00 2019 PST | 20266.958984375 | 20266.959547 +(11 rows) + +-- check gapfill group change detection with TOASTed values +CREATE TABLE gapfill_group_toast(time timestamptz NOT NULL, device text, value float); +SELECT table_name FROM create_hypertable('gapfill_group_toast', 'time'); + table_name + gapfill_group_toast +(1 row) + +INSERT INTO gapfill_group_toast +SELECT + generate_series('2022-06-01'::timestamptz, '2022-06-03'::timestamptz, '1min'::interval), + '4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43', + random(); +ALTER TABLE gapfill_group_toast SET(timescaledb.compress, timescaledb.compress_segmentby = 'device'); +SELECT count(compress_chunk(c)) FROM show_chunks('gapfill_group_toast') c; + count + 2 +(1 row) + +SELECT + time_bucket_gapfill('1 day', time), device +FROM gapfill_group_toast +WHERE time >= '2022-06-01' AND time <= '2022-06-02' +GROUP BY 1,2; + time_bucket_gapfill | device +------------------------------+------------------------------------------------------------------ + Tue May 31 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 + Wed Jun 01 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 +(2 rows) + +DROP TABLE gapfill_group_toast; +-- test bucketing by month +SELECT time_bucket_gapfill('2 month'::interval, ts, '2000-01-01'::timestamptz,'2001-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sun Apr 30 17:00:00 2000 PDT + Fri Jun 30 17:00:00 2000 PDT + Thu Aug 31 17:00:00 2000 PDT + Tue Oct 31 16:00:00 2000 PST + Sun Dec 31 16:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('1 year'::interval, ts, '2000-01-01'::timestamptz,'2003-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Sun Dec 31 16:00:00 2000 PST + Mon Dec 31 16:00:00 2001 PST + Tue Dec 31 16:00:00 2002 PST +(4 rows) + +SELECT time_bucket_gapfill('1 century'::interval, ts, '1900-01-01'::timestamptz,'2103-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sun Dec 31 16:00:00 1899 PST + Fri Dec 31 16:00:00 1999 PST + Thu Dec 31 16:00:00 2099 PST +(3 rows) + +-- test bucketing with timezone +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 15:00:00 1999 PST + Tue Feb 29 15:00:00 2000 PST + Sat Apr 29 15:00:00 2000 PDT + Thu Jun 29 15:00:00 2000 PDT + Tue Aug 29 15:00:00 2000 PDT + Sun Oct 29 15:00:00 2000 PST + Fri Dec 29 15:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, current_setting('timezone'), '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 PST + Wed Mar 01 00:00:00 2000 PST + Mon May 01 00:00:00 2000 PDT + Sat Jul 01 00:00:00 2000 PDT + Fri Sep 01 00:00:00 2000 PDT + Wed Nov 01 00:00:00 2000 PST +(6 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, 'UTC', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sat Apr 29 16:00:00 2000 PDT + Thu Jun 29 16:00:00 2000 PDT + Tue Aug 29 16:00:00 2000 PDT + Sun Oct 29 16:00:00 2000 PST + Fri Dec 29 16:00:00 2000 PST +(7 rows) + +SET timezone TO 'Europe/Berlin'; +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 CET + Wed Mar 01 00:00:00 2000 CET + Mon May 01 00:00:00 2000 CEST + Sat Jul 01 00:00:00 2000 CEST + Fri Sep 01 00:00:00 2000 CEST + Wed Nov 01 00:00:00 2000 CET +(6 rows) + +RESET timezone; +DROP INDEX gapfill_plan_test_indx; +-- Test gapfill with arrays (#5981) +SELECT time_bucket_gapfill(5, ts, 1, 100) as ts, int_arr, locf(last(value, ts)) +FROM ( + SELECT ARRAY[1,2,3,4]::int[] as int_arr, x as ts, x+500000 as value + FROM generate_series(1, 10, 100) as x + ) t +GROUP BY 1, 2 + ts | int_arr | locf +----+-----------+-------- + 0 | {1,2,3,4} | 500001 + 5 | {1,2,3,4} | 500001 + 10 | {1,2,3,4} | 500001 + 15 | {1,2,3,4} | 500001 + 20 | {1,2,3,4} | 500001 + 25 | {1,2,3,4} | 500001 + 30 | {1,2,3,4} | 500001 + 35 | {1,2,3,4} | 500001 + 40 | {1,2,3,4} | 500001 + 45 | {1,2,3,4} | 500001 + 50 | {1,2,3,4} | 500001 + 55 | {1,2,3,4} | 500001 + 60 | {1,2,3,4} | 500001 + 65 | {1,2,3,4} | 500001 + 70 | {1,2,3,4} | 500001 + 75 | {1,2,3,4} | 500001 + 80 | {1,2,3,4} | 500001 + 85 | {1,2,3,4} | 500001 + 90 | {1,2,3,4} | 500001 + 95 | {1,2,3,4} | 500001 +(20 rows) + diff --git a/tsl/test/shared/expected/gapfill-16.out b/tsl/test/shared/expected/gapfill-16.out new file mode 100644 index 00000000000..4ba9c41a2bf --- /dev/null +++ b/tsl/test/shared/expected/gapfill-16.out @@ -0,0 +1,3368 @@ +-- 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. +\set EXPLAIN 'EXPLAIN (COSTS OFF)' +-- we want to see error details in the output +\set VERBOSITY default +CREATE TABLE gapfill_plan_test(time timestamptz NOT NULL, value float); +SELECT table_name FROM create_hypertable('gapfill_plan_test','time',chunk_time_interval=>'4 weeks'::interval); + table_name + gapfill_plan_test +(1 row) + +INSERT INTO gapfill_plan_test SELECT generate_series('2018-01-01'::timestamptz,'2018-04-01'::timestamptz,'1m'::interval), 1.0; +-- simple example +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test sorting +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test sort direction +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1 DESC; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) DESC + -> Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) NULLS FIRST + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now()) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test order by aggregate function +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2,1; +QUERY PLAN + Sort + Sort Key: (avg("*VALUES*".column2)), (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test query without order by +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test parallel query +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + avg(value) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with locf +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- test parallel query with interpolate +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(15 rows) + +-- make sure we can run gapfill in parallel workers +-- ensure this plan runs in parallel +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; +QUERY PLAN + Limit + -> Sort + Sort Key: (interpolate(avg(gapfill_plan_test.value), NULL::record, NULL::record)) + -> Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk + -> Parallel Seq Scan on _hyper_X_X_chunk +(18 rows) + +-- actually run a parallel gapfill +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + time_bucket_gapfill | interpolate +------------------------------+------------- + Mon Jan 01 00:00:00 2018 PST | 1 +(1 row) + +-- test sort optimizations +-- test sort optimization with single member order by, +-- should use index scan (no GapFill node for this one since we're not gapfilling) +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +SET max_parallel_workers_per_gather TO 0; +-- test sort optimizations with locf +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +-- test sort optimizations with interpolate +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(11 rows) + +RESET max_parallel_workers_per_gather; +CREATE INDEX gapfill_plan_test_indx ON gapfill_plan_test(value, time); +-- test sort optimization with ordering by multiple columns and time_bucket_gapfill not last, +-- must not use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1,2; +QUERY PLAN + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)), _hyper_X_X_chunk.value + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(8 rows) + +-- test sort optimization with ordering by multiple columns and time_bucket as last member, +-- should use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 2,1; +QUERY PLAN + Incremental Sort + Sort Key: _hyper_X_X_chunk.value, (time_bucket_gapfill('@ 5 mins'::interval, _hyper_X_X_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + Presorted Key: _hyper_X_X_chunk.value + -> Result + -> Merge Append + Sort Key: _hyper_X_X_chunk.value + -> Index Only Scan using _hyper_X_X_chunk_gapfill_plan_test_indx on _hyper_X_X_chunk + -> Index Only Scan using _hyper_X_X_chunk_gapfill_plan_test_indx on _hyper_X_X_chunk + -> Index Only Scan using _hyper_X_X_chunk_gapfill_plan_test_indx on _hyper_X_X_chunk + -> Index Only Scan using _hyper_X_X_chunk_gapfill_plan_test_indx on _hyper_X_X_chunk +(10 rows) + +\set METRICS metrics_int +-- All test against table :METRICS first +\set ON_ERROR_STOP 0 +-- inverse of previous test query to confirm an error is actually thrown +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time = 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; +ERROR: division by zero +-- test window functions with multiple column references +SELECT + time_bucket_gapfill(1,time,1,2), + first(min(time),min(time)) OVER () +FROM :METRICS +GROUP BY 1; +ERROR: window functions with multiple column references not supported +-- test with unsupported operator +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time =0 AND time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test with 2 tables and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test inner join and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time +WHERE m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- test outer join with constraints in join condition +-- not usable as start/stop +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 LEFT OUTER JOIN :METRICS m2 ON m1.time=m2.time AND m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +\ir include/gapfill_metrics_query.sql +-- 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. +-- test locf lookup query does not trigger when not needed +-- 1/(SELECT 0) will throw an error in the lookup query but in order to not +-- always trigger evaluation it needs to be correlated otherwise postgres will +-- always run it once even if the value is never used +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | locf3 +------+-----------+-----------+------- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +-- test locf with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value)::int,23) AS locf1, + locf(min(value)::int,(SELECT 42)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and "wrong order" +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value),23::float) AS locf1, + locf(min(value),(SELECT 42::float)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 1,2,3; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and window functions +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)), + sum(locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1))) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3; + time | device_id | sensor_id | locf | sum +------+-----------+-----------+------+------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 5 | 10 + 10 | 1 | 1 | 5 | 10 + 0 | 1 | 2 | -100 | -100 + 5 | 1 | 2 | 10 | -90 + 10 | 1 | 2 | 10 | 20 +(6 rows) + +-- test JOINs +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id, + d.name, + sensor_id, + s.name, + avg(m.value) +FROM :METRICS m +INNER JOIN devices d USING(device_id) +INNER JOIN sensors s USING(sensor_id) +WHERE time BETWEEN 0 AND 5 +GROUP BY 1,2,3,4,5; + time | device_id | name | sensor_id | name | avg +------+-----------+----------+-----------+----------+----- + 0 | 1 | Device 1 | 1 | Sensor 1 | 5 + 1 | 1 | Device 1 | 1 | Sensor 1 | + 2 | 1 | Device 1 | 1 | Sensor 1 | + 3 | 1 | Device 1 | 1 | Sensor 1 | + 4 | 1 | Device 1 | 1 | Sensor 1 | + 0 | 1 | Device 1 | 2 | Sensor 2 | + 1 | 1 | Device 1 | 2 | Sensor 2 | + 2 | 1 | Device 1 | 2 | Sensor 2 | + 3 | 1 | Device 1 | 2 | Sensor 2 | + 4 | 1 | Device 1 | 2 | Sensor 2 | + 5 | 1 | Device 1 | 2 | Sensor 2 | 10 +(11 rows) + +-- test interpolate with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + interpolate(min(value)) AS ip, + interpolate(min(value),(-5,-5.0::float),(15,20.0::float)) AS ip1, + interpolate(min(value),(SELECT (-10,-10.0::float)),(SELECT (15,20.0::float))) AS ip2, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) AS ip3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | ip | ip1 | ip2 | ip3 +------+-----------+-----------+-----+----+-----+------------------+------------------ + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | | 10 | 10 | 4.75 + 10 | 1 | 1 | | | 15 | 15 | 4.5 + 0 | 1 | 2 | | | 2.5 | 3.33333333333333 | 4.76190476190476 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | | 15 | 15 | 4.21052631578947 +(6 rows) + +-- test interpolate with correlated subquery and window function +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ), + sum(interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + )) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | interpolate | sum +------+-----------+-----------+------------------+------------------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 4.75 | 9.75 + 10 | 1 | 1 | 4.5 | 9.25 + 0 | 1 | 2 | 4.76190476190476 | 4.76190476190476 + 5 | 1 | 2 | 10 | 14.7619047619048 + 10 | 1 | 2 | 4.21052631578947 | 14.2105263157895 +(6 rows) + +-- test subqueries +-- subqueries will alter the shape of the plan and top-level constraints +-- might not end up in top-level of jointree +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 +WHERE m1.time >=0 AND m1.time < 2 AND device_id IN (SELECT device_id FROM :METRICS) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test inner join with constraints in join condition +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time AND m2.time >=0 AND m2.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test actual table +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time >=0 AND time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with table alias +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS m +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test with 2 tables +SELECT + time_bucket_gapfill(1,m.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 +(2 rows) + +-- test prepared statement with locf with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with interpolate with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with variable gapfill arguments +PREPARE prep_gapfill(int,int,int) AS +SELECT + time_bucket_gapfill($1,time,$2,$3) AS time, + device_id, + sensor_id, + min(value) +FROM :METRICS m1 +WHERE time >= $2 AND time < $3 AND device_id=1 AND sensor_id=1 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +DEALLOCATE prep_gapfill; +-- Tests without tables +-- test locf and interpolate call without gapfill +SELECT locf(1); + locf + 1 +(1 row) + +SELECT interpolate(1); + interpolate + 1 +(1 row) + +-- test locf and interpolate call with NULL input +SELECT locf(NULL::int); + locf + +(1 row) + +SELECT interpolate(NULL::bigint); + interpolate + +(1 row) + +\set ON_ERROR_STOP 0 +-- test time_bucket_gapfill not top level function call +SELECT + 1 + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: no top level time_bucket_gapfill in group by clause +-- test locf with treat_null_as_missing not BOOL +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=1) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function locf(integer, treat_null_as_missing => integer) does not exist +LINE 3: locf(min(time),treat_null_as_missing:=1) + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test locf with treat_null_as_missing not literal +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=random()>0) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid locf argument: treat_null_as_missing must be a BOOL literal +-- test interpolate lookup query with 1 element in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT ROW(10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT ROW(10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with 3 elements in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with mismatching time datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10::float,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10::float,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate lookup query with mismatching value datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10::float))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10::float))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +DETAIL: Returned type double precision does not match expected type integer. +-- test interpolate with unsupported datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(text 'text') +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function interpolate(text) does not exist +LINE 3: interpolate(text 'text') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interval '1d') +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: function interpolate(interval) does not exist +LINE 3: interpolate(interval '1d') + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test multiple time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time,1,11),time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time_bucket_gapfill(1,time,1,11),1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested locf calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(locf(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test nested interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test mixed locf/interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test window function inside locf +SELECT + time_bucket_gapfill(1,time,1,11), + locf(avg(min(time)) OVER ()) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window functions must not be below locf +-- test nested window functions +-- prevented by postgres +SELECT + time_bucket_gapfill(1,time,1,11), + avg(avg(min(time)) OVER ()) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window function calls cannot be nested +LINE 3: avg(avg(min(time)) OVER ()) OVER () + ^ +-- test multiple window functions in single column +SELECT + time_bucket_gapfill(1,time,1,11), + avg(min(time)) OVER () + avg(min(time)) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple window function calls per column not supported +-- test locf not toplevel +SELECT + time_bucket_gapfill(1,time,1,11), + 1 + locf(min(time)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: locf must be toplevel function call +-- test locf inside aggregate +SELECT + time_bucket_gapfill(1,time,1,11), + min(min(locf(time))) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: aggregate functions must be below locf +-- test NULL args +SELECT + time_bucket_gapfill(NULL,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill(1,NULL,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill(1,time,NULL,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(1,time,1,NULL) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +SELECT + time_bucket_gapfill(NULL,time,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill('1day',NULL,'Europe/Berlin','2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill('1day',time,NULL,'2000-06-01','2001-06-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2001-01-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: timezone cannot be NULL +-- test 0 bucket_width +SELECT + time_bucket_gapfill(0,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test negative bucket_width +SELECT + time_bucket_gapfill(-1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test subqueries as interval, start and stop (not supported atm) +SELECT + time_bucket_gapfill((SELECT 1),time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +SELECT + time_bucket_gapfill(1,time,(SELECT 1),11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +SELECT + time_bucket_gapfill(1,time,1,(SELECT 11)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +\set ON_ERROR_STOP 1 +-- test time_bucket_gapfill without aggregation +-- this will not trigger gapfilling +SELECT + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill + 1 + 2 +(2 rows) + +SELECT + time_bucket_gapfill(1,time,1,11), + avg(time) OVER () +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill | avg +---------------------+-------------------- + 1 | 1.5000000000000000 + 2 | 1.5000000000000000 +(2 rows) + +-- test int int2/4/8 +SELECT + time_bucket_gapfill(1::int2,time::int2,0::int2,6::int2) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int4,time::int4,0::int4,6::int4) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int8,time::int8,0::int8,6::int8) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +-- test non-aligned bucket start +SELECT + time_bucket_gapfill(10,time,5,40) +FROM (VALUES (11),(22)) v(time) +GROUP BY 1; + time_bucket_gapfill + 0 + 10 + 20 + 30 +(4 rows) + +-- simple gapfill query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + min(value) AS value +FROM (values (-10,1),(10,2),(11,3),(12,4),(22,5),(30,6),(66,7)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + -10 | 1 + 0 | + 10 | 2 + 20 | 5 + 30 | 6 + 40 | + 60 | 7 +(7 rows) + +-- test references to different columns +SELECT + time_bucket_gapfill(1,t,0,5) as t, + min(t),max(t),min(v),max(v) +FROM(VALUES (1,3),(2,5)) tb(t,v) +GROUP BY 1 ORDER BY 1; + t | min | max | min | max +---+-----+-----+-----+----- + 0 | | | | + 1 | 1 | 1 | 3 | 3 + 2 | 2 | 2 | 5 | 5 + 3 | | | | + 4 | | | | +(5 rows) + +-- test passing of values outside boundaries +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (-1),(1),(3),(6)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + -1 | -1 + 0 | + 1 | 1 + 2 | + 3 | 3 + 4 | + 6 | 6 +(7 rows) + +-- test gap fill before first row and after last row +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | 1 + 2 | 2 + 3 | 3 + 4 | +(5 rows) + +-- test gap fill without rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +WHERE false +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | + 2 | + 3 | + 4 | +(5 rows) + +-- test coalesce +SELECT + time_bucket_gapfill(1,time,0,5), + coalesce(min(time),0), + coalesce(min(value),0), + coalesce(min(value),7) +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | coalesce | coalesce | coalesce +---------------------+----------+----------+---------- + 0 | 0 | 0 | 7 + 1 | 1 | 1 | 1 + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 0 | 0 | 7 +(5 rows) + +-- test case +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + CASE WHEN min(time) IS NOT NULL THEN min(time) ELSE -1 END, + CASE WHEN min(time) IS NOT NULL THEN min(time) + 7 ELSE 0 END, + CASE WHEN 1 = 1 THEN 1 ELSE 0 END +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | case | case | case +---------------------+-----+------+------+------ + 0 | | -1 | 0 | 1 + 1 | 1 | 1 | 8 | 1 + 2 | 2 | 2 | 9 | 1 + 3 | 3 | 3 | 10 | 1 + 4 | | -1 | 0 | 1 +(5 rows) + +-- test constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), min(time), 4 as c +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | min | c +---------------------+-----+-----+--- + 0 | | | 4 + 1 | 1 | 1 | 4 + 2 | 2 | 2 | 4 + 3 | 3 | 3 | 4 + 4 | | | 4 +(5 rows) + +-- test column reordering +SELECT + 1 as c1, '2' as c2, + time_bucket_gapfill(1,time,0,5), + 3.0 as c3, + min(time), min(time), 4 as c4 +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 3 ORDER BY 3; + c1 | c2 | time_bucket_gapfill | c3 | min | min | c4 +----+----+---------------------+-----+-----+-----+---- + 1 | 2 | 0 | 3.0 | | | 4 + 1 | 2 | 1 | 3.0 | 1 | 1 | 4 + 1 | 2 | 2 | 3.0 | 2 | 2 | 4 + 1 | 2 | 3 | 3.0 | 3 | 3 | 4 + 1 | 2 | 4 | 3.0 | | | 4 +(5 rows) + +-- test timestamptz +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMPTZ '2000-01-01',TIMESTAMPTZ '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMPTZ '2000-01-01 9:00:00'),(TIMESTAMPTZ '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +------------------------------+------------------------------ + Fri Dec 31 22:00:00 1999 PST | + Sat Jan 01 04:00:00 2000 PST | Sat Jan 01 09:00:00 2000 PST + Sat Jan 01 10:00:00 2000 PST | + Sat Jan 01 16:00:00 2000 PST | Sat Jan 01 18:00:00 2000 PST + Sat Jan 01 22:00:00 2000 PST | +(5 rows) + +-- test timestamp +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMP '2000-01-01',TIMESTAMP '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMP '2000-01-01 9:00:00'),(TIMESTAMP '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +--------------------------+-------------------------- + Sat Jan 01 00:00:00 2000 | + Sat Jan 01 06:00:00 2000 | Sat Jan 01 09:00:00 2000 + Sat Jan 01 12:00:00 2000 | + Sat Jan 01 18:00:00 2000 | Sat Jan 01 18:00:00 2000 +(4 rows) + +-- test date +SELECT + time_bucket_gapfill(INTERVAL '1w',time,DATE '2000-01-01',DATE '2000-02-10'), + min(time) +FROM (VALUES (DATE '2000-01-08'),(DATE '2000-01-22')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+------------ + 12-27-1999 | + 01-03-2000 | 01-08-2000 + 01-10-2000 | + 01-17-2000 | 01-22-2000 + 01-24-2000 | + 01-31-2000 | + 02-07-2000 | +(7 rows) + +-- test grouping by non-time columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test grouping by non-time columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +WHERE false +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- +(0 rows) + +-- test duplicate columns in GROUP BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,2,3 ORDER BY 2,1; + time | id | id | m +------+----+----+--- + 0 | 1 | 1 | + 1 | 1 | 1 | 1 + 2 | 1 | 1 | + 3 | 1 | 1 | + 4 | 1 | 1 | + 0 | 2 | 2 | + 1 | 2 | 2 | + 2 | 2 | 2 | 2 + 3 | 2 | 2 | + 4 | 2 | 2 | +(10 rows) + +-- test grouping by columns not in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY id,1; + time | m +------+--- + 0 | + 1 | 1 + 2 | + 3 | + 4 | + 0 | + 1 | + 2 | 2 + 3 | + 4 | +(10 rows) + +-- test grouping by non-time columns with text columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 1 | blue | 1 + 2 | blue | + 3 | blue | + 4 | blue | + 0 | red | + 1 | red | + 2 | red | 2 + 3 | red | + 4 | red | +(10 rows) + +-- test grouping by non-time columns with text columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +WHERE false +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- +(0 rows) + +--- test insert into SELECT +CREATE TABLE gapfill_insert_test(id INT); +INSERT INTO gapfill_insert_test SELECT time_bucket_gapfill(1,time,1,5) FROM (VALUES (1),(2)) v(time) GROUP BY 1 ORDER BY 1; +SELECT * FROM gapfill_insert_test; + id + 1 + 2 + 3 + 4 +(4 rows) + +-- test join +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (1,'red',1),(2,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | m | m +------+-------+---+--- + 0 | blue | | + 1 | blue | | + 2 | blue | 2 | + 3 | blue | | + 4 | blue | | 2 + 0 | red | | + 1 | red | 1 | + 2 | red | | + 3 | red | | 1 + 4 | red | | +(10 rows) + +-- test join with locf +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as locf + FROM + (VALUES (0,'red',1),(0,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | locf | m +------+-------+------+--- + 0 | blue | 2 | + 1 | blue | 2 | + 2 | blue | 2 | + 3 | blue | 2 | + 4 | blue | 2 | 2 + 0 | red | 1 | + 1 | red | 1 | + 2 | red | 1 | + 3 | red | 1 | 1 + 4 | red | 1 | 1 +(10 rows) + +-- test locf +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=NULL) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in first row of resultset and treat_null_as_missing with lookup query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false, prev := (SELECT 100)) AS v1, + locf(min(value),treat_null_as_missing:=true, prev := (SELECT 100)) AS v2 +FROM (values (0,NULL),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | v1 | v2 +------+----+----- + 0 | | 100 + 10 | | 100 + 20 | | 100 + 30 | | 100 + 40 | | 100 + 50 | 6 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing with resort +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1 DESC; + time | value +------+------- + 50 | 6 + 40 | 3 + 30 | 3 + 20 | 3 + 10 | 9 + 0 | +(6 rows) + +-- test locf with constants +SELECT + time_bucket_gapfill(1,time,0,5), + 2, + locf(min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | ?column? | locf +---------------------+----------+------ + 0 | 2 | 1 + 1 | 2 | 1 + 2 | 2 | 1 + 3 | 2 | 1 + 4 | 2 | 2 +(5 rows) + +-- test expressions inside locf +SELECT + time_bucket_gapfill(1,time,0,5), + locf(min(value)), + locf(4), + locf(4 + min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | locf | locf | locf +---------------------+------+------+------ + 0 | 1 | 4 | 5 + 1 | 1 | 4 | 5 + 2 | 1 | 4 | 5 + 3 | 1 | 4 | 5 + 4 | 2 | 4 | 6 +(5 rows) + +-- test locf with out of boundary lookup +SELECT + time_bucket_gapfill(10,time,0,70) AS time, + locf(min(value),(SELECT 100)) AS value +FROM (values (20,9),(40,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 100 + 10 | 100 + 20 | 9 + 30 | 9 + 40 | 6 + 50 | 6 + 60 | 6 +(7 rows) + +-- test locf with different datatypes +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1)) AS text, + locf(min(v2)) AS "int[]", + locf(min(v3)) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test locf with different datatypes and treat_null_as_missing +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1),treat_null_as_missing:=true) AS text, + locf(min(v2),treat_null_as_missing:=true) AS "int[]", + locf(min(v3),treat_null_as_missing:=true) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (2,NULL,NULL,NULL), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test interpolate +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(value)) AS value +FROM (values (0,1),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 1 + 10 | 2 + 20 | 3 + 30 | 4 + 40 | 5 + 50 | 6 +(6 rows) + +-- test interpolate with NULL values +SELECT + time_bucket_gapfill(1,time,0,5) AS time, + interpolate(avg(temp)) AS temp +FROM (VALUES (0,0),(2,NULL),(5,5)) v(time,temp) +GROUP BY 1; + time | temp +------+------ + 0 | 0 + 1 | + 2 | + 3 | + 4 | + 5 | 5 +(6 rows) + +-- test interpolate datatypes +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (0,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(50,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + 0 | -3 | -3 | -3 | -3 | -3 + 10 | -2 | -2 | -2 | -1.8 | -1.8 + 20 | -1 | -1 | -1 | -0.6 | -0.6 + 30 | 1 | 1 | 1 | 0.6 | 0.6 + 40 | 2 | 2 | 2 | 1.8 | 1.8 + 50 | 3 | 3 | 3 | 3 | 3 +(6 rows) + +-- test interpolate datatypes with negative time +SELECT + time_bucket_gapfill(10,time,-40,30) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (-40,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(20,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + -40 | -3 | -3 | -3 | -3 | -3 + -30 | -2 | -2 | -2 | -2 | -2 + -20 | -1 | -1 | -1 | -1 | -1 + -10 | 0 | 0 | 0 | 0 | 0 + 0 | 1 | 1 | 1 | 1 | 1 + 10 | 2 | 2 | 2 | 2 | 2 + 20 | 3 | 3 | 3 | 3 | 3 +(7 rows) + +-- test interpolate with multiple groupings +SELECT + time_bucket_gapfill(5,time,0,11), + device, + interpolate(min(v1),(SELECT (-10,-10)),(SELECT (20,10))) +FROM (VALUES (5,1,0),(5,2,0)) as v(time,device,v1) +GROUP BY 1,2 ORDER BY 2,1; + time_bucket_gapfill | device | interpolate +---------------------+--------+------------- + 0 | 1 | -3 + 5 | 1 | 0 + 10 | 1 | 3 + 0 | 2 | -3 + 5 | 2 | 0 + 10 | 2 | 3 +(6 rows) + +-- test cte with gap filling in outer query +WITH data AS ( + SELECT * FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +) +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM data +GROUP BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test cte with gap filling in inner query +WITH gapfill AS ( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m + FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) + GROUP BY 1,id +) +SELECT * FROM gapfill; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test window functions +SELECT + time_bucket_gapfill(10,time,0,60), + interpolate(min(time)), + lag(min(time)) OVER () +FROM (VALUES (0),(50)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | lag +---------------------+-------------+----- + 0 | 0 | + 10 | 10 | 0 + 20 | 20 | + 30 | 30 | + 40 | 40 | + 50 | 50 | +(6 rows) + +-- test window functions with multiple windows +SELECT + time_bucket_gapfill(1,time,0,10), + interpolate(min(time)), + row_number() OVER (), + locf(min(time)), + sum(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 2 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 3 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 4 PRECEDING) +FROM (VALUES (0),(9)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | row_number | locf | sum | sum | sum | sum +---------------------+-------------+------------+------+-----+-----+-----+----- + 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + 1 | 1 | 2 | 0 | 1 | 1 | 1 | 1 + 2 | 2 | 3 | 0 | 3 | 3 | 3 | 3 + 3 | 3 | 4 | 0 | 5 | 6 | 6 | 6 + 4 | 4 | 5 | 0 | 7 | 9 | 10 | 10 + 5 | 5 | 6 | 0 | 9 | 12 | 14 | 15 + 6 | 6 | 7 | 0 | 11 | 15 | 18 | 20 + 7 | 7 | 8 | 0 | 13 | 18 | 22 | 25 + 8 | 8 | 9 | 0 | 15 | 21 | 26 | 30 + 9 | 9 | 10 | 9 | 17 | 24 | 30 | 35 +(10 rows) + +-- test window functions with constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + 4 as c, + lag(min(time)) OVER () +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | c | lag +---------------------+-----+---+----- + 0 | | 4 | + 1 | 1 | 4 | + 2 | 2 | 4 | 1 + 3 | 3 | 4 | 2 + 4 | | 4 | 3 +(5 rows) + +--test window functions with locf +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + locf(min(time)) AS locf, + lag(locf(min(time))) over () AS lag_locf, + lead(locf(min(time))) over () AS lead_locf +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | locf | lag_locf | lead_locf +---------------------+-----+---------+----------+------+----------+----------- + 0 | | | 1 | | | 1 + 1 | 1 | | 2 | 1 | | 2 + 2 | 2 | 1 | | 2 | 1 | 2 + 3 | | 2 | | 2 | 2 | 2 + 4 | | | | 2 | 2 | +(5 rows) + +--test window functions with interpolate +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + lead(interpolate(min(time))) over () AS lead_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | interpolate | lag_interpolate | lead_interpolate +---------------------+-----+---------+----------+-------------+-----------------+------------------ + 0 | | | 1 | | | 1 + 1 | 1 | | | 1 | | 2 + 2 | | 1 | 3 | 2 | 1 | 3 + 3 | 3 | | | 3 | 2 | + 4 | | 3 | | | 3 | +(5 rows) + +--test window functions with expressions +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + 1 + lag(min(time)) over () AS lag_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + 1 + lag(interpolate(min(time))) over () AS lag_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lag_min | interpolate | lag_interpolate | lag_interpolate +---------------------+-----+---------+---------+-------------+-----------------+----------------- + 0 | | | | | | + 1 | 1 | | | 1 | | + 2 | | 1 | 2 | 2 | 1 | 2 + 3 | 3 | | | 3 | 2 | 3 + 4 | | 3 | 4 | | 3 | 4 +(5 rows) + +--test row_number/rank/percent_rank/... window functions with gapfill reference +SELECT + time_bucket_gapfill(1,time,0,5), + ntile(2) OVER () AS ntile_2, + ntile(3) OVER () AS ntile_3, + ntile(5) OVER () AS ntile_5, + row_number() OVER (), + cume_dist() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + rank() OVER (), + rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + percent_rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)) +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | ntile_2 | ntile_3 | ntile_5 | row_number | cume_dist | rank | rank | percent_rank +---------------------+---------+---------+---------+------------+-----------+------+------+-------------- + 0 | 1 | 1 | 1 | 1 | 0.2 | 1 | 1 | 0 + 1 | 1 | 1 | 2 | 2 | 0.4 | 1 | 2 | 0.25 + 2 | 1 | 2 | 3 | 3 | 0.6 | 1 | 3 | 0.5 + 3 | 2 | 2 | 4 | 4 | 0.8 | 1 | 4 | 0.75 + 4 | 2 | 3 | 5 | 5 | 1 | 1 | 5 | 1 +(5 rows) + +-- test first_value/last_value/nth_value +SELECT + time_bucket_gapfill(1,time,0,5), + first_value(min(time)) OVER (), + nth_value(min(time),3) OVER (), + last_value(min(time)) OVER () +FROM (VALUES (0),(2),(5)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | nth_value | last_value +---------------------+-------------+-----------+------------ + 0 | 0 | 2 | 5 + 1 | 0 | 2 | 5 + 2 | 0 | 2 | 5 + 3 | 0 | 2 | 5 + 4 | 0 | 2 | 5 + 5 | 0 | 2 | 5 +(6 rows) + +-- test window functions with PARTITION BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (), + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | row_number | row_number +------+-------+------------+------------ + 0 | blue | 1 | 1 + 1 | blue | 2 | 2 + 2 | blue | 3 | 3 + 3 | blue | 4 | 4 + 4 | blue | 5 | 5 + 0 | red | 6 | 1 + 1 | red | 7 | 2 + 2 | red | 8 | 3 + 3 | red | 9 | 4 + 4 | red | 10 | 5 +(10 rows) + +-- test multiple windows +\set ON_ERROR_STOP 0 +SELECT + time_bucket_gapfill(1,time,0,11), + first_value(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + interpolate(min(time)), + last_value(interpolate(min(time))) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +FROM (VALUES (0),(10)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | interpolate | last_value +---------------------+-------------+-------------+------------ + 0 | 0 | 0 | 1 + 1 | 0 | 1 | 2 + 2 | 1 | 2 | 3 + 3 | 2 | 3 | 4 + 4 | 3 | 4 | 5 + 5 | 4 | 5 | 6 + 6 | 5 | 6 | 7 + 7 | 6 | 7 | 8 + 8 | 7 | 8 | 9 + 9 | 8 | 9 | 10 + 10 | 9 | 10 | 10 +(11 rows) + +-- test reorder +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM + (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 0 | 2 | + 1 | 1 | 1 + 1 | 2 | + 2 | 1 | + 2 | 2 | 2 + 3 | 1 | + 3 | 2 | + 4 | 1 | + 4 | 2 | +(10 rows) + +-- test order by locf +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | locf +---------------------+------ + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 + 1 | +(5 rows) + +-- test order by interpolate +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 4 | + 5 | + 1 | 1 + 2 | 2 + 3 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +-- test queries on hypertable +-- test locf and interpolate together +SELECT + time_bucket_gapfill(interval '1h',time,timestamptz '2018-01-01 05:00:00-8', timestamptz '2018-01-01 07:00:00-8'), + device_id, + locf(avg(v1)) AS locf_v1, + locf(min(v2)) AS locf_v2, + interpolate(avg(v1)) AS interpolate_v1, + interpolate(avg(v2)) AS interpolate_v2 +FROM metrics_tstz +GROUP BY 1,2 +ORDER BY 1,2; + time_bucket_gapfill | device_id | locf_v1 | locf_v2 | interpolate_v1 | interpolate_v2 +------------------------------+-----------+---------+---------+----------------+---------------- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.5 | 10 | 0.25 | 5 + Mon Jan 01 06:00:00 2018 PST | 2 | 0.7 | 20 | 1.05 | 30 + Mon Jan 01 06:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 +(9 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz) < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +-- interpolation with correlated subquery lookup before interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 3:00 PST'::timestamptz, '2018-01-01 8:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + (SELECT (time,0.5::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time DESC LIMIT 1) + ), + avg(v1) +FROM metrics_tstz m1 +WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 03:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 04:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 +(5 rows) + +-- interpolation with correlated subquery lookup after interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 5:00 PST'::timestamptz, '2018-01-01 9:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + next=>(SELECT (time,v2::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time LIMIT 1) + ),avg(v1) +FROM metrics_tstz m1 WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 08:00:00 2018 PST | 1 | -5 | +(4 rows) + +\set ON_ERROR_STOP 0 +-- bucket_width non simple expression +SELECT + time_bucket_gapfill(t,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +-- no start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- no start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,finish:=1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +-- NULL start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- unsupported finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +-- no finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- NULL finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with column reference on right side +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE t > t AND t < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with cast +SELECT + time_bucket_gapfill(1,t1::int8) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 >= 1 AND t1 <= 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with multiple column references +SELECT + time_bucket_gapfill(1,t1+t2) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 1 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL start in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END AND t1 < 4 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- expression with NULL finish in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as start argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- non-Const NULL as finish argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,NULL,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +HINT: Specify start and finish as arguments or in the WHERE clause. +-- time_bucket_gapfill with constraints ORed +SELECT + time_bucket_gapfill(1::int8,t::int8) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 OR t < 3 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +HINT: Specify start and finish as arguments or in the WHERE clause. +\set ON_ERROR_STOP 1 +-- int32 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- same query with less or equal as finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t <= 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 + 3 +(5 rows) + +-- int32 time_bucket_gapfill with start column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + -1 < t AND t < 3 +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 +(3 rows) + +-- int32 time_bucket_gapfill with finish column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= 0 AND 3 >= t +GROUP BY 1; + time_bucket_gapfill + 0 + 1 + 2 + 3 +(4 rows) + +-- int16 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int2,t) +FROM (VALUES (1::int2),(2::int2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- int64 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int8,t) +FROM (VALUES (1::int8),(2::int8)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- date time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('1d'::interval,t) +FROM (VALUES ('1999-12-30'::date),('2000-01-01'::date)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + 12-29-1999 + 12-30-1999 + 12-31-1999 + 01-01-2000 + 01-02-2000 +(5 rows) + +-- timestamp time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamp),('2000-01-01'::timestamp)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 00:00:00 1999 + Wed Dec 29 12:00:00 1999 + Thu Dec 30 00:00:00 1999 + Thu Dec 30 12:00:00 1999 + Fri Dec 31 00:00:00 1999 + Fri Dec 31 12:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sat Jan 01 12:00:00 2000 + Sun Jan 02 00:00:00 2000 + Sun Jan 02 12:00:00 2000 +(10 rows) + +-- timestamptz time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Tue Dec 28 16:00:00 1999 PST + Wed Dec 29 04:00:00 1999 PST + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(11 rows) + +-- timestamptz time_bucket_gapfill with more complex expression +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- timestamptz time_bucket_gapfill with different datatype in finish constraint +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03'::date +GROUP BY 1; + time_bucket_gapfill + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- time_bucket_gapfill with now() as start +SELECT + time_bucket_gapfill('1h'::interval,t) +FROM (VALUES (now()),(now())) v(t) +WHERE + t >= now() AND t < now() - '1h'::interval +GROUP BY 1; + time_bucket_gapfill +(0 rows) + +-- time_bucket_gapfill with multiple constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 and t>1 AND t <=4 AND length(version()) > 0 +GROUP BY 1; + time_bucket_gapfill + 2 +(1 row) + +-- int32 time_bucket_gapfill with greater for start +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t > -2 AND t < 3 +GROUP BY 1; + time_bucket_gapfill + -1 + 0 + 1 + 2 +(4 rows) + +-- test DISTINCT +SELECT DISTINCT ON (color) + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 0 | red | +(2 rows) + +-- test DISTINCT with window functions +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER () +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 6 + 1 | red | 7 + 2 | red | 8 + 3 | red | 9 + 4 | red | 10 +(10 rows) + +-- test DISTINCT with window functions and PARTITION BY +SELECT DISTINCT ON (color,row_number() OVER (PARTITION BY color)) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test DISTINCT with window functions not in targetlist +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test column references +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test with Nested Loop +SELECT l.id, bucket, data_value FROM + (VALUES (1), (2), (3), (4)) a(id) + INNER JOIN LATERAL ( + SELECT b.id id, time_bucket_gapfill('1'::int, time, start=>'1'::int, finish=> '5'::int) bucket, locf(last(data, time)) data_value + FROM (VALUES (1, 1, 1), (1, 4, 4), (2, 1, -1), (2, 4, -4)) b(id, time, data) + WHERE a.id = b.id + GROUP BY b.id, bucket + ) as l on (true); + id | bucket | data_value +----+--------+------------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | 4 | 4 + 2 | 1 | -1 + 2 | 2 | -1 + 2 | 3 | -1 + 2 | 4 | -4 +(8 rows) + +-- test prepared statement +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(value)) +FROM (VALUES (1,1),(2,2)) v(time,value) +GROUP BY 1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +DEALLOCATE prep_gapfill; +-- test column references with TIME_COLUMN last +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test expressions on GROUP BY columns +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + length(color), + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,5; + row_number | locf | color | length | time +------------+------+-------+--------+------ + 1 | | blue | 4 | 0 + 2 | 1 | blue | 4 | 1 + 3 | 1 | blue | 4 | 2 + 4 | 1 | blue | 4 | 3 + 5 | 1 | blue | 4 | 4 + 1 | | red | 3 | 0 + 2 | | red | 3 | 1 + 3 | 2 | red | 3 | 2 + 4 | 2 | red | 3 | 3 + 5 | 2 | red | 3 | 4 +(10 rows) + +-- test columns derived from GROUP BY columns with cast +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | device_id +------+----------- + 0 | 1 + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 1 + 0 | 2 + 1 | 2 + 2 | 2 + 3 | 2 + 4 | 2 +(10 rows) + +-- test columns derived from GROUP BY columns with expression +SELECT + time_bucket_gapfill(1,time,0,5) as time, + 'Device ' || device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | ?column? +------+---------- + 0 | Device 1 + 1 | Device 1 + 2 | Device 1 + 3 | Device 1 + 4 | Device 1 + 0 | Device 2 + 1 | Device 2 + 2 | Device 2 + 3 | Device 2 + 4 | Device 2 +(10 rows) + +--test interpolation with big differences in values (test overflows in calculations) +--we use the biggest possible difference in time(x) and the value(y). +--For bigints we also test values of smaller than bigintmax/min to avoid +--the symmetry where x=y (which catches more errors) +SELECT 9223372036854775807 as big_int_max \gset +SELECT -9223372036854775808 as big_int_min \gset +SELECT + time_bucket_gapfill(1,time,0,1) AS time, + interpolate(min(s)) AS "smallint", + interpolate(min(i)) AS "int", + interpolate(min(b)) AS "bigint", + interpolate(min(b2)) AS "bigint2", + interpolate(min(d)) AS "double" +FROM (values (:big_int_min,(-32768)::smallint,(-2147483648)::int,:big_int_min,-2147483648::bigint, '-Infinity'::double precision), + (:big_int_max, 32767::smallint, 2147483647::int,:big_int_max, 2147483647::bigint, 'Infinity'::double precision)) v(time,s,i,b,b2,d) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | bigint2 | double +----------------------+----------+-------------+----------------------+-------------+----------- + -9223372036854775808 | -32768 | -2147483648 | -9223372036854775808 | -2147483648 | -Infinity + 0 | 0 | 0 | 0 | 0 | Infinity + 9223372036854775807 | 32767 | 2147483647 | 9223372036854775807 | 2147483647 | Infinity +(3 rows) + +-- issue #2232: This query used to trigger error "could not find +-- pathkey item to sort" due to a corrupt query plan +SELECT time_bucket_gapfill('1 h', time) AS time, + locf(sum(v1)) AS v1_sum, + interpolate(sum(v2)) AS v2_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v2_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 06:00:00 2018 PST | 2.1 | 65 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 + Mon Jan 01 04:00:00 2018 PST | | +(4 rows) + +-- query without gapfill: +SELECT time_bucket('1 h', time) AS time, + sum(v1) AS v1_sum, + sum(v2) AS v1_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v1_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 +(2 rows) + +-- query to show original data +SELECT * FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +ORDER BY 1 DESC, 2; + time | device_id | v1 | v2 +------------------------------+-----------+-----+---- + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 +(6 rows) + +-- issue #3048 +-- test gapfill/hashagg planner interaction +-- this used to produce a plan without gapfill node +EXPLAIN (costs off) SELECT time_bucket_gapfill('52w', time, start:='2000-01-01', finish:='2000-01-10') AS time, + sum(v1) AS v1_sum +FROM metrics +GROUP BY 1; +QUERY PLAN + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 364 days'::interval, _hyper_X_X_chunk."time", 'Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone, 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk + -> Seq Scan on _hyper_X_X_chunk +(10 rows) + +-- issue #3834 +-- test projection handling in gapfill +CREATE TABLE i3834(time timestamptz NOT NULL, ship_id int, value float); +SELECT table_name FROM create_hypertable('i3834','time'); + table_name + i3834 +(1 row) + +INSERT INTO i3834 VALUES ('2020-12-01 14:05:00+01',1,3.123), ('2020-12-01 14:05:00+01',2,4.123), ('2020-12-01 14:05:00+01',3,5.123); +SELECT + time_bucket_gapfill('30000 ms'::interval, time) AS time, + ship_id, + interpolate (avg(value)), + 'speedlog' AS source +FROM + i3834 +WHERE + ship_id IN (1, 2) + AND time >= '2020-12-01 14:05:00+01' + AND time < '2020-12-01 14:10:00+01' +GROUP BY 1,2; + time | ship_id | interpolate | source +------------------------------+---------+-------------+---------- + Tue Dec 01 05:05:00 2020 PST | 1 | 3.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 1 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 1 | | speedlog + Tue Dec 01 05:05:00 2020 PST | 2 | 4.123 | speedlog + Tue Dec 01 05:05:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:06:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:07:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:08:30 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:00 2020 PST | 2 | | speedlog + Tue Dec 01 05:09:30 2020 PST | 2 | | speedlog +(20 rows) + +DROP TABLE i3834; +-- issue #1528 +-- test float rounding for certain float values when start and end are identical +SELECT + time_bucket_gapfill('1min'::interval, ts::timestamptz, start:='2019-11-05 2:20', finish:='2019-11-05 2:30'), + interpolate(avg(20266.959547::float4)) AS float4, + interpolate(avg(20266.959547::float8)) AS float8 +FROM (VALUES ('2019-11-05 2:20'), ('2019-11-05 2:30')) v (ts) +GROUP BY 1; + time_bucket_gapfill | float4 | float8 +------------------------------+-----------------+-------------- + Tue Nov 05 02:20:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:21:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:22:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:23:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:24:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:25:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:26:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:27:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:28:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:29:00 2019 PST | 20266.958984375 | 20266.959547 + Tue Nov 05 02:30:00 2019 PST | 20266.958984375 | 20266.959547 +(11 rows) + +-- check gapfill group change detection with TOASTed values +CREATE TABLE gapfill_group_toast(time timestamptz NOT NULL, device text, value float); +SELECT table_name FROM create_hypertable('gapfill_group_toast', 'time'); + table_name + gapfill_group_toast +(1 row) + +INSERT INTO gapfill_group_toast +SELECT + generate_series('2022-06-01'::timestamptz, '2022-06-03'::timestamptz, '1min'::interval), + '4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43', + random(); +ALTER TABLE gapfill_group_toast SET(timescaledb.compress, timescaledb.compress_segmentby = 'device'); +SELECT count(compress_chunk(c)) FROM show_chunks('gapfill_group_toast') c; + count + 2 +(1 row) + +SELECT + time_bucket_gapfill('1 day', time), device +FROM gapfill_group_toast +WHERE time >= '2022-06-01' AND time <= '2022-06-02' +GROUP BY 1,2; + time_bucket_gapfill | device +------------------------------+------------------------------------------------------------------ + Tue May 31 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 + Wed Jun 01 17:00:00 2022 PDT | 4e0ee04cc6a94fd40497b8dbaac2fe434e0ee04cc6a94fd40497b8dbaac2fe43 +(2 rows) + +DROP TABLE gapfill_group_toast; +-- test bucketing by month +SELECT time_bucket_gapfill('2 month'::interval, ts, '2000-01-01'::timestamptz,'2001-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sun Apr 30 17:00:00 2000 PDT + Fri Jun 30 17:00:00 2000 PDT + Thu Aug 31 17:00:00 2000 PDT + Tue Oct 31 16:00:00 2000 PST + Sun Dec 31 16:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('1 year'::interval, ts, '2000-01-01'::timestamptz,'2003-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Sun Dec 31 16:00:00 2000 PST + Mon Dec 31 16:00:00 2001 PST + Tue Dec 31 16:00:00 2002 PST +(4 rows) + +SELECT time_bucket_gapfill('1 century'::interval, ts, '1900-01-01'::timestamptz,'2103-01-01'::timestamptz) FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sun Dec 31 16:00:00 1899 PST + Fri Dec 31 16:00:00 1999 PST + Thu Dec 31 16:00:00 2099 PST +(3 rows) + +-- test bucketing with timezone +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 15:00:00 1999 PST + Tue Feb 29 15:00:00 2000 PST + Sat Apr 29 15:00:00 2000 PDT + Thu Jun 29 15:00:00 2000 PDT + Tue Aug 29 15:00:00 2000 PDT + Sun Oct 29 15:00:00 2000 PST + Fri Dec 29 15:00:00 2000 PST +(7 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, current_setting('timezone'), '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 PST + Wed Mar 01 00:00:00 2000 PST + Mon May 01 00:00:00 2000 PDT + Sat Jul 01 00:00:00 2000 PDT + Fri Sep 01 00:00:00 2000 PDT + Wed Nov 01 00:00:00 2000 PST +(6 rows) + +SELECT time_bucket_gapfill('2 month'::interval, ts, 'UTC', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Fri Dec 31 16:00:00 1999 PST + Tue Feb 29 16:00:00 2000 PST + Sat Apr 29 16:00:00 2000 PDT + Thu Jun 29 16:00:00 2000 PDT + Tue Aug 29 16:00:00 2000 PDT + Sun Oct 29 16:00:00 2000 PST + Fri Dec 29 16:00:00 2000 PST +(7 rows) + +SET timezone TO 'Europe/Berlin'; +SELECT time_bucket_gapfill('2 month'::interval, ts, 'Europe/Berlin', '2000-01-01','2001-01-01') FROM (VALUES ('2000-03-01'::timestamptz)) v(ts) GROUP BY 1; + time_bucket_gapfill + Sat Jan 01 00:00:00 2000 CET + Wed Mar 01 00:00:00 2000 CET + Mon May 01 00:00:00 2000 CEST + Sat Jul 01 00:00:00 2000 CEST + Fri Sep 01 00:00:00 2000 CEST + Wed Nov 01 00:00:00 2000 CET +(6 rows) + +RESET timezone; +DROP INDEX gapfill_plan_test_indx; +-- Test gapfill with arrays (#5981) +SELECT time_bucket_gapfill(5, ts, 1, 100) as ts, int_arr, locf(last(value, ts)) +FROM ( + SELECT ARRAY[1,2,3,4]::int[] as int_arr, x as ts, x+500000 as value + FROM generate_series(1, 10, 100) as x + ) t +GROUP BY 1, 2 + ts | int_arr | locf +----+-----------+-------- + 0 | {1,2,3,4} | 500001 + 5 | {1,2,3,4} | 500001 + 10 | {1,2,3,4} | 500001 + 15 | {1,2,3,4} | 500001 + 20 | {1,2,3,4} | 500001 + 25 | {1,2,3,4} | 500001 + 30 | {1,2,3,4} | 500001 + 35 | {1,2,3,4} | 500001 + 40 | {1,2,3,4} | 500001 + 45 | {1,2,3,4} | 500001 + 50 | {1,2,3,4} | 500001 + 55 | {1,2,3,4} | 500001 + 60 | {1,2,3,4} | 500001 + 65 | {1,2,3,4} | 500001 + 70 | {1,2,3,4} | 500001 + 75 | {1,2,3,4} | 500001 + 80 | {1,2,3,4} | 500001 + 85 | {1,2,3,4} | 500001 + 90 | {1,2,3,4} | 500001 + 95 | {1,2,3,4} | 500001 +(20 rows) + diff --git a/tsl/test/shared/sql/.gitignore b/tsl/test/shared/sql/.gitignore index ca69145ab79..1e6053ee040 100644 --- a/tsl/test/shared/sql/.gitignore +++ b/tsl/test/shared/sql/.gitignore @@ -4,6 +4,7 @@ /dist_fetcher_type-*.sql /dist_remote_error-*.sql /dist_remote_error.text +/gapfill-*.sql /generated_columns-*.sql /ordered_append-*.sql /ordered_append_join-*.sql diff --git a/tsl/test/shared/sql/CMakeLists.txt b/tsl/test/shared/sql/CMakeLists.txt index 3b3d6fbc4ae..99083b6fd13 100644 --- a/tsl/test/shared/sql/CMakeLists.txt +++ b/tsl/test/shared/sql/CMakeLists.txt @@ -7,12 +7,12 @@ set(TEST_FILES_SHARED constraint_exclusion_prepared.sql decompress_join.sql decompress_placeholdervar.sql - gapfill.sql subtract_integer_from_now.sql) set(TEST_TEMPLATES_SHARED - generated_columns.sql.in ordered_append.sql.in ordered_append_join.sql.in - transparent_decompress_chunk.sql.in space_constraint.sql.in) + generated_columns.sql.in gapfill.sql.in ordered_append.sql.in + ordered_append_join.sql.in transparent_decompress_chunk.sql.in + space_constraint.sql.in) if((${PG_VERSION_MAJOR} GREATER_EQUAL "14")) list(APPEND TEST_FILES_SHARED compression_dml.sql memoize.sql) diff --git a/tsl/test/shared/sql/gapfill.sql b/tsl/test/shared/sql/gapfill.sql.in similarity index 100% rename from tsl/test/shared/sql/gapfill.sql rename to tsl/test/shared/sql/gapfill.sql.in diff --git a/tsl/test/sql/.gitignore b/tsl/test/sql/.gitignore index 7c80876e95a..dd66d992c90 100644 --- a/tsl/test/sql/.gitignore +++ b/tsl/test/sql/.gitignore @@ -1,7 +1,6 @@ /*.pgbinary /cagg_bgw-*.sql /cagg_ddl-*.sql -/cagg_ddl_dist_ht-*.sql /cagg_errors_deprecated-*.sql /cagg_invalidation_dist_ht-*.sql /cagg_permissions-*.sql @@ -28,7 +27,6 @@ /modify_exclusion-*.sql /plan_skip_scan-*.sql /remote-copy-*sv -/telemetry_stats-*.sql /transparent_decompression-*.sql /transparent_decompression_ordered_index-*.sql /merge_append_partially_compressed-*.sql diff --git a/tsl/test/sql/CMakeLists.txt b/tsl/test/sql/CMakeLists.txt index 9a623ffee2d..0e2c345be3b 100644 --- a/tsl/test/sql/CMakeLists.txt +++ b/tsl/test/sql/CMakeLists.txt @@ -30,7 +30,7 @@ set(TEST_FILES skip_scan.sql size_utils_tsl.sql) -if(ENABLE_MULTINODE_TESTS) +if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") list(APPEND TEST_FILES dist_param.sql dist_views.sql) endif() @@ -70,11 +70,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) cagg_on_cagg_joins.sql cagg_tableam.sql cagg_policy_run.sql - data_fetcher.sql - data_node_bootstrap.sql - data_node.sql ddl_hook.sql - debug_notice.sql decompress_vector_qual.sql hypertable_generalization.sql insert_memory_usage.sql @@ -87,15 +83,23 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) recompress_chunk_segmentwise.sql transparent_decompression_join_index.sql feature_flags.sql) + if(USE_TELEMETRY) + list(APPEND TEST_FILES telemetry_stats.sql) + endif() - if(ENABLE_MULTINODE_TESTS) + if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") list( APPEND TEST_FILES cagg_bgw_dist_ht.sql + cagg_ddl_dist_ht.sql cagg_migrate_dist_ht.sql cagg_on_cagg_dist_ht.sql cagg_on_cagg_joins_dist_ht.sql + data_fetcher.sql + data_node_bootstrap.sql + data_node.sql + debug_notice.sql dist_api_calls.sql dist_commands.sql dist_compression.sql @@ -144,11 +148,8 @@ set(SOLO_TESTS compress_bgw_reorder_drop_chunks compression_ddl cagg_bgw - cagg_bgw_dist_ht - cagg_ddl - cagg_ddl_dist_ht + cagg_ddl-${PG_VERSION_MAJOR} cagg_dump - data_fetcher dist_util move remote_connection_cache @@ -159,10 +160,16 @@ set(SOLO_TESTS telemetry_stats-${PG_VERSION_MAJOR}) # In PG versions 15.0 to 15.2, dist_move_chunk can cause a deadlock when run in # parallel with other tests as mentioned in #4972. -if(${PG_VERSION_MAJOR} EQUAL "15" AND ${PG_VERSION_MINOR} LESS "3") +if(ENABLE_MULTINODE_TESTS + AND ${PG_VERSION_MAJOR} EQUAL "15" + AND ${PG_VERSION_MINOR} LESS "3") list(APPEND SOLO_TESTS dist_move_chunk) endif() +if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") + list(APPEND SOLO_TESTS cagg_bgw_dist_ht data_fetcher) +endif() + set(TEST_TEMPLATES compression_sorted_merge.sql.in cagg_union_view.sql.in @@ -190,11 +197,10 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) continuous_aggs.sql.in continuous_aggs_deprecated.sql.in deparse.sql.in) - if(ENABLE_MULTINODE_TESTS) + if(ENABLE_MULTINODE_TESTS AND ${PG_VERSION_MAJOR} LESS "16") list( APPEND TEST_TEMPLATES - cagg_ddl_dist_ht.sql.in cagg_invalidation_dist_ht.sql.in dist_hypertable.sql.in dist_grant.sql.in @@ -203,9 +209,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) dist_partial_agg.sql.in dist_query.sql.in) endif() - if(USE_TELEMETRY) - list(APPEND TEST_TEMPLATES telemetry_stats.sql.in) - endif() endif(CMAKE_BUILD_TYPE MATCHES Debug) # Check if PostgreSQL was compiled with JIT support diff --git a/tsl/test/sql/cagg_ddl_dist_ht.sql.in b/tsl/test/sql/cagg_ddl_dist_ht.sql similarity index 100% rename from tsl/test/sql/cagg_ddl_dist_ht.sql.in rename to tsl/test/sql/cagg_ddl_dist_ht.sql diff --git a/tsl/test/sql/chunk_api.sql b/tsl/test/sql/chunk_api.sql index 83ddd3b55cf..ac0a6ec5f18 100644 --- a/tsl/test/sql/chunk_api.sql +++ b/tsl/test/sql/chunk_api.sql @@ -3,10 +3,6 @@ -- LICENSE-TIMESCALE for a copy of the license. \c :TEST_DBNAME :ROLE_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 - -\ir include/remote_exec.sql GRANT CREATE ON DATABASE :"TEST_DBNAME" TO :ROLE_DEFAULT_PERM_USER; SET ROLE :ROLE_DEFAULT_PERM_USER; @@ -137,126 +133,6 @@ FROM pg_stats WHERE tablename IN FROM show_chunks('chunkapi')) ORDER BY tablename, attname; --- Test getting chunk stats on a distribute hypertable -SET ROLE :ROLE_CLUSTER_SUPERUSER; - -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2')) v(name) -) a; - -GRANT USAGE - ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2 - TO :ROLE_1, :ROLE_DEFAULT_PERM_USER; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_1; - -SET ROLE :ROLE_1; -CREATE TABLE disttable (time timestamptz, device int, temp float, color text); -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device'); -INSERT INTO disttable VALUES ('2018-01-01 05:00:00-8', 1, 23.4, 'green'), - ('2018-01-01 06:00:00-8', 4, 22.3, NULL), - ('2018-01-01 06:00:00-8', 1, 21.1, 'green'); - --- Make sure we get deterministic behavior across all nodes -CALL distributed_exec($$ SELECT setseed(1); $$); - --- No stats on the local table -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - --- Run ANALYZE on data node 1 -CALL distributed_exec('ANALYZE disttable', ARRAY[:'DATA_NODE_1']); - --- Stats should now be refreshed after running get_chunk_{col,rel}stats -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; - -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - --- Test that user without table permissions can't get column stats -SET ROLE :ROLE_DEFAULT_PERM_USER; -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); -SET ROLE :ROLE_1; - --- Run ANALYZE again, but on both nodes. -ANALYZE disttable; - --- Now expect stats from all data node chunks -SELECT * FROM _timescaledb_functions.get_chunk_relstats('disttable'); -SELECT * FROM _timescaledb_functions.get_chunk_colstats('disttable'); - --- Test ANALYZE with a replica chunk. We'd like to ensure the --- stats-fetching functions handle duplicate stats from different (but --- identical) replica chunks. -SELECT set_replication_factor('disttable', 2); -INSERT INTO disttable VALUES ('2019-01-01 05:00:00-8', 1, 23.4, 'green'); --- Run twice to test that stats-fetching functions handle replica chunks. -ANALYZE disttable; -ANALYZE disttable; - -SELECT relname, reltuples, relpages, relallvisible FROM pg_class WHERE relname IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY relname; -SELECT * FROM pg_stats WHERE tablename IN -(SELECT (_timescaledb_functions.show_chunk(show_chunks)).table_name - FROM show_chunks('disttable')) -ORDER BY 1,2,3; - --- Check underlying pg_statistics table (looking at all columns except --- starelid, which changes depending on how many tests are run before --- this) -RESET ROLE; -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum; - -SELECT test.remote_exec(NULL, $$ -SELECT ch, staattnum, stainherit, stanullfrac, stawidth, stadistinct, stakind1, stakind2, stakind3, stakind4, stakind5, staop1, staop2, staop3, staop4, staop5, -stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5, stavalues1, stavalues2, stavalues3, stavalues4, stavalues5 -FROM pg_statistic st, show_chunks('disttable') ch -WHERE st.starelid = ch -ORDER BY ch, staattnum; -$$); - --- Clean up -RESET ROLE; -TRUNCATE disttable; -SELECT * FROM delete_data_node(:'DATA_NODE_1', force => true); -SELECT * FROM delete_data_node(:'DATA_NODE_2', force => true); -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); - -- Test create_chunk_table to recreate the chunk table and show dimension slices SET ROLE :ROLE_DEFAULT_PERM_USER; @@ -517,10 +393,3 @@ SELECT * FROM chunkapi ORDER BY 1,2,3; SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); DROP TABLE chunkapi; - -\c :TEST_DBNAME :ROLE_SUPERUSER -SET client_min_messages = ERROR; -DROP TABLESPACE tablespace1; -DROP TABLESPACE tablespace2; -SET client_min_messages = NOTICE; -\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER diff --git a/tsl/test/sql/chunk_utils_internal.sql b/tsl/test/sql/chunk_utils_internal.sql index 4c90381e7d8..82458babcd3 100644 --- a/tsl/test/sql/chunk_utils_internal.sql +++ b/tsl/test/sql/chunk_utils_internal.sql @@ -439,34 +439,6 @@ WHERE hypertable_id IN (SELECT id from _timescaledb_catalog.hypertable WHERE table_name = 'ht_try') ORDER BY table_name; --- TEST error try freeze/unfreeze on dist hypertable --- Add distributed hypertables -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\c :TEST_DBNAME :ROLE_SUPERUSER - -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2')) v(name) -) a; - -CREATE TABLE disthyper (timec timestamp, device integer); -SELECT create_distributed_hypertable('disthyper', 'timec', 'device'); -INSERT into disthyper VALUES ('2020-01-01', 10); - ---freeze one of the chunks -SELECT chunk_schema || '.' || chunk_name as "CHNAME3" -FROM timescaledb_information.chunks -WHERE hypertable_name = 'disthyper' -ORDER BY chunk_name LIMIT 1 -\gset - -\set ON_ERROR_STOP 0 -SELECT _timescaledb_functions.freeze_chunk( :'CHNAME3'); -SELECT _timescaledb_functions.unfreeze_chunk( :'CHNAME3'); -\set ON_ERROR_STOP 1 - -- TEST can create OSM chunk if there are constraints on the hypertable \c :TEST_DBNAME :ROLE_4 CREATE TABLE measure( id integer PRIMARY KEY, mname varchar(10)); @@ -767,6 +739,3 @@ INSERT INTO osm_slice_update VALUES (1); -- clean up databases created \c :TEST_DBNAME :ROLE_SUPERUSER DROP DATABASE postgres_fdw_db WITH (FORCE); -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); - diff --git a/tsl/test/sql/exp_cagg_monthly.sql b/tsl/test/sql/exp_cagg_monthly.sql index 894e8e94b87..60a4ab4222d 100644 --- a/tsl/test/sql/exp_cagg_monthly.sql +++ b/tsl/test/sql/exp_cagg_monthly.sql @@ -407,108 +407,6 @@ SELECT * FROM conditions_large_1y ORDER BY bucket; RESET timescaledb.materializations_per_refresh_window; --- Test caggs with monthly buckets on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 - -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; - -CREATE TABLE conditions_dist( - day DATE NOT NULL, - temperature INT NOT NULL); - -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01' :: date, '2010-03-01' :: date - interval '1 day', '1 day') as ts; - -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day) AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; - -SELECT mat_hypertable_id AS cagg_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset - -SELECT raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset - -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - -SELECT * FROM conditions_dist_1m ORDER BY bucket; - --- Same test but with non-realtime, NO DATA aggregate and manual refresh - -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day) AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-03-01'); -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - --- Check invalidation for caggs on top of distributed hypertable - -INSERT INTO conditions_dist(day, temperature) -VALUES ('2010-01-15', 999), ('2010-02-15', -999), ('2010-03-01', 15); - -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - --- Clean up -DROP TABLE conditions_dist CASCADE; - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); - -- Test the specific code path of creating a CAGG on top of empty hypertable. CREATE TABLE conditions_empty( diff --git a/tsl/test/sql/exp_cagg_origin.sql b/tsl/test/sql/exp_cagg_origin.sql index ff4593c99b1..2c1be16e3b9 100644 --- a/tsl/test/sql/exp_cagg_origin.sql +++ b/tsl/test/sql/exp_cagg_origin.sql @@ -254,107 +254,6 @@ ORDER BY month, city; -- Clean up DROP TABLE conditions CASCADE; --- Test caggs with monthly buckets and custom origin on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 - -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; - -CREATE TABLE conditions_dist( - day date NOT NULL, - temperature INT NOT NULL); - -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01' :: date, '2010-03-01' :: date - interval '1 day', '1 day') as ts; - -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, '2010-01-01') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; - -SELECT mat_hypertable_id AS cagg_id, raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset - -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - -SELECT * FROM conditions_dist_1m ORDER BY bucket; - --- Same test but with non-realtime, NO DATA aggregate and manual refresh - -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, '2005-01-01') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-03-01'); -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - --- Check invalidation for caggs on top of distributed hypertable - -INSERT INTO conditions_dist(day, temperature) -VALUES ('2010-01-15', 999), ('2010-02-15', -999), ('2010-03-01', 15); - -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01', '2010-04-01'); -SELECT * FROM conditions_dist_1m ORDER BY bucket; -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - --- Compression on top of distributed hypertables - -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); - -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; - -SELECT * FROM conditions_dist_1m_manual ORDER BY bucket; - --- Clean up -DROP TABLE conditions_dist CASCADE; - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -SELECT delete_data_node(name) -FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v (name); -SET ROLE :ROLE_DEFAULT_PERM_USER; - -- Test the specific code path of creating a CAGG on top of empty hypertable. CREATE TABLE conditions_empty( @@ -658,9 +557,3 @@ SELECT add_continuous_aggregate_policy('conditions_summary_timestamptz', -- Clean up DROP TABLE conditions_timestamptz CASCADE; - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); - diff --git a/tsl/test/sql/exp_cagg_timezone.sql b/tsl/test/sql/exp_cagg_timezone.sql index 723d9160e90..88886c07e3b 100644 --- a/tsl/test/sql/exp_cagg_timezone.sql +++ b/tsl/test/sql/exp_cagg_timezone.sql @@ -415,140 +415,6 @@ SELECT city, to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as mont FROM conditions2_summary ORDER by month, city; --- Test caggs with monthly buckets on top of distributed hypertable -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 -\set DATA_NODE_3 :TEST_DBNAME _3 - -SELECT node_name, database, node_created, database_created, extension_created -FROM ( - SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).* - FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name) -) a; - -GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC; --- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes -GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER; -SET ROLE :ROLE_DEFAULT_PERM_USER; - -CREATE TABLE conditions_dist( - day timestamptz NOT NULL, - temperature INT NOT NULL); - -SELECT table_name FROM create_distributed_hypertable('conditions_dist', 'day', chunk_time_interval => INTERVAL '1 day'); - -INSERT INTO conditions_dist(day, temperature) -SELECT ts, date_part('month', ts)*100 + date_part('day', ts) -FROM generate_series('2010-01-01 00:00:00 MSK' :: timestamptz, '2010-03-01 00:00:00 MSK' :: timestamptz - interval '1 day', '1 day') as ts; - -CREATE MATERIALIZED VIEW conditions_dist_1m -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, 'MSK') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket; - -SELECT mat_hypertable_id AS cagg_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset - -SELECT raw_hypertable_id AS ht_id -FROM _timescaledb_catalog.continuous_agg -WHERE user_view_name = 'conditions_dist_1m' -\gset - -SELECT bucket_width -FROM _timescaledb_catalog.continuous_agg -WHERE mat_hypertable_id = :cagg_id; - -SELECT experimental, name, bucket_width, origin, timezone -FROM _timescaledb_catalog.continuous_aggs_bucket_function -WHERE mat_hypertable_id = :cagg_id; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - --- Same test but with non-realtime, NO DATA aggregate and manual refresh - -CREATE MATERIALIZED VIEW conditions_dist_1m_manual -WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS -SELECT - timescaledb_experimental.time_bucket_ng('1 month', day, 'MSK') AS bucket, - MIN(temperature), - MAX(temperature) -FROM conditions_dist -GROUP BY bucket -WITH NO DATA; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01 00:00:00 MSK', '2010-03-01 00:00:00 MSK'); - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - --- Check invalidation for caggs on top of distributed hypertable - -INSERT INTO conditions_dist(day, temperature) VALUES -('2010-01-15 00:00:00 MSK', 999), -('2010-02-15 00:00:00 MSK', -999), -('2010-03-01 00:00:00 MSK', 15); - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - -CALL refresh_continuous_aggregate('conditions_dist_1m', '2010-01-01 00:00:00 MSK', '2010-04-01 00:00:00 MSK'); - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - -CALL refresh_continuous_aggregate('conditions_dist_1m_manual', '2010-01-01 00:00:00 MSK', '2010-04-01 00:00:00 MSK'); - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m -ORDER BY month; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - --- Check compatibility with compressed distributed hypertables - -ALTER MATERIALIZED VIEW conditions_dist_1m_manual SET ( timescaledb.compress ); - -SELECT compress_chunk(ch) -FROM show_chunks('conditions_dist_1m_manual') ch limit 1; - -SELECT to_char(bucket at time zone 'MSK', 'YYYY-MM-DD HH24:MI:SS') as month, min, max -FROM conditions_dist_1m_manual -ORDER BY month; - --- Clean up -DROP TABLE conditions_dist CASCADE; - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); -DROP DATABASE :DATA_NODE_3 WITH (FORCE); - -- Make sure add_continuous_aggregate_policy() works CREATE TABLE conditions_policy( diff --git a/tsl/test/sql/read_only.sql b/tsl/test/sql/read_only.sql index 795828d0eab..4c0eec46629 100644 --- a/tsl/test/sql/read_only.sql +++ b/tsl/test/sql/read_only.sql @@ -8,9 +8,6 @@ -- properly recognize read-only transaction state -- -\set DATA_NODE_1 :TEST_DBNAME _1 -\set DATA_NODE_2 :TEST_DBNAME _2 - -- create_hypertable() -- CREATE TABLE test_table(time bigint NOT NULL, device int); @@ -96,76 +93,6 @@ DROP TABLE test_table; SET default_transaction_read_only TO off; DROP TABLE test_table; --- data nodes --- -CREATE TABLE disttable(time timestamptz NOT NULL, device int); - --- add_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM add_data_node(:'DATA_NODE_1', host => 'localhost', database => :'DATA_NODE_1'); -\set ON_ERROR_STOP 1 - -SET default_transaction_read_only TO off; -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node(:'DATA_NODE_1', host => 'localhost', database => :'DATA_NODE_1'); -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node(:'DATA_NODE_2', host => 'localhost', database => :'DATA_NODE_2'); - --- create_distributed_hypertable() --- -SET default_transaction_read_only TO on; - -\set ON_ERROR_STOP 0 -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device', data_nodes => ARRAY[:'DATA_NODE_1']); -\set ON_ERROR_STOP 1 - -SET default_transaction_read_only TO off; -SELECT * FROM create_distributed_hypertable('disttable', 'time', 'device', data_nodes => ARRAY[:'DATA_NODE_1']); - --- attach_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM attach_data_node(:'DATA_NODE_2', 'disttable'); -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM attach_data_node(:'DATA_NODE_2', 'disttable'); - --- detach_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM detach_data_node(:'DATA_NODE_2', 'disttable'); -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM detach_data_node(:'DATA_NODE_2', 'disttable'); - --- delete_data_node() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM delete_data_node(:'DATA_NODE_2'); -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -SELECT * FROM delete_data_node(:'DATA_NODE_2'); - --- set_replication_factor() --- -SET default_transaction_read_only TO on; -\set ON_ERROR_STOP 0 -SELECT * FROM set_replication_factor('disttable', 2); -\set ON_ERROR_STOP 1 - --- drop distributed hypertable --- -\set ON_ERROR_STOP 0 -DROP TABLE disttable; -\set ON_ERROR_STOP 1 -SET default_transaction_read_only TO off; -DROP TABLE disttable; - -- Test some read-only cases of DDL operations -- CREATE TABLE test_table(time bigint NOT NULL, device int); @@ -286,8 +213,3 @@ SELECT remove_retention_policy('test_table'); SELECT add_job('now','12h'); SELECT alter_job(1,scheduled:=false); SELECT delete_job(1); - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DATA_NODE_1 WITH (FORCE); -DROP DATABASE :DATA_NODE_2 WITH (FORCE); - diff --git a/tsl/test/sql/telemetry_stats.sql.in b/tsl/test/sql/telemetry_stats.sql similarity index 73% rename from tsl/test/sql/telemetry_stats.sql.in rename to tsl/test/sql/telemetry_stats.sql index c48cd91a485..0b7e35776dd 100644 --- a/tsl/test/sql/telemetry_stats.sql.in +++ b/tsl/test/sql/telemetry_stats.sql @@ -122,128 +122,6 @@ ANALYZE normal, hyper, part; REFRESH MATERIALIZED VIEW telemetry_report; SELECT jsonb_pretty(rels) AS relations FROM relations; --- Add distributed hypertables -\set DN_DBNAME_1 :TEST_DBNAME _1 -\set DN_DBNAME_2 :TEST_DBNAME _2 - --- Not an access node or data node -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - --- Become an access node by adding a data node -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_1', host => 'localhost', database => :'DN_DBNAME_1'); - --- Telemetry should show one data node and "acces node" status -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT r -> 'num_data_nodes' AS num_data_nodes, - r -> 'distributed_member' AS distributed_member -FROM telemetry_report; - --- See telemetry report from a data node -\ir include/remote_exec.sql -SELECT test.remote_exec(NULL, $$ - SELECT t -> 'num_data_nodes' AS num_data_nodes, - t -> 'distributed_member' AS distributed_member - FROM get_telemetry_report() t; -$$); - -SELECT node_name, database, node_created, database_created, extension_created -FROM add_data_node('data_node_2', host => 'localhost', database => :'DN_DBNAME_2'); -CREATE TABLE disthyper (LIKE normal); -SELECT create_distributed_hypertable('disthyper', 'time', 'device'); - --- Show distributed hypertables stats with no data -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - --- No datanode-related stats on the access node -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn -FROM relations; - --- Insert data into the distributed hypertable -INSERT INTO disthyper -SELECT * FROM normal; - --- Update telemetry stats and show output on access node and data --- nodes. Note that the access node doesn't store data so shows --- zero. It should have stats from ANALYZE, though, like --- num_reltuples. -ANALYZE disthyper; -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); - --- Add compression -ALTER TABLE disthyper SET (timescaledb.compress); -SELECT compress_chunk(c) -FROM show_chunks('disthyper') c ORDER BY c LIMIT 4; - -ANALYZE disthyper; --- Update telemetry stats and show updated compression stats -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - --- Show data node stats -SELECT test.remote_exec(NULL, $$ - SELECT - jsonb_pretty(t -> 'relations' -> 'distributed_hypertables_data_node') AS distributed_hypertables_dn - FROM get_telemetry_report() t; -$$); - --- Create a replicated distributed hypertable and show replication stats -CREATE TABLE disthyper_repl (LIKE normal); -SELECT create_distributed_hypertable('disthyper_repl', 'time', 'device', replication_factor => 2); -INSERT INTO disthyper_repl -SELECT * FROM normal; - -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'distributed_hypertables_access_node') AS distributed_hypertables_an -FROM relations; - --- Create a continuous aggregate on the distributed hypertable -CREATE MATERIALIZED VIEW distcontagg -WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; - -CREATE MATERIALIZED VIEW distcontagg_old -WITH (timescaledb.continuous, timescaledb.materialized_only=false, timescaledb.finalized=false) AS -SELECT - time_bucket('1 hour', time) AS hour, - device, - min(time) -FROM - disthyper -GROUP BY hour, device; - -VACUUM; - -REFRESH MATERIALIZED VIEW telemetry_report; -SELECT - jsonb_pretty(rels -> 'continuous_aggregates') AS continuous_aggregates -FROM relations; - -- check telemetry for fixed schedule jobs works create or replace procedure job_test_fixed(jobid int, config jsonb) language plpgsql as $$ begin @@ -353,8 +231,3 @@ SELECT jsonb_pretty(get_telemetry_report() -> 'relations' -> 'continuous_aggrega DROP VIEW relations; DROP MATERIALIZED VIEW telemetry_report; - -\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER -DROP DATABASE :DN_DBNAME_1 WITH (FORCE); -DROP DATABASE :DN_DBNAME_2 WITH (FORCE); - diff --git a/tsl/test/src/remote/remote_exec.c b/tsl/test/src/remote/remote_exec.c index 89a3dc2f4b1..405351fcd38 100644 --- a/tsl/test/src/remote/remote_exec.c +++ b/tsl/test/src/remote/remote_exec.c @@ -164,6 +164,13 @@ extern List *hypertable_data_node_array_to_list(ArrayType *serverarr); Datum ts_remote_exec(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("executing remote command is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + ArrayType *data_nodes = PG_ARGISNULL(0) ? NULL : PG_GETARG_ARRAYTYPE_P(0); char *sql = TextDatumGetCString(PG_GETARG_DATUM(1)); List *data_node_list; @@ -202,6 +209,13 @@ ts_remote_exec(PG_FUNCTION_ARGS) Datum ts_remote_exec_get_result_strings(PG_FUNCTION_ARGS) { +#if PG16_GE + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("getting results from remote command execution is not supported"), + errhint("Multi-node is not supported anymore on PostgreSQL >= 16."))); +#endif + ArrayType *data_nodes = PG_ARGISNULL(0) ? NULL : PG_GETARG_ARRAYTYPE_P(0); char *sql = TextDatumGetCString(PG_GETARG_DATUM(1)); List *data_node_list = NIL;