-- -- Testing ALTER TABLE on columnar tables. -- CREATE SCHEMA columnar_alter; SET search_path tO columnar_alter, public; CREATE TABLE test_alter_table (a int, b int, c int) USING columnar; WITH sample_data AS (VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9) ) INSERT INTO test_alter_table SELECT * FROM sample_data; -- drop a column ALTER TABLE test_alter_table DROP COLUMN a; select version_major, version_minor, reserved_stripe_id, reserved_row_number from columnar_test_helpers.columnar_storage_info('test_alter_table'); version_major | version_minor | reserved_stripe_id | reserved_row_number --------------------------------------------------------------------- 2 | 0 | 2 | 150001 (1 row) -- test analyze ANALYZE test_alter_table; -- verify select queries run as expected SELECT * FROM test_alter_table; b | c --------------------------------------------------------------------- 2 | 3 5 | 6 8 | 9 (3 rows) SELECT a FROM test_alter_table; ERROR: column "a" does not exist SELECT b FROM test_alter_table; b --------------------------------------------------------------------- 2 5 8 (3 rows) -- verify insert runs as expected INSERT INTO test_alter_table (SELECT 3, 5, 8); ERROR: INSERT has more expressions than target columns INSERT INTO test_alter_table (SELECT 5, 8); -- add a column with no defaults ALTER TABLE test_alter_table ADD COLUMN d int; SELECT * FROM test_alter_table; b | c | d --------------------------------------------------------------------- 2 | 3 | 5 | 6 | 8 | 9 | 5 | 8 | (4 rows) INSERT INTO test_alter_table (SELECT 3, 5, 8); SELECT * FROM test_alter_table; b | c | d --------------------------------------------------------------------- 2 | 3 | 5 | 6 | 8 | 9 | 5 | 8 | 3 | 5 | 8 (5 rows) select version_major, version_minor, reserved_stripe_id, reserved_row_number from columnar_test_helpers.columnar_storage_info('test_alter_table'); version_major | version_minor | reserved_stripe_id | reserved_row_number --------------------------------------------------------------------- 2 | 0 | 4 | 450001 (1 row) -- add a fixed-length column with default value ALTER TABLE test_alter_table ADD COLUMN e int default 3; SELECT * from test_alter_table; b | c | d | e --------------------------------------------------------------------- 2 | 3 | | 3 5 | 6 | | 3 8 | 9 | | 3 5 | 8 | | 3 3 | 5 | 8 | 3 (5 rows) INSERT INTO test_alter_table (SELECT 1, 2, 4, 8); SELECT * from test_alter_table; b | c | d | e --------------------------------------------------------------------- 2 | 3 | | 3 5 | 6 | | 3 8 | 9 | | 3 5 | 8 | | 3 3 | 5 | 8 | 3 1 | 2 | 4 | 8 (6 rows) select version_major, version_minor, reserved_stripe_id, reserved_row_number from columnar_test_helpers.columnar_storage_info('test_alter_table'); version_major | version_minor | reserved_stripe_id | reserved_row_number --------------------------------------------------------------------- 2 | 0 | 5 | 600001 (1 row) -- add a variable-length column with default value ALTER TABLE test_alter_table ADD COLUMN f text DEFAULT 'TEXT ME'; SELECT * from test_alter_table; b | c | d | e | f --------------------------------------------------------------------- 2 | 3 | | 3 | TEXT ME 5 | 6 | | 3 | TEXT ME 8 | 9 | | 3 | TEXT ME 5 | 8 | | 3 | TEXT ME 3 | 5 | 8 | 3 | TEXT ME 1 | 2 | 4 | 8 | TEXT ME (6 rows) INSERT INTO test_alter_table (SELECT 1, 2, 4, 8, 'ABCDEF'); SELECT * from test_alter_table; b | c | d | e | f --------------------------------------------------------------------- 2 | 3 | | 3 | TEXT ME 5 | 6 | | 3 | TEXT ME 8 | 9 | | 3 | TEXT ME 5 | 8 | | 3 | TEXT ME 3 | 5 | 8 | 3 | TEXT ME 1 | 2 | 4 | 8 | TEXT ME 1 | 2 | 4 | 8 | ABCDEF (7 rows) -- drop couple of columns ALTER TABLE test_alter_table DROP COLUMN c; ALTER TABLE test_alter_table DROP COLUMN e; ANALYZE test_alter_table; SELECT * from test_alter_table; b | d | f --------------------------------------------------------------------- 2 | | TEXT ME 5 | | TEXT ME 8 | | TEXT ME 5 | | TEXT ME 3 | 8 | TEXT ME 1 | 4 | TEXT ME 1 | 4 | ABCDEF (7 rows) SELECT count(*) from test_alter_table; count --------------------------------------------------------------------- 7 (1 row) SELECT count(t.*) from test_alter_table t; count --------------------------------------------------------------------- 7 (1 row) -- unsupported default values ALTER TABLE test_alter_table ADD COLUMN g boolean DEFAULT isfinite(current_date); ALTER TABLE test_alter_table ADD COLUMN h DATE DEFAULT current_date; SELECT * FROM test_alter_table; ERROR: unsupported default value for column "g" HINT: Expression is either mutable or does not evaluate to constant value ALTER TABLE test_alter_table ALTER COLUMN g DROP DEFAULT; SELECT * FROM test_alter_table; ERROR: unsupported default value for column "h" HINT: Expression is either mutable or does not evaluate to constant value ALTER TABLE test_alter_table ALTER COLUMN h DROP DEFAULT; ANALYZE test_alter_table; SELECT * FROM test_alter_table; b | d | f | g | h --------------------------------------------------------------------- 2 | | TEXT ME | | 5 | | TEXT ME | | 8 | | TEXT ME | | 5 | | TEXT ME | | 3 | 8 | TEXT ME | | 1 | 4 | TEXT ME | | 1 | 4 | ABCDEF | | (7 rows) -- unsupported type change ALTER TABLE test_alter_table ADD COLUMN i int; ALTER TABLE test_alter_table ADD COLUMN j float; ALTER TABLE test_alter_table ADD COLUMN k text; -- this is valid type change ALTER TABLE test_alter_table ALTER COLUMN i TYPE float; -- this is not valid ALTER TABLE test_alter_table ALTER COLUMN j TYPE int; -- text / varchar conversion is valid both ways ALTER TABLE test_alter_table ALTER COLUMN k TYPE varchar(20); ALTER TABLE test_alter_table ALTER COLUMN k TYPE text; -- rename column ALTER TABLE test_alter_table RENAME COLUMN k TO k_renamed; -- rename table ALTER TABLE test_alter_table RENAME TO test_alter_table_renamed; DROP TABLE test_alter_table_renamed; -- https://github.com/citusdata/citus/issues/4602 create domain str_domain as text not null; create table domain_test (a int, b int) using columnar; insert into domain_test values (1, 2); insert into domain_test values (1, 2); -- the following should error out since the domain is not nullable alter table domain_test add column c str_domain; ERROR: domain str_domain does not allow null values -- but this should succeed alter table domain_test add column c str_domain DEFAULT 'x'; SELECT * FROM domain_test; a | b | c --------------------------------------------------------------------- 1 | 2 | x 1 | 2 | x (2 rows) -- similar to "add column c str_domain DEFAULT 'x'", both were getting -- stucked before fixing https://github.com/citusdata/citus/issues/5164 BEGIN; ALTER TABLE domain_test ADD COLUMN d INT DEFAULT random(); ROLLBACK; BEGIN; ALTER TABLE domain_test ADD COLUMN d SERIAL; SELECT * FROM domain_test ORDER BY 1,2,3,4; a | b | c | d --------------------------------------------------------------------- 1 | 2 | x | 1 1 | 2 | x | 2 (2 rows) ROLLBACK; set default_table_access_method TO 'columnar'; CREATE TABLE has_volatile AS SELECT * FROM generate_series(1,10) id; ALTER TABLE has_volatile ADD col4 int DEFAULT (random() * 10000)::int; SELECT id, col4 < 10000 FROM has_volatile ORDER BY id; id | ?column? --------------------------------------------------------------------- 1 | t 2 | t 3 | t 4 | t 5 | t 6 | t 7 | t 8 | t 9 | t 10 | t (10 rows) -- https://github.com/citusdata/citus/issues/4601 CREATE TABLE itest13 (a int) using columnar; INSERT INTO itest13 VALUES (1), (2), (3); ALTER TABLE itest13 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY; SELECT * FROM itest13 ORDER BY a; a | c --------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 (3 rows) create table atacc1 (a int) using columnar; insert into atacc1 values(1); -- should error out. It previously crashed. alter table atacc1 add column b float8 not null default random(), add primary key(a); -- Add a generate column with an expression value create table test_gen_ex (x int) using columnar; INSERT INTO test_gen_ex VALUES (1), (2), (3); ALTER TABLE test_gen_ex ADD COLUMN y int generated always as (x+1) stored; SELECT * FROM test_gen_ex; x | y --------------------------------------------------------------------- 1 | 2 2 | 3 3 | 4 (3 rows) -- check removing all columns while having some data to simulate -- table with non-zero rows but zero-columns. -- https://github.com/citusdata/citus/issues/4626 BEGIN; create table local(y int); insert into local values (1), (2); alter table local drop column y; CREATE TABLE zero_col_columnar (like local) USING COLUMNAR; ALTER TABLE local RENAME TO local_xxxxx; INSERT INTO zero_col_columnar SELECT * FROM local_xxxxx; COMMIT; SELECT * FROM zero_col_columnar; -- (2 rows) SELECT count(*) FROM zero_col_columnar; count --------------------------------------------------------------------- 2 (1 row) EXPLAIN (costs off, summary off) SELECT * FROM zero_col_columnar; QUERY PLAN --------------------------------------------------------------------- Custom Scan (ColumnarScan) on zero_col_columnar Columnar Projected Columns: (2 rows) INSERT INTO zero_col_columnar DEFAULT VALUES; INSERT INTO zero_col_columnar DEFAULT VALUES; INSERT INTO zero_col_columnar DEFAULT VALUES; SELECT * FROM zero_col_columnar; -- (5 rows) SELECT count(*) FROM zero_col_columnar; count --------------------------------------------------------------------- 5 (1 row) EXPLAIN (costs off, summary off) SELECT * FROM zero_col_columnar; QUERY PLAN --------------------------------------------------------------------- Custom Scan (ColumnarScan) on zero_col_columnar Columnar Projected Columns: (2 rows) VACUUM VERBOSE zero_col_columnar; INFO: statistics for "zero_col_columnar": storage id: xxxxx total file size: 16384, total data size: 0 compression rate: 1.00x total row count: 5, stripe count: 4, average rows per stripe: 1 chunk count: 0, containing data for dropped columns: 0 ANALYZE zero_col_columnar; VACUUM FULL zero_col_columnar; SELECT * FROM zero_col_columnar; -- (5 rows) TRUNCATE zero_col_columnar; SELECT * FROM zero_col_columnar; -- (0 rows) DROP TABLE zero_col_columnar; CREATE TABLE zero_col_columnar(a int) USING columnar; INSERT INTO zero_col_columnar SELECT i FROM generate_series(1, 5) i; alter table zero_col_columnar drop column a; SELECT * FROM zero_col_columnar; -- (5 rows) INSERT INTO zero_col_columnar DEFAULT VALUES; INSERT INTO zero_col_columnar DEFAULT VALUES; INSERT INTO zero_col_columnar DEFAULT VALUES; SELECT * FROM zero_col_columnar; -- (8 rows) VACUUM VERBOSE zero_col_columnar; INFO: statistics for "zero_col_columnar": storage id: xxxxx total file size: 49152, total data size: 60 compression rate: 0.40x total row count: 8, stripe count: 4, average rows per stripe: 2 chunk count: 4, containing data for dropped columns: 4, zstd compressed: 4 ANALYZE zero_col_columnar; VACUUM FULL zero_col_columnar; SELECT * FROM zero_col_columnar; -- (8 rows) -- Add constraints -- Add a CHECK constraint CREATE TABLE products ( product_no integer, name text, price int CONSTRAINT price_constraint CHECK (price > 0) ) USING columnar; -- first insert should fail INSERT INTO products VALUES (1, 'bread', 0); ERROR: new row for relation "products" violates check constraint "price_constraint" DETAIL: Failing row contains (1, bread, 0). INSERT INTO products VALUES (1, 'bread', 10); ALTER TABLE products ADD CONSTRAINT dummy_constraint CHECK (price > product_no); -- first insert should fail INSERT INTO products VALUES (2, 'shampoo', 1); ERROR: new row for relation "products" violates check constraint "dummy_constraint" DETAIL: Failing row contains (2, shampoo, 1). INSERT INTO products VALUES (2, 'shampoo', 20); ALTER TABLE products DROP CONSTRAINT dummy_constraint; INSERT INTO products VALUES (3, 'pen', 2); SELECT * FROM products ORDER BY 1; product_no | name | price --------------------------------------------------------------------- 1 | bread | 10 2 | shampoo | 20 3 | pen | 2 (3 rows) -- Add a UNIQUE constraint CREATE TABLE products_unique ( product_no integer UNIQUE, name text, price numeric ) USING columnar; ALTER TABLE products ADD COLUMN store_id text UNIQUE; -- Add a PRIMARY KEY constraint CREATE TABLE products_primary ( product_no integer PRIMARY KEY, name text, price numeric ) USING columnar; BEGIN; ALTER TABLE products DROP COLUMN store_id; ALTER TABLE products ADD COLUMN store_id text PRIMARY KEY; ERROR: column "store_id" contains null values ROLLBACK; -- Add an EXCLUSION constraint (should fail) CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) ) USING columnar; ERROR: unsupported access method for the index on columnar table circles -- Row level security CREATE TABLE public.row_level_security_col (id int, pgUser CHARACTER VARYING) USING columnar; CREATE USER user1; NOTICE: not propagating CREATE ROLE/USER commands to worker nodes HINT: Connect to worker nodes directly to manually create all necessary users and roles. CREATE USER user2; NOTICE: not propagating CREATE ROLE/USER commands to worker nodes HINT: Connect to worker nodes directly to manually create all necessary users and roles. INSERT INTO public.row_level_security_col VALUES (1, 'user1'), (2, 'user2'); GRANT SELECT, UPDATE, INSERT, DELETE ON public.row_level_security_col TO user1; GRANT SELECT, UPDATE, INSERT, DELETE ON public.row_level_security_col TO user2; CREATE POLICY policy_col ON public.row_level_security_col FOR ALL TO PUBLIC USING (pgUser = current_user); ALTER TABLE public.row_level_security_col ENABLE ROW LEVEL SECURITY; SELECT * FROM public.row_level_security_col ORDER BY 1; id | pguser --------------------------------------------------------------------- 1 | user1 2 | user2 (2 rows) SET ROLE user1; SELECT * FROM public.row_level_security_col; id | pguser --------------------------------------------------------------------- 1 | user1 (1 row) SET ROLE user2; SELECT * FROM public.row_level_security_col; id | pguser --------------------------------------------------------------------- 2 | user2 (1 row) RESET ROLE; DROP TABLE public.row_level_security_col; DROP USER user1; DROP USER user2; SET client_min_messages TO WARNING; DROP SCHEMA columnar_alter CASCADE;