mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 18:13:18 +08:00
A hypertable's associated schema is used to create and store internal data tables (chunks). A hypertable creates tables in that schema, typically with full superuser permissions, regardless of whether the hypertable's owner or the current user have permissions for the schema. If the schema doesn't exist, the hypertable will create it when creating the first chunk, even though the user or table owner does not have permissions to create schemas in the database. This change adds proper permissions checks to create_hypertable() so that users cannot create hypertables with a custom associated schema unless they have the proper permissions on the schema or the database. Chunks are also no longer created with internal schema permissions if the associated schema is something different from the internal schema.
280 lines
14 KiB
Plaintext
280 lines
14 KiB
Plaintext
\c single :ROLE_SUPERUSER
|
|
create schema test_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
|
|
create schema chunk_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER_2;
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
|
create table test_schema.test_table(time BIGINT, temp float8, device_id text, device_type text, location text, id int, id2 int);
|
|
\set ON_ERROR_STOP 0
|
|
-- get_create_command should fail since hypertable isn't made yet
|
|
SELECT * FROM _timescaledb_internal.get_create_command('test_table');
|
|
ERROR: hypertable test_table not found
|
|
\set ON_ERROR_STOP 1
|
|
\dt "test_schema".*
|
|
List of relations
|
|
Schema | Name | Type | Owner
|
|
-------------+------------+-------+-------------------
|
|
test_schema | test_table | table | default_perm_user
|
|
(1 row)
|
|
|
|
create table test_schema.test_table_no_not_null(time BIGINT, device_id text);
|
|
\set ON_ERROR_STOP 0
|
|
-- Permission denied with unprivileged role
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
|
select * from create_hypertable('test_schema.test_table_no_not_null', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
|
ERROR: permission denied for schema test_schema at character 33
|
|
-- CREATE on schema is not enough
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
|
GRANT ALL ON SCHEMA test_schema TO :ROLE_DEFAULT_PERM_USER_2;
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
|
select * from create_hypertable('test_schema.test_table_no_not_null', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
|
ERROR: Permission denied for relation test_schema.test_table_no_not_null
|
|
\set ON_ERROR_STOP 1
|
|
-- Should work with when granted table owner role
|
|
RESET ROLE;
|
|
GRANT :ROLE_DEFAULT_PERM_USER TO :ROLE_DEFAULT_PERM_USER_2;
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
|
select * from create_hypertable('test_schema.test_table_no_not_null', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
|
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
\set ON_ERROR_STOP 0
|
|
insert into test_schema.test_table_no_not_null (device_id) VALUES('foo');
|
|
ERROR: null value in column "time" violates not-null constraint
|
|
\set ON_ERROR_STOP 1
|
|
insert into test_schema.test_table_no_not_null (time, device_id) VALUES(1, 'foo');
|
|
SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
|
|
Column | Type | Nullable
|
|
-----------+--------+----------
|
|
time | bigint | t
|
|
device_id | text | f
|
|
(2 rows)
|
|
|
|
ALTER TABLE test_schema.test_table_no_not_null ALTER time DROP NOT NULL;
|
|
SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
|
|
Column | Type | Nullable
|
|
-----------+--------+----------
|
|
time | bigint | f
|
|
device_id | text | f
|
|
(2 rows)
|
|
|
|
SELECT _timescaledb_internal.set_time_columns_not_null();
|
|
set_time_columns_not_null
|
|
---------------------------
|
|
|
|
(1 row)
|
|
|
|
SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
|
|
Column | Type | Nullable
|
|
-----------+--------+----------
|
|
time | bigint | t
|
|
device_id | text | f
|
|
(2 rows)
|
|
|
|
RESET ROLE;
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
|
\set ON_ERROR_STOP 0
|
|
-- No permissions on associated schema should fail
|
|
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
|
|
ERROR: User default_perm_user lacks permissions to create chunks in schema "chunk_schema"
|
|
\set ON_ERROR_STOP 1
|
|
-- Granting permissions on chunk_schema should make things work
|
|
RESET ROLE;
|
|
GRANT CREATE ON SCHEMA chunk_schema TO :ROLE_DEFAULT_PERM_USER;
|
|
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
|
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
|
|
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
SELECT * FROM _timescaledb_internal.get_create_command('test_table');
|
|
get_create_command
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
SELECT create_hypertable('test_schema.test_table', '"time"', 'device_id', 2, chunk_time_interval => 2592000000000, create_default_indexes=>FALSE);
|
|
(1 row)
|
|
|
|
--test adding one more closed dimension
|
|
select add_dimension('test_schema.test_table', 'location', 2);
|
|
add_dimension
|
|
---------------
|
|
|
|
(1 row)
|
|
|
|
select * from _timescaledb_catalog.hypertable where table_name = 'test_table';
|
|
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
|
----+-------------+------------+------------------------+-------------------------+----------------
|
|
2 | test_schema | test_table | chunk_schema | _hyper_2 | 3
|
|
(1 row)
|
|
|
|
select * from _timescaledb_catalog.dimension;
|
|
id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length
|
|
----+---------------+-------------+-------------+---------+------------+--------------------------+--------------------+-----------------
|
|
1 | 1 | time | bigint | t | | | | 2592000000000
|
|
2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
3 | 2 | time | bigint | t | | | | 2592000000000
|
|
4 | 2 | device_id | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
5 | 2 | location | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
(5 rows)
|
|
|
|
\set ON_ERROR_STOP 0
|
|
-- get_create_command only works on tables w/ 1 or 2 dimensions
|
|
SELECT * FROM _timescaledb_internal.get_create_command('test_table');
|
|
ERROR: get_create_command only supports hypertables with up to 2 dimensions
|
|
\set ON_ERROR_STOP 1
|
|
--test adding one more open dimension
|
|
select add_dimension('test_schema.test_table', 'id', interval_length => 1000);
|
|
NOTICE: Adding NOT NULL constraint to time column id (NULL time values not allowed)
|
|
add_dimension
|
|
---------------
|
|
|
|
(1 row)
|
|
|
|
select * from _timescaledb_catalog.hypertable where table_name = 'test_table';
|
|
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
|
----+-------------+------------+------------------------+-------------------------+----------------
|
|
2 | test_schema | test_table | chunk_schema | _hyper_2 | 4
|
|
(1 row)
|
|
|
|
select * from _timescaledb_catalog.dimension;
|
|
id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length
|
|
----+---------------+-------------+-------------+---------+------------+--------------------------+--------------------+-----------------
|
|
1 | 1 | time | bigint | t | | | | 2592000000000
|
|
2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
3 | 2 | time | bigint | t | | | | 2592000000000
|
|
4 | 2 | device_id | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
5 | 2 | location | text | f | 2 | _timescaledb_internal | get_partition_hash |
|
|
6 | 2 | id | integer | t | | | | 1000
|
|
(6 rows)
|
|
|
|
-- Test add_dimension: can use interval types for TIMESTAMPTZ columns
|
|
CREATE TABLE dim_test_time(time TIMESTAMPTZ, time2 TIMESTAMPTZ, time3 BIGINT, temp float8, device int, location int);
|
|
SELECT create_hypertable('dim_test_time', 'time');
|
|
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
SELECT add_dimension('dim_test_time', 'time2', interval_length => INTERVAL '1 day');
|
|
NOTICE: Adding NOT NULL constraint to time column time2 (NULL time values not allowed)
|
|
add_dimension
|
|
---------------
|
|
|
|
(1 row)
|
|
|
|
-- Test add_dimension: only integral should work on BIGINT columns
|
|
\set ON_ERROR_STOP 0
|
|
SELECT add_dimension('dim_test_time', 'time3', interval_length => INTERVAL '1 day');
|
|
ERROR: interval_length needs to be an integer type for SMALLINT, INTEGER, and BIGINT time columns
|
|
-- string is not a valid type
|
|
SELECT add_dimension('dim_test_time', 'time3', interval_length => 'foo'::TEXT);
|
|
ERROR: interval_length needs to be an integer type for SMALLINT, INTEGER, and BIGINT time columns
|
|
\set ON_ERROR_STOP 1
|
|
SELECT add_dimension('dim_test_time', 'time3', interval_length => 500);
|
|
NOTICE: Adding NOT NULL constraint to time column time3 (NULL time values not allowed)
|
|
add_dimension
|
|
---------------
|
|
|
|
(1 row)
|
|
|
|
-- Test add_dimension: integrals should work on TIMESTAMPTZ columns
|
|
CREATE TABLE dim_test_time2(time TIMESTAMPTZ, time2 TIMESTAMPTZ, temp float8, device int, location int);
|
|
SELECT create_hypertable('dim_test_time2', 'time');
|
|
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
SELECT add_dimension('dim_test_time2', 'time2', interval_length => 500);
|
|
WARNING: You specified a interval_length of less than a second, make sure that this is what you intended
|
|
NOTICE: Adding NOT NULL constraint to time column time2 (NULL time values not allowed)
|
|
add_dimension
|
|
---------------
|
|
|
|
(1 row)
|
|
|
|
\set ON_ERROR_STOP 0
|
|
--adding on a non-hypertable
|
|
CREATE TABLE not_hypertable(time TIMESTAMPTZ, temp float8, device int, location int);
|
|
SELECT add_dimension('not_hypertable', 'time', interval_length => 500);
|
|
ERROR: "not_hypertable" is not a hypertable; cannot add dimension
|
|
--adding a non-exist column
|
|
SELECT add_dimension('test_schema.test_table', 'nope', 2);
|
|
ERROR: column "nope" does not exist
|
|
--adding the same dimension twice should fail
|
|
select add_dimension('test_schema.test_table', 'location', 2);
|
|
ERROR: A dimension on column "location" already exists
|
|
--adding dimension with both number_partitions and interval_length should fail
|
|
select add_dimension('test_schema.test_table', 'id2', number_partitions => 2, interval_length => 1000);
|
|
ERROR: Cannot specify both interval and number of slices/partitions for a single dimension
|
|
--adding a new dimension on a non-empty table should also fail
|
|
insert into test_schema.test_table values (123456789, 23.8, 'blue', 'type1', 'nyc', 1, 1);
|
|
select add_dimension('test_schema.test_table', 'device_type', 2);
|
|
ERROR: Cannot add new dimension to a non-empty table
|
|
\set ON_ERROR_STOP 1
|
|
--show chunks in the associated schema
|
|
\dt "chunk_schema".*
|
|
List of relations
|
|
Schema | Name | Type | Owner
|
|
--------------+------------------+-------+-------------------
|
|
chunk_schema | _hyper_2_2_chunk | table | default_perm_user
|
|
(1 row)
|
|
|
|
--test partitioning in only time dimension
|
|
create table test_schema.test_1dim(time timestamp, temp float);
|
|
select create_hypertable('test_schema.test_1dim', 'time');
|
|
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
SELECT * FROM _timescaledb_internal.get_create_command('test_1dim');
|
|
get_create_command
|
|
-----------------------------------------------------------------------------------------------------------------------------------
|
|
SELECT create_hypertable('test_schema.test_1dim', '"time"', chunk_time_interval => 2592000000000, create_default_indexes=>FALSE);
|
|
(1 row)
|
|
|
|
\dt "test_schema".*
|
|
List of relations
|
|
Schema | Name | Type | Owner
|
|
-------------+------------------------+-------+-------------------
|
|
test_schema | test_1dim | table | default_perm_user
|
|
test_schema | test_table | table | default_perm_user
|
|
test_schema | test_table_no_not_null | table | default_perm_user
|
|
(3 rows)
|
|
|
|
select create_hypertable('test_schema.test_1dim', 'time', if_not_exists => true);
|
|
NOTICE: hypertable test_schema.test_1dim already exists, skipping
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
-- Should error when creating again without if_not_exists set to true
|
|
\set ON_ERROR_STOP 0
|
|
select create_hypertable('test_schema.test_1dim', 'time');
|
|
ERROR: hypertable test_schema.test_1dim already exists
|
|
\set ON_ERROR_STOP 1
|
|
-- if_not_exist should also work with data in the hypertable
|
|
insert into test_schema.test_1dim VALUES ('2004-10-19 10:23:54+02', 1.0);
|
|
select create_hypertable('test_schema.test_1dim', 'time', if_not_exists => true);
|
|
NOTICE: hypertable test_schema.test_1dim already exists, skipping
|
|
create_hypertable
|
|
-------------------
|
|
|
|
(1 row)
|
|
|
|
-- Should error when creating again without if_not_exists set to true
|
|
\set ON_ERROR_STOP 0
|
|
select create_hypertable('test_schema.test_1dim', 'time');
|
|
ERROR: hypertable test_schema.test_1dim already exists
|
|
\set ON_ERROR_STOP 1
|
|
-- Reset GRANTS
|
|
\c single :ROLE_SUPERUSER
|
|
REVOKE :ROLE_DEFAULT_PERM_USER FROM :ROLE_DEFAULT_PERM_USER_2;
|