mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 10:33:27 +08:00
To increase schema security we do not want to mix our own internal objects with user objects. Since chunks are created in the _timescaledb_internal schema our internal functions should live in a different dedicated schema. This patch make the necessary adjustments for the following functions: - chunk_constraint_add_table_constraint(_timescaledb_catalog.chunk_constraint) - chunk_drop_replica(regclass,name) - chunk_index_clone(oid) - chunk_index_replace(oid,oid) - create_chunk_replica_table(regclass,name) - drop_stale_chunks(name,integer[]) - health() - hypertable_constraint_add_table_fk_constraint(name,name,name,integer) - process_ddl_event() - wait_subscription_sync(name,name,integer,numeric)
286 lines
9.8 KiB
PL/PgSQL
286 lines
9.8 KiB
PL/PgSQL
-- 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.
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
|
|
-- Support for execute_sql_and_filter_server_name_on_error()
|
|
\unset ECHO
|
|
\o /dev/null
|
|
\ir include/remote_exec.sql
|
|
\ir include/filter_exec.sql
|
|
\o
|
|
\set ECHO all
|
|
|
|
\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;
|
|
|
|
\des
|
|
|
|
RESET ROLE;
|
|
CREATE FUNCTION _timescaledb_internal.invoke_distributed_commands()
|
|
RETURNS void
|
|
AS :TSL_MODULE_PATHNAME, 'ts_invoke_distributed_commands'
|
|
LANGUAGE C STRICT;
|
|
|
|
CREATE FUNCTION _timescaledb_internal.invoke_faulty_distributed_command()
|
|
RETURNS void
|
|
AS :TSL_MODULE_PATHNAME, 'ts_invoke_faulty_distributed_command'
|
|
LANGUAGE C STRICT;
|
|
GRANT CREATE ON SCHEMA public TO :ROLE_1;
|
|
SET ROLE :ROLE_1;
|
|
|
|
SELECT _timescaledb_internal.invoke_distributed_commands();
|
|
|
|
\c :DATA_NODE_1
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :DATA_NODE_2
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :DATA_NODE_3
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
GRANT CREATE ON SCHEMA public TO :ROLE_1;
|
|
SET ROLE :ROLE_1;
|
|
|
|
-- Verify failed insert command gets fully rolled back
|
|
\set ON_ERROR_STOP 0
|
|
SELECT _timescaledb_internal.invoke_faulty_distributed_command();
|
|
\set ON_ERROR_STOP 1
|
|
|
|
\c :DATA_NODE_1
|
|
SELECT * from disttable2;
|
|
\c :DATA_NODE_2
|
|
SELECT * from disttable2;
|
|
|
|
-- Test connection session identity
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
\unset ECHO
|
|
\o /dev/null
|
|
\ir include/remote_exec.sql
|
|
\o
|
|
\set ECHO all
|
|
|
|
-- Register_is_access_node_session_on_data_node() function and test that it returns false for
|
|
-- connections openned by test suite. This simualates behaviour expected
|
|
-- with a client connections.
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
|
|
\c :DATA_NODE_1
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :DATA_NODE_2
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :DATA_NODE_3
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
GRANT CREATE ON SCHEMA public TO :ROLE_1;
|
|
SET ROLE :ROLE_1;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
-- Ensure peer dist id is already set and can be set only once
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM test.remote_exec(ARRAY[:'DATA_NODE_1'], $$ SELECT * FROM _timescaledb_functions.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Repeat is_access_node_session_on_data_node() test again, but this time using connections openned from
|
|
-- access node to data nodes. Must return true.
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT is_access_node_session_on_data_node(); $$);
|
|
|
|
-- Test distributed_exec()
|
|
|
|
-- Test calling distributed exec via direct function call
|
|
RESET ROLE;
|
|
CREATE OR REPLACE PROCEDURE distributed_exec_direct_function_call(
|
|
query TEXT,
|
|
node_list name[] = NULL,
|
|
transactional BOOLEAN = TRUE)
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_direct_function_call' LANGUAGE C;
|
|
GRANT CREATE ON SCHEMA public TO :ROLE_1;
|
|
SET ROLE :ROLE_1;
|
|
|
|
-- Invalid input
|
|
\set ON_ERROR_STOP 0
|
|
\set VERBOSITY default
|
|
BEGIN;
|
|
-- Not allowed in transcation block if transactional=false
|
|
CALL distributed_exec_direct_function_call('CREATE TABLE dist_test(id int)', NULL, false);
|
|
ROLLBACK;
|
|
-- multi-dimensional array of data nodes
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{{db_data_node_1}}');
|
|
-- Specified, but empty data node array
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{}');
|
|
-- Specified, but contains null values
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{db_data_node_1, NULL}');
|
|
-- Specified, but not a data node
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{db_data_node_1928}');
|
|
\set VERBOSITY terse
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Make sure dist session is properly set
|
|
CALL distributed_exec('DO $$ BEGIN ASSERT(SELECT is_access_node_session_on_data_node()) = true; END; $$;');
|
|
|
|
-- Test creating and dropping a table
|
|
CALL distributed_exec('CREATE TABLE dist_test (id int)');
|
|
CALL distributed_exec('INSERT INTO dist_test values (7)');
|
|
-- Test INSERTING data using empty array of data nodes (same behavior as not specifying).
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * from dist_test; $$);
|
|
CALL distributed_exec('DROP TABLE dist_test');
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('INSERT INTO dist_test VALUES (8)', ARRAY[:'DATA_NODE_1']);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Test creating and dropping a role
|
|
CREATE ROLE dist_test_role;
|
|
-- Expect this to be an error, since data nodes are created on the same instance
|
|
\set ON_ERROR_STOP 0
|
|
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
|
|
CALL distributed_exec('CREATE ROLE dist_test_role');
|
|
$$);
|
|
\set ON_ERROR_STOP 1
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT 1 from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'; $$);
|
|
DROP ROLE DIST_TEST_ROLE;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
|
|
CALL distributed_exec('DROP ROLE dist_test_role');
|
|
$$);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Do not allow to run distributed_exec() on a data nodes
|
|
\c :DATA_NODE_1
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('SELECT 1');
|
|
\set ON_ERROR_STOP 1
|
|
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
|
|
-- Test health check function output on access node
|
|
SELECT * FROM _timescaledb_functions.health() ORDER BY 1 NULLS FIRST;
|
|
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_1');
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_2');
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_3');
|
|
|
|
-- Test health check when no longer an access node (no data nodes)
|
|
SELECT * FROM _timescaledb_functions.health() ORDER BY 1 NULLS FIRST;
|
|
|
|
DROP DATABASE :DATA_NODE_1;
|
|
DROP DATABASE :DATA_NODE_2;
|
|
DROP DATABASE :DATA_NODE_3;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
-- Calling distributed_exec without data nodes should fail
|
|
CALL distributed_exec('SELECT 1');
|
|
CALL distributed_exec('SELECT 1', '{data_node_1}');
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Test TS execution on non-TSDB server
|
|
CREATE EXTENSION postgres_fdw;
|
|
CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw
|
|
OPTIONS (host 'foo', dbname 'foodb', port '5432');
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM test.remote_exec('{myserver}', $$ SELECT 1; $$);
|
|
\set ON_ERROR_STOP 1
|
|
DROP SERVER myserver;
|
|
DROP EXTENSION postgres_fdw;
|
|
|
|
-- Test that transactional behaviour is the default and that it can be
|
|
-- disabled.
|
|
--
|
|
-- In this case, we only execute it on one data node since we are
|
|
-- creating a database and multiple creations of the database would
|
|
-- clash when executed on the same instace.
|
|
--
|
|
-- We prefix the database names with the test file to be able to
|
|
-- parallelize the test. Not possible right now because there are
|
|
-- other databases above that prevents this.
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
SELECT node_name, database, node_created, database_created, extension_created FROM add_data_node('dist_commands_1', host => 'localhost', database => :'DATA_NODE_1');
|
|
SELECT node_name, database, node_created, database_created, extension_created FROM add_data_node('dist_commands_2', host => 'localhost', database => :'DATA_NODE_2');
|
|
GRANT USAGE ON FOREIGN SERVER dist_commands_1, dist_commands_2 TO PUBLIC;
|
|
GRANT CREATE ON SCHEMA public TO :ROLE_1;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('CREATE DATABASE dist_commands_magic',
|
|
node_list => '{dist_commands_1}');
|
|
\set ON_ERROR_STOP 1
|
|
CALL distributed_exec('CREATE DATABASE dist_commands_magic',
|
|
node_list => '{dist_commands_1}', transactional => FALSE);
|
|
DROP DATABASE dist_commands_magic;
|
|
|
|
-- Test that distributed_exec honor the 2PC behaviour when starting a
|
|
-- transaction locally. It should also give an error if attempting to
|
|
-- execute non-transactionally inside a local transaction.
|
|
|
|
-- To test that distributed_exec honors transactions, we create a
|
|
-- table on both data nodes, and then tweak one of the tables so that
|
|
-- we get a duplicate key when updating the table on both data
|
|
-- nodes. This should then abort the transaction on all data nodes.
|
|
\c :TEST_DBNAME :ROLE_1
|
|
CALL distributed_exec($$
|
|
CREATE TABLE my_table (key INT, value TEXT, PRIMARY KEY (key));
|
|
$$);
|
|
|
|
\c :DATA_NODE_1
|
|
INSERT INTO my_table VALUES (1, 'foo');
|
|
|
|
\c :TEST_DBNAME :ROLE_1
|
|
\set ON_ERROR_STOP 0
|
|
BEGIN;
|
|
CALL distributed_exec($$ INSERT INTO my_table VALUES (1, 'bar') $$);
|
|
COMMIT;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- No changes should be there
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
-- This should work.
|
|
BEGIN;
|
|
CALL distributed_exec($$ INSERT INTO my_table VALUES (2, 'bar'); $$);
|
|
COMMIT;
|
|
|
|
-- We should see changes
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
-- This should fail since we are inside a transaction and asking for
|
|
-- transactional execution on the remote nodes. Non-transactional
|
|
-- execution should be outside transactions.
|
|
\set ON_ERROR_STOP 0
|
|
BEGIN;
|
|
CALL distributed_exec(
|
|
$$ INSERT INTO my_table VALUES (3, 'baz') $$,
|
|
transactional => FALSE
|
|
);
|
|
COMMIT;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- We should see no changes
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
DROP DATABASE :DATA_NODE_1;
|
|
DROP DATABASE :DATA_NODE_2;
|