diff --git a/sql/ddl_internal.sql b/sql/ddl_internal.sql index 8d8ca0f13..20243d54f 100644 --- a/sql/ddl_internal.sql +++ b/sql/ddl_internal.sql @@ -8,3 +8,8 @@ AS '@MODULE_PATHNAME@', 'ts_chunk_index_clone' LANGUAGE C VOLATILE STRICT; CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_index_replace(chunk_index_oid_old OID, chunk_index_oid_new OID) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_chunk_index_replace' LANGUAGE C VOLATILE STRICT; + +CREATE OR REPLACE FUNCTION _timescaledb_internal.create_chunk_replica_table( + chunk REGCLASS, + data_node_name NAME +) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_chunk_create_replica_table' LANGUAGE C VOLATILE; diff --git a/sql/updates/reverse-dev.sql b/sql/updates/reverse-dev.sql index 2565e069d..889471a1c 100644 --- a/sql/updates/reverse-dev.sql +++ b/sql/updates/reverse-dev.sql @@ -3,6 +3,7 @@ DROP FUNCTION IF EXISTS _timescaledb_internal.block_new_chunks; DROP FUNCTION IF EXISTS _timescaledb_internal.allow_new_chunks; DROP FUNCTION IF EXISTS _timescaledb_internal.refresh_continuous_aggregate; DROP FUNCTION IF EXISTS _timescaledb_internal.create_chunk_table; +DROP FUNCTION IF EXISTS _timescaledb_internal.create_chunk_replica_table; -- We need to rewrite all continuous aggregates to make sure that the -- queries do not contain qualification. They will be re-written in diff --git a/src/cross_module_fn.c b/src/cross_module_fn.c index 84376b8a5..3e8c85b1b 100644 --- a/src/cross_module_fn.c +++ b/src/cross_module_fn.c @@ -84,6 +84,7 @@ CROSSMODULE_WRAPPER(chunk_set_default_data_node); CROSSMODULE_WRAPPER(chunk_get_relstats); CROSSMODULE_WRAPPER(chunk_get_colstats); CROSSMODULE_WRAPPER(chunk_create_empty_table); +CROSSMODULE_WRAPPER(chunk_create_replica_table); CROSSMODULE_WRAPPER(timescaledb_fdw_handler); CROSSMODULE_WRAPPER(timescaledb_fdw_validator); @@ -401,6 +402,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = { .chunk_get_relstats = error_no_default_fn_pg_community, .chunk_get_colstats = error_no_default_fn_pg_community, .chunk_create_empty_table = error_no_default_fn_pg_community, + .chunk_create_replica_table = error_no_default_fn_pg_community, .hypertable_distributed_set_replication_factor = error_no_default_fn_pg_community, .update_compressed_chunk_relstats = update_compressed_chunk_relstats_default, }; diff --git a/src/cross_module_fn.h b/src/cross_module_fn.h index db563238c..804a17899 100644 --- a/src/cross_module_fn.h +++ b/src/cross_module_fn.h @@ -161,6 +161,7 @@ typedef struct CrossModuleFunctions PGFunction chunk_get_colstats; PGFunction hypertable_distributed_set_replication_factor; PGFunction chunk_create_empty_table; + PGFunction chunk_create_replica_table; void (*update_compressed_chunk_relstats)(Oid uncompressed_relid, Oid compressed_relid); CompressSingleRowState *(*compress_row_init)(int srcht_id, Relation in_rel, Relation out_rel); TupleTableSlot *(*compress_row_exec)(CompressSingleRowState *cr, TupleTableSlot *slot); diff --git a/tsl/src/chunk.c b/tsl/src/chunk.c index 32b98eaa5..07b2ffd34 100644 --- a/tsl/src/chunk.c +++ b/tsl/src/chunk.c @@ -32,12 +32,35 @@ #include #include #include +#include +#include #include "chunk.h" +#include "chunk_api.h" #include "data_node.h" #include "deparse.h" #include "remote/dist_commands.h" +static bool +chunk_match_data_node_by_server(const Chunk *chunk, const ForeignServer *server) +{ + bool server_found = false; + ListCell *lc; + + foreach (lc, chunk->data_nodes) + { + ChunkDataNode *cdn = lfirst(lc); + + if (cdn->foreign_server_oid == server->serverid) + { + server_found = true; + break; + } + } + + return server_found; +} + static bool chunk_set_foreign_server(Chunk *chunk, ForeignServer *new_server) { @@ -49,21 +72,8 @@ chunk_set_foreign_server(Chunk *chunk, ForeignServer *new_server) CatalogSecurityContext sec_ctx; Oid old_server_id; long updated; - ListCell *lc; - bool new_server_found = false; - foreach (lc, chunk->data_nodes) - { - ChunkDataNode *cdn = lfirst(lc); - - if (cdn->foreign_server_oid == new_server->serverid) - { - new_server_found = true; - break; - } - } - - if (!new_server_found) + if (!chunk_match_data_node_by_server(chunk, new_server)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("chunk \"%s\" does not exist on data node \"%s\"", @@ -259,3 +269,65 @@ chunk_invoke_drop_chunks(Oid relid, Datum older_than, Datum older_than_type) return num_results; } + +static bool +chunk_is_distributed(const Chunk *chunk) +{ + return chunk->relkind == RELKIND_FOREIGN_TABLE; +} + +Datum +chunk_create_replica_table(PG_FUNCTION_ARGS) +{ + Oid chunk_relid; + const char *data_node_name; + const Chunk *chunk; + const Hypertable *ht; + const ForeignServer *server; + Cache *hcache = ts_hypertable_cache_pin(); + + TS_PREVENT_FUNC_IF_READ_ONLY(); + + GETARG_NOTNULL_OID(chunk_relid, 0, "chunk"); + GETARG_NOTNULL_NULLABLE(data_node_name, 1, "data node name", CSTRING); + + chunk = ts_chunk_get_by_relid(chunk_relid, false); + if (chunk == NULL) + { + const char *rel_name = get_rel_name(chunk_relid); + if (rel_name == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("oid \"%u\" is not a chunk", chunk_relid))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("relation \"%s\" is not a chunk", rel_name))); + } + if (!chunk_is_distributed(chunk)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("chunk \"%s\" doesn't belong to a distributed hypertable", + get_rel_name(chunk_relid)))); + + ht = ts_hypertable_cache_get_entry(hcache, chunk->hypertable_relid, CACHE_FLAG_NONE); + ts_hypertable_permissions_check(ht->main_table_relid, GetUserId()); + + /* Check the given data node exists */ + server = data_node_get_foreign_server(data_node_name, ACL_USAGE, true, false); + /* Find if hypertable is attached to the data node and return an error otherwise */ + data_node_hypertable_get_by_node_name(ht, data_node_name, true); + + if (chunk_match_data_node_by_server(chunk, server)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("chunk \"%s\" already exists on data node \"%s\"", + get_rel_name(chunk_relid), + data_node_name))); + + chunk_api_call_create_empty_chunk_table(ht, chunk, data_node_name); + + ts_cache_release(hcache); + + PG_RETURN_VOID(); +} diff --git a/tsl/src/chunk.h b/tsl/src/chunk.h index 82afcf810..4cd08a13a 100644 --- a/tsl/src/chunk.h +++ b/tsl/src/chunk.h @@ -14,5 +14,6 @@ extern void chunk_update_foreign_server_if_needed(int32 chunk_id, Oid existing_server_id); extern Datum chunk_set_default_data_node(PG_FUNCTION_ARGS); extern int chunk_invoke_drop_chunks(Oid relid, Datum older_than, Datum older_than_type); +extern Datum chunk_create_replica_table(PG_FUNCTION_ARGS); #endif /* TIMESCALEDB_TSL_CHUNK_H */ diff --git a/tsl/src/chunk_api.c b/tsl/src/chunk_api.c index 725893681..7f967e368 100644 --- a/tsl/src/chunk_api.c +++ b/tsl/src/chunk_api.c @@ -27,12 +27,10 @@ #include #include -#include #include #include #include #include -#include #include #include "remote/async.h" @@ -1664,3 +1662,24 @@ chunk_create_empty_table(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } + +#define CREATE_CHUNK_TABLE_NAME "create_chunk_table" + +void +chunk_api_call_create_empty_chunk_table(const Hypertable *ht, const Chunk *chunk, + const char *node_name) +{ + const char *create_cmd = + psprintf("SELECT %s.%s($1, $2, $3, $4)", INTERNAL_SCHEMA_NAME, CREATE_CHUNK_TABLE_NAME); + const char *params[4] = { quote_qualified_identifier(NameStr(ht->fd.schema_name), + NameStr(ht->fd.table_name)), + chunk_api_dimension_slices_json(chunk, ht), + NameStr(chunk->fd.schema_name), + NameStr(chunk->fd.table_name) }; + + ts_dist_cmd_close_response( + ts_dist_cmd_params_invoke_on_data_nodes(create_cmd, + stmt_params_create_from_values(params, 4), + list_make1((void *) node_name), + true)); +} diff --git a/tsl/src/chunk_api.h b/tsl/src/chunk_api.h index 895565fce..3f615babd 100644 --- a/tsl/src/chunk_api.h +++ b/tsl/src/chunk_api.h @@ -10,6 +10,8 @@ #include +#include "hypertable_data_node.h" + extern Datum chunk_show(PG_FUNCTION_ARGS); extern Datum chunk_create(PG_FUNCTION_ARGS); extern void chunk_api_create_on_data_nodes(const Chunk *chunk, const Hypertable *ht); @@ -17,5 +19,7 @@ extern Datum chunk_api_get_chunk_relstats(PG_FUNCTION_ARGS); extern Datum chunk_api_get_chunk_colstats(PG_FUNCTION_ARGS); extern void chunk_api_update_distributed_hypertable_stats(Oid relid); extern Datum chunk_create_empty_table(PG_FUNCTION_ARGS); +extern void chunk_api_call_create_empty_chunk_table(const Hypertable *ht, const Chunk *chunk, + const char *node_name); #endif /* TIMESCALEDB_TSL_CHUNK_API_H */ diff --git a/tsl/src/data_node.c b/tsl/src/data_node.c index a431e411d..be88ba8d3 100644 --- a/tsl/src/data_node.c +++ b/tsl/src/data_node.c @@ -40,7 +40,6 @@ #include "remote/connection_cache.h" #include "data_node.h" #include "remote/utils.h" -#include "hypertable.h" #include "hypertable_cache.h" #include "errors.h" #include "dist_util.h" @@ -1158,16 +1157,18 @@ data_node_detach_hypertable_data_nodes(const char *node_name, List *hypertable_d repartition); } -static HypertableDataNode * -get_hypertable_data_node(Oid table_id, const char *node_name, bool owner_check, bool attach_check) +HypertableDataNode * +data_node_hypertable_get_by_node_name(const Hypertable *ht, const char *node_name, + bool attach_check) { HypertableDataNode *hdn = NULL; - Cache *hcache = ts_hypertable_cache_pin(); - Hypertable *ht = ts_hypertable_cache_get_entry(hcache, table_id, CACHE_FLAG_NONE); ListCell *lc; - if (owner_check) - ts_hypertable_permissions_check(table_id, GetUserId()); + if (!hypertable_is_distributed(ht)) + ereport(ERROR, + (errcode(ERRCODE_TS_HYPERTABLE_NOT_DISTRIBUTED), + errmsg("hypertable \"%s\" is not distributed", + get_rel_name(ht->main_table_relid)))); foreach (lc, ht->data_nodes) { @@ -1185,16 +1186,31 @@ get_hypertable_data_node(Oid table_id, const char *node_name, bool owner_check, (errcode(ERRCODE_TS_DATA_NODE_NOT_ATTACHED), errmsg("data node \"%s\" is not attached to hypertable \"%s\"", node_name, - get_rel_name(table_id)))); + get_rel_name(ht->main_table_relid)))); else ereport(NOTICE, (errcode(ERRCODE_TS_DATA_NODE_NOT_ATTACHED), errmsg("data node \"%s\" is not attached to hypertable \"%s\", " "skipping", node_name, - get_rel_name(table_id)))); + get_rel_name(ht->main_table_relid)))); } + return hdn; +} + +static HypertableDataNode * +get_hypertable_data_node(Oid table_id, const char *node_name, bool owner_check, bool attach_check) +{ + HypertableDataNode *hdn = NULL; + Cache *hcache = ts_hypertable_cache_pin(); + const Hypertable *ht = ts_hypertable_cache_get_entry(hcache, table_id, CACHE_FLAG_NONE); + + if (owner_check) + ts_hypertable_permissions_check(table_id, GetUserId()); + + hdn = data_node_hypertable_get_by_node_name(ht, node_name, attach_check); + ts_cache_release(hcache); return hdn; diff --git a/tsl/src/data_node.h b/tsl/src/data_node.h index e9c44c28f..22cca26d4 100644 --- a/tsl/src/data_node.h +++ b/tsl/src/data_node.h @@ -8,7 +8,10 @@ #include +#include + #include "catalog.h" +#include "hypertable.h" #include "remote/dist_txn.h" /* Used to skip ACL checks */ @@ -39,6 +42,10 @@ extern List *data_node_oids_to_node_name_list(List *data_node_oids, AclMode mode extern void data_node_name_list_check_acl(List *data_node_names, AclMode mode); extern Datum data_node_ping(PG_FUNCTION_ARGS); +extern HypertableDataNode *data_node_hypertable_get_by_node_name(const Hypertable *ht, + const char *node_name, + bool attach_check); + /* This should only be used for testing */ extern Datum data_node_add_without_dist_id(PG_FUNCTION_ARGS); diff --git a/tsl/src/init.c b/tsl/src/init.c index 2d7a2af23..7f72d3969 100644 --- a/tsl/src/init.c +++ b/tsl/src/init.c @@ -192,6 +192,7 @@ CrossModuleFunctions tsl_cm_functions = { .chunk_get_relstats = chunk_api_get_chunk_relstats, .chunk_get_colstats = chunk_api_get_chunk_colstats, .chunk_create_empty_table = chunk_create_empty_table, + .chunk_create_replica_table = chunk_create_replica_table, .hypertable_distributed_set_replication_factor = hypertable_set_replication_factor, .cache_syscache_invalidate = cache_syscache_invalidate, .update_compressed_chunk_relstats = update_compressed_chunk_relstats, diff --git a/tsl/src/remote/dist_commands.c b/tsl/src/remote/dist_commands.c index 8d3d2e0dd..26e002e09 100644 --- a/tsl/src/remote/dist_commands.c +++ b/tsl/src/remote/dist_commands.c @@ -15,7 +15,6 @@ #include "dist_commands.h" #include "dist_txn.h" #include "connection_cache.h" -#include "async.h" #include "data_node.h" #include "dist_util.h" #include "miscadmin.h" @@ -81,7 +80,8 @@ ts_dist_cmd_collect_responses(List *requests) * server OIDs. */ DistCmdResult * -ts_dist_cmd_invoke_on_data_nodes(const char *sql, List *data_nodes, bool transactional) +ts_dist_cmd_params_invoke_on_data_nodes(const char *sql, StmtParams *params, List *data_nodes, + bool transactional) { ListCell *lc; List *requests = NIL; @@ -113,7 +113,11 @@ ts_dist_cmd_invoke_on_data_nodes(const char *sql, List *data_nodes, bool transac ereport(DEBUG2, (errmsg_internal("sending \"%s\" to data node \"%s\"", sql, node_name))); - req = async_request_send(connection, sql); + if (params == NULL) + req = async_request_send(connection, sql); + else + req = async_request_send_with_params(connection, sql, params, FORMAT_TEXT); + async_request_attach_user_data(req, (char *) node_name); requests = lappend(requests, req); } @@ -125,6 +129,12 @@ ts_dist_cmd_invoke_on_data_nodes(const char *sql, List *data_nodes, bool transac return results; } +DistCmdResult * +ts_dist_cmd_invoke_on_data_nodes(const char *sql, List *data_nodes, bool transactional) +{ + return ts_dist_cmd_params_invoke_on_data_nodes(sql, NULL, data_nodes, transactional); +} + DistCmdResult * ts_dist_cmd_invoke_on_data_nodes_using_search_path(const char *sql, const char *search_path, List *node_names, bool transactional) diff --git a/tsl/src/remote/dist_commands.h b/tsl/src/remote/dist_commands.h index b8e70a89f..26198c55c 100644 --- a/tsl/src/remote/dist_commands.h +++ b/tsl/src/remote/dist_commands.h @@ -7,13 +7,16 @@ #define TIMESCALEDB_TSL_REMOTE_DIST_COMMANDS_H #include -#include + +#include "async.h" typedef struct DistCmdResult DistCmdResult; typedef struct List PreparedDistCmd; extern DistCmdResult *ts_dist_cmd_invoke_on_data_nodes(const char *sql, List *node_names, bool transactional); +extern DistCmdResult *ts_dist_cmd_params_invoke_on_data_nodes(const char *sql, StmtParams *params, + List *data_nodes, bool transactional); extern DistCmdResult *ts_dist_cmd_invoke_on_data_nodes_using_search_path(const char *sql, const char *search_path, List *node_names, diff --git a/tsl/test/shared/expected/dist_chunk.out b/tsl/test/shared/expected/dist_chunk.out new file mode 100644 index 000000000..15d5a984e --- /dev/null +++ b/tsl/test/shared/expected/dist_chunk.out @@ -0,0 +1,114 @@ +-- 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 function _timescaledb_internal.create_chunk_replica_table +-- A table for the first chunk will be created on the data node, where it is not present. +SELECT chunk_name, data_nodes +FROM timescaledb_information.chunks +WHERE hypertable_name = 'dist_chunk_copy'; + chunk_name | data_nodes +-------------------------+--------------------------- + _dist_hyper_X_X_chunk | {data_node_1,data_node_2} + _dist_hyper_X_X_chunk | {data_node_2,data_node_3} + _dist_hyper_X_X_chunk | {data_node_1,data_node_3} + _dist_hyper_X_X_chunk | {data_node_1,data_node_2} + _dist_hyper_X_X_chunk | {data_node_2,data_node_3} +(5 rows) + +SELECT compress_chunk('_timescaledb_internal._dist_hyper_X_X_chunk'); + compress_chunk +----------------------------------------------- + _timescaledb_internal._dist_hyper_X_X_chunk +(1 row) + +SELECT compress_chunk('_timescaledb_internal._dist_hyper_X_X_chunk'); + compress_chunk +----------------------------------------------- + _timescaledb_internal._dist_hyper_X_X_chunk +(1 row) + +-- Non-distributed chunk will be used to test an error +SELECT chunk_name +FROM timescaledb_information.chunks +WHERE hypertable_name = 'conditions'; + chunk_name +-------------------- + _hyper_X_X_chunk + _hyper_X_X_chunk +(2 rows) + +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.create_chunk_replica_table(NULL, 'data_node_1'); +ERROR: chunk cannot be NULL +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', NULL); +ERROR: data node name cannot be NULL +SELECT _timescaledb_internal.create_chunk_replica_table(1234, 'data_node_1'); +ERROR: oid "1234" is not a chunk +SELECT _timescaledb_internal.create_chunk_replica_table('metrics_int', 'data_node_1'); +ERROR: relation "metrics_int" is not a chunk +SELECT _timescaledb_internal.create_chunk_replica_table('conditions', 'data_node_1'); +ERROR: relation "conditions" is not a chunk +SELECT _timescaledb_internal. create_chunk_replica_table('_timescaledb_internal._hyper_X_X_chunk', 'data_node_1'); +ERROR: chunk "_hyper_X_X_chunk" doesn't belong to a distributed hypertable +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_1'); +ERROR: chunk "_dist_hyper_X_X_chunk" already exists on data node "data_node_1" +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_1'); +ERROR: relation "_timescaledb_internal._dist_hyper_X_X_chunk" does not exist at character 57 +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_4'); +ERROR: server "data_node_4" does not exist +BEGIN READ ONLY; +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_3'); +ERROR: cannot execute create_chunk_replica_table() in a read-only transaction +COMMIT; +\set ON_ERROR_STOP 1 +\c data_node_3 +SELECT table_name +FROM information_schema.tables +WHERE table_schema = '_timescaledb_internal' AND + (table_name LIKE '_dist_hyper_15_%' OR table_name LIKE 'compress_hyper_5_%'); + table_name +-------------------------- + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + compress_hyper_X_X_chunk +(4 rows) + +\c :TEST_DBNAME +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_3'); + create_chunk_replica_table +---------------------------- + +(1 row) + +-- Test that the table cannot be created since it was already created on the data node +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_3'); +ERROR: [data_node_3]: relation "_dist_hyper_X_X_chunk" already exists +\set ON_ERROR_STOP 1 +-- Creating chunk replica table ignores compression now: +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_X_X_chunk', 'data_node_3'); + create_chunk_replica_table +---------------------------- + +(1 row) + +\c data_node_3 +SELECT table_name +FROM information_schema.tables +WHERE table_schema = '_timescaledb_internal' AND + (table_name LIKE '_dist_hyper_15_%' OR table_name LIKE 'compress_hyper_5_%'); + table_name +-------------------------- + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + _dist_hyper_X_X_chunk + compress_hyper_X_X_chunk +(6 rows) + +\c :TEST_DBNAME +DROP TABLE dist_chunk_copy; +CALL distributed_exec($$ DROP TABLE _timescaledb_internal._dist_hyper_X_X_chunk $$, '{"data_node_3"}'); +CALL distributed_exec($$ DROP TABLE _timescaledb_internal._dist_hyper_X_X_chunk $$, '{"data_node_3"}'); diff --git a/tsl/test/shared/sql/CMakeLists.txt b/tsl/test/shared/sql/CMakeLists.txt index 02e37e4d9..6bef45079 100644 --- a/tsl/test/shared/sql/CMakeLists.txt +++ b/tsl/test/shared/sql/CMakeLists.txt @@ -1,6 +1,10 @@ set(TEST_FILES_SHARED - constify_timestamptz_op_interval.sql constraint_exclusion_prepared.sql - decompress_placeholdervar.sql dist_gapfill.sql dist_insert.sql + constify_timestamptz_op_interval.sql + constraint_exclusion_prepared.sql + decompress_placeholdervar.sql + dist_chunk.sql + dist_gapfill.sql + dist_insert.sql dist_distinct.sql) if(CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/tsl/test/shared/sql/dist_chunk.sql b/tsl/test/shared/sql/dist_chunk.sql new file mode 100644 index 000000000..8735c1283 --- /dev/null +++ b/tsl/test/shared/sql/dist_chunk.sql @@ -0,0 +1,61 @@ +-- 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 function _timescaledb_internal.create_chunk_replica_table + +-- A table for the first chunk will be created on the data node, where it is not present. +SELECT chunk_name, data_nodes +FROM timescaledb_information.chunks +WHERE hypertable_name = 'dist_chunk_copy'; + +SELECT compress_chunk('_timescaledb_internal._dist_hyper_15_68_chunk'); +SELECT compress_chunk('_timescaledb_internal._dist_hyper_15_70_chunk'); + +-- Non-distributed chunk will be used to test an error +SELECT chunk_name +FROM timescaledb_information.chunks +WHERE hypertable_name = 'conditions'; + +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.create_chunk_replica_table(NULL, 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', NULL); +SELECT _timescaledb_internal.create_chunk_replica_table(1234, 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('metrics_int', 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('conditions', 'data_node_1'); +SELECT _timescaledb_internal. create_chunk_replica_table('_timescaledb_internal._hyper_10_51_chunk', 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_27_chunk', 'data_node_1'); +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', 'data_node_4'); +BEGIN READ ONLY; +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', 'data_node_3'); +COMMIT; +\set ON_ERROR_STOP 1 + +\c data_node_3 +SELECT table_name +FROM information_schema.tables +WHERE table_schema = '_timescaledb_internal' AND + (table_name LIKE '_dist_hyper_15_%' OR table_name LIKE 'compress_hyper_5_%'); +\c :TEST_DBNAME + +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', 'data_node_3'); + +-- Test that the table cannot be created since it was already created on the data node +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_67_chunk', 'data_node_3'); +\set ON_ERROR_STOP 1 + +-- Creating chunk replica table ignores compression now: +SELECT _timescaledb_internal.create_chunk_replica_table('_timescaledb_internal._dist_hyper_15_70_chunk', 'data_node_3'); + +\c data_node_3 +SELECT table_name +FROM information_schema.tables +WHERE table_schema = '_timescaledb_internal' AND + (table_name LIKE '_dist_hyper_15_%' OR table_name LIKE 'compress_hyper_5_%'); +\c :TEST_DBNAME + +DROP TABLE dist_chunk_copy; +CALL distributed_exec($$ DROP TABLE _timescaledb_internal._dist_hyper_15_67_chunk $$, '{"data_node_3"}'); +CALL distributed_exec($$ DROP TABLE _timescaledb_internal._dist_hyper_15_70_chunk $$, '{"data_node_3"}'); diff --git a/tsl/test/shared/sql/include/shared_setup.sql b/tsl/test/shared/sql/include/shared_setup.sql index 76d1d0fc0..6b1d711cf 100644 --- a/tsl/test/shared/sql/include/shared_setup.sql +++ b/tsl/test/shared/sql/include/shared_setup.sql @@ -213,3 +213,15 @@ INSERT INTO metrics_int_dist1 VALUES (5,1,2,10.0), (100,1,1,0.0), (100,1,2,-100.0); + +CREATE TABLE dist_chunk_copy ( + time timestamptz NOT NULL, + value integer); + +SELECT create_distributed_hypertable('dist_chunk_copy', 'time', replication_factor => 2); +ALTER TABLE dist_chunk_copy SET (timescaledb.compress); + +SELECT setseed(0); +INSERT INTO dist_chunk_copy +SELECT t, random() * 20 +FROM generate_series('2020-01-01'::timestamp, '2020-01-25'::timestamp, '1d') t;