mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-25 07:40:48 +08:00
Grants and revokes where not propagated to compressed hypertables, if the hypertable had a compressed hypertable, meaning that `pg_dump` and `pg_restore` would not be able to dump nor restore the compressed part of a hypertable unless they were owners and/or was a superuser. This commit fixes this by propagating grants and revokes on a hypertable to the associated compressed hypertable, if one exists, which will then include the compressed hypertable and the associated chunks in the grant and revoke execution. It also adds code to fix the permissions of compressed hypertables and all associated chunks in an update and adds an update test to check that the permissions match. Fixes #3209
351 lines
16 KiB
Plaintext
351 lines
16 KiB
Plaintext
-- 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 ON_ERROR_STOP 0
|
|
CREATE VIEW hypertable_details AS
|
|
WITH
|
|
names AS (SELECT * FROM pg_class cl JOIN pg_namespace ns ON ns.oid = relnamespace)
|
|
SELECT (SELECT format('%I.%I', ht.schema_name, ht.table_name) FROM names
|
|
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable,
|
|
(SELECT relacl FROM names
|
|
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable_acl,
|
|
(SELECT format('%I.%I', ct.schema_name, ct.table_name) FROM names
|
|
WHERE ct.schema_name = nspname AND ct.table_name = relname) AS compressed,
|
|
(SELECT relacl FROM names
|
|
WHERE ct.schema_name = nspname AND ct.table_name = relname) AS compressed_acl
|
|
FROM _timescaledb_catalog.hypertable ht
|
|
LEFT JOIN _timescaledb_catalog.hypertable ct ON ht.compressed_hypertable_id = ct.id;
|
|
CREATE VIEW chunk_details AS
|
|
WITH
|
|
names AS (SELECT * FROM pg_class cl JOIN pg_namespace ns ON ns.oid = relnamespace)
|
|
SELECT (SELECT format('%I.%I', ht.schema_name, ht.table_name) FROM names
|
|
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable,
|
|
(SELECT relacl FROM names
|
|
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable_acl,
|
|
(SELECT format('%I.%I', ch.schema_name, ch.table_name) FROM names
|
|
WHERE ch.schema_name = nspname AND ch.table_name = relname) AS chunk,
|
|
(SELECT relacl FROM names
|
|
WHERE ch.schema_name = nspname AND ch.table_name = relname) AS chunk_acl
|
|
FROM _timescaledb_catalog.hypertable ht
|
|
JOIN _timescaledb_catalog.chunk ch ON ch.hypertable_id = ht.id;
|
|
CREATE TABLE conditions (
|
|
timec TIMESTAMPTZ NOT NULL,
|
|
location TEXT NOT NULL,
|
|
location2 char(10) NOT NULL,
|
|
temperature DOUBLE PRECISION NULL,
|
|
humidity DOUBLE PRECISION NULL
|
|
);
|
|
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=>'1month'::interval);
|
|
table_name
|
|
------------
|
|
conditions
|
|
(1 row)
|
|
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'timec');
|
|
ERROR: must be owner of table conditions
|
|
insert into conditions
|
|
select generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 'klick', 55, 75;
|
|
ERROR: permission denied for table conditions
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
--now owner tries and succeeds --
|
|
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'timec');
|
|
insert into conditions
|
|
select generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 'klick', 55, 75;
|
|
--try modifying compress properties --
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'humidity');
|
|
ERROR: must be owner of table conditions
|
|
--- compress_chunks and decompress_chunks fail without correct perm --
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
select compress_chunk(ch1.schema_name|| '.' || ch1.table_name)
|
|
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' and ch1.compressed_chunk_id IS NULL;
|
|
ERROR: must be owner of hypertable "conditions"
|
|
select decompress_chunk(ch1.schema_name|| '.' || ch1.table_name)
|
|
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions';
|
|
ERROR: must be owner of hypertable "conditions"
|
|
select add_compression_policy('conditions', '1day'::interval);
|
|
ERROR: must be owner of hypertable "conditions"
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
select add_compression_policy('conditions', '1day'::interval);
|
|
add_compression_policy
|
|
------------------------
|
|
1000
|
|
(1 row)
|
|
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
--try dropping policy
|
|
select remove_compression_policy('conditions', true);
|
|
ERROR: must be owner of hypertable "conditions"
|
|
--Tests for GRANTS.
|
|
-- as owner grant select , compress chunk and check SELECT works
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
GRANT SELECT on conditions to :ROLE_DEFAULT_PERM_USER_2;
|
|
select count(*) from
|
|
(select compress_chunk(ch1.schema_name|| '.' || ch1.table_name)
|
|
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' and ch1.compressed_chunk_id IS NULL ) as subq;
|
|
count
|
|
-------
|
|
2
|
|
(1 row)
|
|
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
select count(*) from conditions;
|
|
count
|
|
-------
|
|
31
|
|
(1 row)
|
|
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
GRANT INSERT on conditions to :ROLE_DEFAULT_PERM_USER_2;
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
insert into conditions
|
|
select '2019-04-01 00:00+0'::timestamp with time zone, 'NYC', 'klick', 55, 75;
|
|
select count(*) from conditions;
|
|
count
|
|
-------
|
|
32
|
|
(1 row)
|
|
|
|
update conditions
|
|
set location = 'SFO'
|
|
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
|
|
ERROR: permission denied for table conditions
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
GRANT UPDATE, DELETE on conditions to :ROLE_DEFAULT_PERM_USER_2;
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
select location from conditions where timec = '2019-04-01 00:00+0';
|
|
location
|
|
----------
|
|
NYC
|
|
(1 row)
|
|
|
|
--NOTE constraint exclusion excludes the other chunks here
|
|
--otherwise update would fail
|
|
update conditions
|
|
set location = 'SFO'
|
|
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
|
|
select location from conditions where timec = '2019-04-01 00:00+0';
|
|
location
|
|
----------
|
|
SFO
|
|
(1 row)
|
|
|
|
--update expected to fail as executor touches all chunks
|
|
update conditions
|
|
set location = 'PNC'
|
|
where location = 'SFO';
|
|
ERROR: cannot update/delete rows from chunk "_hyper_1_1_chunk" as it is compressed
|
|
delete from conditions
|
|
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
|
|
select location from conditions where timec = '2019-04-01 00:00+0';
|
|
location
|
|
----------
|
|
(0 rows)
|
|
|
|
CREATE VIEW v2 as select * from conditions;
|
|
select count(*) from v2;
|
|
count
|
|
-------
|
|
31
|
|
(1 row)
|
|
|
|
--should fail after revoking permissions
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
|
REVOKE SELECT on conditions FROM :ROLE_DEFAULT_PERM_USER_2;
|
|
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
|
select count(*) from v2;
|
|
ERROR: permission denied for table conditions
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
DROP TABLE conditions CASCADE;
|
|
NOTICE: drop cascades to 2 other objects
|
|
NOTICE: drop cascades to view v2
|
|
-- Testing that permissions propagate to compressed hypertables and to
|
|
-- compressed chunks.
|
|
CREATE TABLE conditions (
|
|
timec TIMESTAMPTZ NOT NULL,
|
|
location TEXT NOT NULL,
|
|
temperature DOUBLE PRECISION NULL,
|
|
humidity DOUBLE PRECISION NULL
|
|
);
|
|
SELECT table_name FROM create_hypertable( 'conditions', 'timec',
|
|
chunk_time_interval => '1 week'::interval);
|
|
table_name
|
|
------------
|
|
conditions
|
|
(1 row)
|
|
|
|
ALTER TABLE conditions SET (
|
|
timescaledb.compress,
|
|
timescaledb.compress_segmentby = 'location',
|
|
timescaledb.compress_orderby = 'timec'
|
|
);
|
|
INSERT INTO conditions
|
|
SELECT generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 55, 75;
|
|
SELECT compress_chunk(show_chunks('conditions'));
|
|
compress_chunk
|
|
-----------------------------------------
|
|
_timescaledb_internal._hyper_3_6_chunk
|
|
_timescaledb_internal._hyper_3_7_chunk
|
|
_timescaledb_internal._hyper_3_8_chunk
|
|
_timescaledb_internal._hyper_3_9_chunk
|
|
_timescaledb_internal._hyper_3_10_chunk
|
|
(5 rows)
|
|
|
|
-- Check that ACL propagates to compressed hypertable. We could prune
|
|
-- the listing by only selecting chunks where the ACL does not match
|
|
-- the hypertable ACL, but for now we list all to make debugging easy.
|
|
\x on
|
|
SELECT * FROM hypertable_details WHERE hypertable = 'public.conditions';
|
|
-[ RECORD 1 ]--+-----------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
compressed | _timescaledb_internal._compressed_hypertable_4
|
|
compressed_acl |
|
|
|
|
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
|
|
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
|
|
ORDER BY hypertable, chunk;
|
|
-[ RECORD 1 ]--+------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
|
|
chunk_acl |
|
|
-[ RECORD 2 ]--+------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
|
|
chunk_acl |
|
|
-[ RECORD 3 ]--+------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
|
|
chunk_acl |
|
|
-[ RECORD 4 ]--+------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
|
|
chunk_acl |
|
|
-[ RECORD 5 ]--+------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl |
|
|
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
|
|
chunk_acl |
|
|
|
|
GRANT SELECT ON conditions TO :ROLE_DEFAULT_PERM_USER;
|
|
SELECT * FROM hypertable_details WHERE hypertable = 'public.conditions';
|
|
-[ RECORD 1 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
compressed | _timescaledb_internal._compressed_hypertable_4
|
|
compressed_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
|
|
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
|
|
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
|
|
ORDER BY hypertable, chunk;
|
|
-[ RECORD 1 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 2 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 3 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 4 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 5 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
|
|
-- Add some new data and compress the chunks. The chunks should get
|
|
-- the permissions of the hypertable. We pick a start date to make
|
|
-- sure that we are not inserting into an already compressed chunk.
|
|
INSERT INTO conditions
|
|
SELECT generate_series('2019-01-07 00:00'::timestamp, '2019-02-07 00:00'::timestamp, '1 day'), 'XYZ', 47, 11;
|
|
SELECT compress_chunk(show_chunks('conditions', newer_than => '2019-01-01'));
|
|
-[ RECORD 1 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_16_chunk
|
|
-[ RECORD 2 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_17_chunk
|
|
-[ RECORD 3 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_18_chunk
|
|
-[ RECORD 4 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_19_chunk
|
|
-[ RECORD 5 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_20_chunk
|
|
-[ RECORD 6 ]--+----------------------------------------
|
|
compress_chunk | _timescaledb_internal._hyper_3_21_chunk
|
|
|
|
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
|
|
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
|
|
ORDER BY hypertable, chunk;
|
|
-[ RECORD 1 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 2 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 3 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 4 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 5 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 6 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_22_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 7 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_23_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 8 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_24_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 9 ]--+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_25_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 10 ]-+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_26_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
-[ RECORD 11 ]-+---------------------------------------------------------------
|
|
hypertable | public.conditions
|
|
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
chunk | _timescaledb_internal.compress_hyper_4_27_chunk
|
|
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
|
|
|
|
\x off
|