mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 10:33:27 +08:00
This change ensures the database and extension is validated whenever these objects aren't created, instead of only doing validation when `bootstrap=>false` is passed when adding a data node. This fixes a corner case where a data node could be added and removed several times, even though the data node's database was already marked as having been part of a multi-node setup. A new test checks that a data node cannot be re-added after deleting it on the access node, irrespective of whether one bootstraps the data node or not when it is added.
386 lines
14 KiB
PL/PgSQL
386 lines
14 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_SUPERUSER;
|
|
CREATE OR REPLACE FUNCTION show_data_nodes()
|
|
RETURNS TABLE(data_node_name NAME, host TEXT, port INT, dbname NAME)
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_data_node_show' LANGUAGE C;
|
|
|
|
-- Fetch the encoding, collation, and ctype as quoted strings into
|
|
-- variables.
|
|
SELECT QUOTE_LITERAL(PG_ENCODING_TO_CHAR(encoding)) AS enc
|
|
, QUOTE_LITERAL(datcollate) AS coll
|
|
, QUOTE_LITERAL(datctype) AS ctype
|
|
FROM pg_database
|
|
WHERE datname = current_database()
|
|
\gset
|
|
|
|
-- Cleanup from other potential tests that created these databases
|
|
SET client_min_messages TO ERROR;
|
|
DROP DATABASE IF EXISTS bootstrap_test;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost', database => 'bootstrap_test');
|
|
|
|
-- Ensure database and extensions are installed and have the correct
|
|
-- encoding, ctype and collation.
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT extname, nspname
|
|
FROM pg_extension e, pg_namespace n
|
|
WHERE e.extnamespace = n.oid
|
|
AND e.extname = 'timescaledb';
|
|
SELECT PG_ENCODING_TO_CHAR(encoding) = :enc
|
|
, datcollate = :coll
|
|
, datctype = :ctype
|
|
FROM pg_database
|
|
WHERE datname = current_database();
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
-- After delete_data_node, the database and extension should still
|
|
-- exist on the data node
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
SELECT * FROM show_data_nodes();
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
SELECT extname, nspname
|
|
FROM pg_extension e, pg_namespace n
|
|
WHERE e.extnamespace = n.oid
|
|
AND e.extname = 'timescaledb';
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
\set ON_ERROR_STOP 0
|
|
-- Trying to add the data node again should fail, with or without
|
|
-- bootstrapping.
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost', database => 'bootstrap_test', bootstrap=>false);
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost', database => 'bootstrap_test');
|
|
\set ON_ERROR_STOP 0
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Bootstrap the database and check that calling it without
|
|
-- bootstrapping does not find any problems.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
SELECT * FROM show_data_nodes();
|
|
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
-- This should show dist_uuid row on the deleted node since that is
|
|
-- not removed by delete_data_node.
|
|
SELECT key FROM _timescaledb_catalog.metadata WHERE key = 'dist_uuid';
|
|
|
|
-- Delete the dist_uuid so that we can try to re-add it without
|
|
-- bootstrapping.
|
|
DELETE FROM _timescaledb_catalog.metadata WHERE key = 'dist_uuid';
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Do a manual bootstrap of the data node and check that it can be
|
|
-- added.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Do a manual bootstrap of the data node and check that it can be
|
|
-- added even when bootstrap is true. This is to check that we can
|
|
-- bootstrap a database with an extension already installed.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Create a database and check that a mismatching encoding is caught
|
|
-- when bootstrapping (since it will skip creating the database).
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
CREATE DATABASE bootstrap_test
|
|
ENCODING SQL_ASCII
|
|
LC_COLLATE 'C'
|
|
LC_CTYPE 'C'
|
|
TEMPLATE template0
|
|
OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Do a manual bootstrap of the data but check that a mismatching
|
|
-- encoding, ctype, or collation will be caught.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
-- Pick an alternative locale for testing from the list of installed
|
|
-- collations. This locale needs to be differnt from the current
|
|
-- database's locale. We pick it from the list of collations in a
|
|
-- platform agnostic way since, e.g., Linux and Windows have very
|
|
-- different locale names.
|
|
SELECT QUOTE_LITERAL(c.collctype) AS other_locale
|
|
FROM pg_collation c, pg_database d
|
|
WHERE c.collencoding = d.encoding
|
|
AND d.datctype != c.collctype
|
|
AND d.datname = current_database()
|
|
ORDER BY c.oid DESC
|
|
LIMIT 1
|
|
\gset
|
|
|
|
CREATE DATABASE bootstrap_test
|
|
ENCODING SQL_ASCII
|
|
LC_COLLATE 'C'
|
|
LC_CTYPE 'C'
|
|
TEMPLATE template0
|
|
OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
CREATE DATABASE bootstrap_test
|
|
ENCODING :"enc"
|
|
LC_COLLATE :other_locale
|
|
LC_CTYPE :ctype
|
|
TEMPLATE template0
|
|
OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
CREATE DATABASE bootstrap_test
|
|
ENCODING :"enc"
|
|
LC_COLLATE :coll
|
|
LC_CTYPE :other_locale
|
|
TEMPLATE template0
|
|
OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Bootstrap the database and remove it. Check that the missing
|
|
-- database is caught when adding the node and not bootstrapping.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
SELECT * FROM show_data_nodes();
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Bootstrap the database and remove the extension.
|
|
--
|
|
-- Check that adding the data node and not bootstrapping will fail
|
|
-- indicating that the extension is missing.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
SELECT * FROM delete_data_node('bootstrap_test');
|
|
\c bootstrap_test :ROLE_SUPERUSER;
|
|
|
|
SELECT extname FROM pg_extension WHERE extname = 'timescaledb';
|
|
DROP EXTENSION timescaledb CASCADE;
|
|
SELECT extname FROM pg_extension WHERE extname = 'timescaledb';
|
|
|
|
\set ON_ERROR_STOP 0
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => false);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DROP DATABASE bootstrap_test;
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Create a new access node manually so that we can set a specific
|
|
-- schema for the access node and then bootstrap a data node partially
|
|
-- with a non-public schema so that we can see that an error is
|
|
-- generated.
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
CREATE DATABASE access_node OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c access_node :ROLE_CLUSTER_SUPERUSER
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
-- Show the schema for the extension to verify that it is not public.
|
|
SELECT extname FROM pg_extension WHERE extname = 'timescaledb';
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
\c bootstrap_test :ROLE_CLUSTER_SUPERUSER
|
|
SET client_min_messages TO ERROR;
|
|
CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
\c access_node :ROLE_CLUSTER_SUPERUSER
|
|
|
|
-- Add data node and delete it under error suppression. We want to
|
|
-- avoid later tests to have random failures because the add succeeds.
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM _timescaledb_catalog.add_data_node(
|
|
'bootstrap_test', host => 'localhost',
|
|
database => 'bootstrap_test', bootstrap => true);
|
|
SELECT * FROM _timescaledb_catalog.delete_data_node('bootstrap_test');
|
|
\set ON_ERROR_STOP 1
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
DROP DATABASE bootstrap_test;
|
|
DROP DATABASE access_node;
|
|
|
|
----------------------------------------------------------------------
|
|
-- Test for ongoing transaction
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
BEGIN;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM add_data_node('bootstrap_test', host => 'localhost', database => 'bootstrap_test');
|
|
\set ON_ERROR_STOP 1
|
|
COMMIT;
|
|
SELECT * FROM show_data_nodes();
|
|
|
|
---------------------------------------------------------------------
|
|
-- Test unusual database names
|
|
--
|
|
-- bootstrap_database = 'postgres'
|
|
CREATE FUNCTION test_database_name(name TEXT) RETURNS void AS $$
|
|
BEGIN
|
|
PERFORM add_data_node('_test1', host => 'localhost', database => name);
|
|
PERFORM delete_data_node('_test1');
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
SELECT test_database_name('Unusual Name');
|
|
SELECT test_database_name(U&'\0441\043B\043E\043D');
|
|
|
|
DROP DATABASE "Unusual Name";
|
|
DROP DATABASE U&"\0441\043B\043E\043D";
|
|
|
|
-- Test Access Node DATABASE DROP NOTICE message
|
|
--
|
|
|
|
-- Make sure the NOTICE message not shown on a DROP DATABASE error
|
|
\set ON_ERROR_STOP 0
|
|
DROP DATABASE :TEST_DBNAME;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
CREATE DATABASE drop_db_test;
|
|
\c drop_db_test :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
SET client_min_messages TO ERROR;
|
|
CREATE EXTENSION timescaledb;
|
|
SET client_min_messages TO NOTICE;
|
|
|
|
-- No security label exists
|
|
SELECT label FROM pg_shseclabel
|
|
WHERE objoid = (SELECT oid from pg_database WHERE datname = 'drop_db_test') AND
|
|
provider = 'timescaledb';
|
|
|
|
SELECT * FROM add_data_node('drop_db_test_dn', host => 'localhost', database => 'drop_db_test_dn');
|
|
|
|
-- Make sure security label is created
|
|
SELECT substr(label, 0, 10) || ':uuid'
|
|
FROM pg_shseclabel
|
|
WHERE objoid = (SELECT oid from pg_database WHERE datname = 'drop_db_test') AND
|
|
provider = 'timescaledb';
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
|
|
|
-- Check that timescaledb security label cannot be used directly
|
|
\set ON_ERROR_STOP 0
|
|
SECURITY LABEL FOR timescaledb
|
|
ON DATABASE drop_db_test
|
|
IS 'dist_uuid:00000000-0000-0000-0000-00000000000';
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Check that security label functionality is working
|
|
CREATE TABLE seclabel_test(id int);
|
|
SECURITY LABEL ON TABLE seclabel_test IS 'label';
|
|
DROP TABLE seclabel_test;
|
|
|
|
-- This will generate NOTICE message
|
|
DROP DATABASE drop_db_test;
|
|
DROP DATABASE drop_db_test_dn;
|
|
|
|
-- Ensure label is deleted after the DROP
|
|
SELECT label FROM pg_shseclabel
|
|
WHERE objoid = (SELECT oid from pg_database WHERE datname = 'drop_db_test') AND
|
|
provider = 'timescaledb';
|