Add CREATE VIEW statement for columnar.storage with security barrier

Add new views for columnar storage: stripe, chunk_group, and chunk with security barrier

Refactor columnar views to use OR REPLACE for consistent binding and add missing comments

Enhance columnar test helpers with visibility check function and update related queries for improved storage ID retrieval
m3hm3t/pg18_columnar_access_temp_err
Mehmet Yilmaz 2025-10-16 10:58:31 +00:00
parent 041ae15ba3
commit 2e4cb36bb2
7 changed files with 98 additions and 12 deletions

View File

@ -1,2 +1,44 @@
-- citus_columnar--13.2-1--14.0-1
-- bump version to 14.0-1
CREATE OR REPLACE VIEW columnar.storage WITH (security_barrier) AS
SELECT c.oid::regclass AS relation,
columnar.get_storage_id(c.oid) AS storage_id
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_am am ON c.relam = am.oid
WHERE am.amname = 'columnar'
-- exclude other sessions' temp rels, but keep *my* temp tables
AND (c.relpersistence <> 't'
OR c.relnamespace = pg_catalog.pg_my_temp_schema())
AND pg_catalog.pg_has_role(c.relowner, 'USAGE');
COMMENT ON VIEW columnar.storage IS 'Columnar relation ID to storage ID mapping.';
GRANT SELECT ON columnar.storage TO PUBLIC;
-- re-emit dependent views with OR REPLACE so they stay bound cleanly
CREATE OR REPLACE VIEW columnar.stripe WITH (security_barrier) AS
SELECT relation, storage.storage_id, stripe_num, file_offset, data_length,
column_count, chunk_row_count, row_count, chunk_group_count, first_row_number
FROM columnar_internal.stripe stripe, columnar.storage storage
WHERE stripe.storage_id = storage.storage_id;
COMMENT ON VIEW columnar.stripe
IS 'Columnar stripe information for tables on which the current user has ownership privileges.';
GRANT SELECT ON columnar.stripe TO PUBLIC;
CREATE OR REPLACE VIEW columnar.chunk_group WITH (security_barrier) AS
SELECT relation, storage.storage_id, stripe_num, chunk_group_num, row_count
FROM columnar_internal.chunk_group cg, columnar.storage storage
WHERE cg.storage_id = storage.storage_id;
COMMENT ON VIEW columnar.chunk_group
IS 'Columnar chunk group information for tables on which the current user has ownership privileges.';
GRANT SELECT ON columnar.chunk_group TO PUBLIC;
CREATE OR REPLACE VIEW columnar.chunk WITH (security_barrier) AS
SELECT relation, storage.storage_id, stripe_num, attr_num, chunk_group_num,
minimum_value, maximum_value, value_stream_offset, value_stream_length,
exists_stream_offset, exists_stream_length, value_compression_type,
value_compression_level, value_decompressed_length, value_count
FROM columnar_internal.chunk chunk, columnar.storage storage
WHERE chunk.storage_id = storage.storage_id;
COMMENT ON VIEW columnar.chunk
IS 'Columnar chunk information for tables on which the current user has ownership privileges.';
GRANT SELECT ON columnar.chunk TO PUBLIC;

View File

@ -214,8 +214,8 @@ SELECT COUNT(*) FROM columnar_temp WHERE i < 5;
4
(1 row)
SELECT columnar.get_storage_id(oid) AS columnar_temp_storage_id
FROM pg_class WHERE relname='columnar_temp' \gset
SELECT COALESCE(columnar_test_helpers.get_storage_id_if_visible('columnar_temp'::regclass), 0)
AS columnar_temp_storage_id \gset
BEGIN;
DROP TABLE columnar_temp;
-- show that we drop stripes properly

View File

@ -11,10 +11,19 @@ $$ LANGUAGE SQL;
INSERT INTO t2 SELECT i, f(i) FROM generate_series(1, 5) i;
-- there are no subtransactions, so above statement should batch
-- INSERTs inside the UDF and create on stripe per table.
SELECT relname, count(*) FROM columnar.stripe a, pg_class b
WHERE columnar.get_storage_id(b.oid)=a.storage_id AND relname IN ('t1', 't2')
GROUP BY relname
ORDER BY relname;
WITH rels(rel) AS (
VALUES ('t1'::regclass), ('t2'::regclass)
),
sids AS (
SELECT rel, columnar.get_storage_id(rel) AS sid
FROM rels
)
SELECT c.relname, COUNT(*) AS count
FROM columnar_internal.stripe st
JOIN sids s ON st.storage_id = s.sid
JOIN pg_catalog.pg_class c ON c.oid = s.rel
GROUP BY c.relname
ORDER BY c.relname;
relname | count
---------------------------------------------------------------------
t1 | 1

View File

@ -146,3 +146,15 @@ BEGIN
RETURN NEXT;
END LOOP;
END; $$ language plpgsql;
CREATE OR REPLACE FUNCTION get_storage_id_if_visible(rel regclass)
RETURNS bigint
LANGUAGE sql STABLE AS $$
SELECT CASE
WHEN c.relpersistence = 't'
AND c.relnamespace <> pg_catalog.pg_my_temp_schema()
THEN NULL -- other sessions temp → dont touch
ELSE columnar.get_storage_id(c.oid)
END
FROM pg_catalog.pg_class c
WHERE c.oid = $1::oid
$$;

View File

@ -174,8 +174,8 @@ INSERT INTO columnar_temp SELECT i FROM generate_series(1,5) i;
-- test basic select
SELECT COUNT(*) FROM columnar_temp WHERE i < 5;
SELECT columnar.get_storage_id(oid) AS columnar_temp_storage_id
FROM pg_class WHERE relname='columnar_temp' \gset
SELECT COALESCE(columnar_test_helpers.get_storage_id_if_visible('columnar_temp'::regclass), 0)
AS columnar_temp_storage_id \gset
BEGIN;
DROP TABLE columnar_temp;

View File

@ -15,10 +15,20 @@ INSERT INTO t2 SELECT i, f(i) FROM generate_series(1, 5) i;
-- there are no subtransactions, so above statement should batch
-- INSERTs inside the UDF and create on stripe per table.
SELECT relname, count(*) FROM columnar.stripe a, pg_class b
WHERE columnar.get_storage_id(b.oid)=a.storage_id AND relname IN ('t1', 't2')
GROUP BY relname
ORDER BY relname;
WITH rels(rel) AS (
VALUES ('t1'::regclass), ('t2'::regclass)
),
sids AS (
SELECT rel, columnar.get_storage_id(rel) AS sid
FROM rels
)
SELECT c.relname, COUNT(*) AS count
FROM columnar_internal.stripe st
JOIN sids s ON st.storage_id = s.sid
JOIN pg_catalog.pg_class c ON c.oid = s.rel
GROUP BY c.relname
ORDER BY c.relname;
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;

View File

@ -158,3 +158,16 @@ BEGIN
RETURN NEXT;
END LOOP;
END; $$ language plpgsql;
CREATE OR REPLACE FUNCTION get_storage_id_if_visible(rel regclass)
RETURNS bigint
LANGUAGE sql STABLE AS $$
SELECT CASE
WHEN c.relpersistence = 't'
AND c.relnamespace <> pg_catalog.pg_my_temp_schema()
THEN NULL -- other sessions temp → dont touch
ELSE columnar.get_storage_id(c.oid)
END
FROM pg_catalog.pg_class c
WHERE c.oid = $1::oid
$$;