Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ADD COLUMN IF NOT EXISTS error on compressed hypertable #4159

Merged
merged 1 commit into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion tsl/src/compression/create.c
Original file line number Diff line number Diff line change
Expand Up @@ -1063,10 +1063,17 @@ tsl_process_compress_table_add_column(Hypertable *ht, ColumnDef *orig_def)
Oid coloid;
int32 orig_htid = ht->fd.id;
char *colname = orig_def->colname;
TypeName *orig_typname = orig_def->typeName;

FormData_hypertable_compression *ht_comp =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the newly added lines to line 1067.
So the check would be the first thing in the function (before any initialization etc) .

ts_hypertable_compression_get_by_pkey(orig_htid, colname);
/* don't add column if it already exists */
if (ht_comp)
return;

TypeName *orig_typname = orig_def->typeName;
coloid = LookupTypeNameOid(NULL, orig_typname, false);
compresscolinfo_init_singlecolumn(&compress_cols, colname, coloid);

if (TS_HYPERTABLE_HAS_COMPRESSION_TABLE(ht))
{
int32 compress_htid = ht->fd.compressed_hypertable_id;
Expand Down
32 changes: 32 additions & 0 deletions tsl/test/expected/compression_errors.out
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,35 @@ SELECT config FROM _timescaledb_config.bgw_job WHERE id = :compressjob_id;
CALL run_job(:compressjob_id);
ERROR: job 1001 has null config
CONTEXT: PL/pgSQL function _timescaledb_internal.policy_compression(integer,jsonb) line 21 at RAISE
-- test ADD COLUMN IF NOT EXISTS
CREATE TABLE metric (time TIMESTAMPTZ NOT NULL, val FLOAT8 NOT NULL, dev_id INT4 NOT NULL);
SELECT create_hypertable('metric', 'time', 'dev_id', 10);
create_hypertable
----------------------
(23,public,metric,t)
(1 row)

ALTER TABLE metric SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'dev_id',
timescaledb.compress_orderby = 'time DESC'
);
INSERT INTO metric(time, val, dev_id)
SELECT s.*, 3.14+1, 1
FROM generate_series('2021-08-17 00:00:00'::timestamp,
'2021-08-17 00:02:00'::timestamp, '1 s'::interval) s;
SELECT compress_chunk(show_chunks('metric'));
compress_chunk
------------------------------------------
_timescaledb_internal._hyper_23_17_chunk
(1 row)

-- column does not exist the first time
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" VARCHAR ;
-- column already exists the second time
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" VARCHAR ;
NOTICE: column "medium" of relation "metric" already exists, skipping
-- also add one without IF NOT EXISTS
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
ERROR: column "medium_1" of relation "metric" already exists
85 changes: 85 additions & 0 deletions tsl/test/expected/dist_compression.out
Original file line number Diff line number Diff line change
Expand Up @@ -1388,3 +1388,88 @@ ORDER BY 1;
c2
(1 row)

-- test ADD COLUMN IF NOT EXISTS on a distributed hypertable
CREATE TABLE metric (time TIMESTAMPTZ NOT NULL, val FLOAT8 NOT NULL, dev_id INT4 NOT NULL);

SELECT create_distributed_hypertable('metric', 'time');
create_distributed_hypertable
-------------------------------
(10,public,metric,t)
(1 row)

ALTER TABLE metric SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'dev_id',
timescaledb.compress_orderby = 'time DESC'
);

INSERT INTO metric(time, val, dev_id)
SELECT s.*, 3.14+1, 1
FROM generate_series('2021-07-01 00:00:00'::timestamp,
'2021-08-17 00:02:00'::timestamp, '30 s'::interval) s;
SELECT compress_chunk(chunk)
FROM show_chunks('metric') AS chunk
ORDER BY chunk;
compress_chunk
-----------------------------------------------
_timescaledb_internal._dist_hyper_10_36_chunk
_timescaledb_internal._dist_hyper_10_37_chunk
_timescaledb_internal._dist_hyper_10_38_chunk
_timescaledb_internal._dist_hyper_10_39_chunk
_timescaledb_internal._dist_hyper_10_40_chunk
_timescaledb_internal._dist_hyper_10_41_chunk
_timescaledb_internal._dist_hyper_10_42_chunk
(7 rows)

-- make sure we have chunks on all data nodes
select * from timescaledb_information.chunks where hypertable_name like 'metric';
hypertable_schema | hypertable_name | chunk_schema | chunk_name | primary_dimension | primary_dimension_type | range_start | range_end | range_start_integer | range_end_integer | is_compressed | chunk_tablespace | data_nodes
-------------------+-----------------+-----------------------+-------------------------+-------------------+--------------------------+------------------------------+------------------------------+---------------------+-------------------+---------------+------------------+-------------------------
public | metric | _timescaledb_internal | _dist_hyper_10_36_chunk | time | timestamp with time zone | Wed Jun 30 17:00:00 2021 PDT | Wed Jul 07 17:00:00 2021 PDT | | | t | | {db_dist_compression_2}
public | metric | _timescaledb_internal | _dist_hyper_10_37_chunk | time | timestamp with time zone | Wed Jul 07 17:00:00 2021 PDT | Wed Jul 14 17:00:00 2021 PDT | | | t | | {db_dist_compression_3}
public | metric | _timescaledb_internal | _dist_hyper_10_38_chunk | time | timestamp with time zone | Wed Jul 14 17:00:00 2021 PDT | Wed Jul 21 17:00:00 2021 PDT | | | t | | {db_dist_compression_1}
public | metric | _timescaledb_internal | _dist_hyper_10_39_chunk | time | timestamp with time zone | Wed Jul 21 17:00:00 2021 PDT | Wed Jul 28 17:00:00 2021 PDT | | | t | | {db_dist_compression_2}
public | metric | _timescaledb_internal | _dist_hyper_10_40_chunk | time | timestamp with time zone | Wed Jul 28 17:00:00 2021 PDT | Wed Aug 04 17:00:00 2021 PDT | | | t | | {db_dist_compression_3}
public | metric | _timescaledb_internal | _dist_hyper_10_41_chunk | time | timestamp with time zone | Wed Aug 04 17:00:00 2021 PDT | Wed Aug 11 17:00:00 2021 PDT | | | t | | {db_dist_compression_1}
public | metric | _timescaledb_internal | _dist_hyper_10_42_chunk | time | timestamp with time zone | Wed Aug 11 17:00:00 2021 PDT | Wed Aug 18 17:00:00 2021 PDT | | | t | | {db_dist_compression_2}
(7 rows)

-- perform all combinations
-- [IF NOT EXISTS] - []
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" varchar;
-- [IF NOT EXISTS] - ["medium"]
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" varchar;
NOTICE: column "medium" of relation "metric" already exists, skipping
-- [] - []
ALTER TABLE metric ADD COLUMN "medium_1" varchar;
-- [] - ["medium_1"]
\set ON_ERROR_STOP 0
ALTER TABLE metric ADD COLUMN "medium_1" varchar;
ERROR: column "medium_1" of relation "metric" already exists

SELECT * FROM metric limit 5;
time | val | dev_id | medium | medium_1
------------------------------+------+--------+--------+----------
Wed Jul 21 16:59:30 2021 PDT | 4.14 | 1 | |
Wed Jul 21 16:59:00 2021 PDT | 4.14 | 1 | |
Wed Jul 21 16:58:30 2021 PDT | 4.14 | 1 | |
Wed Jul 21 16:58:00 2021 PDT | 4.14 | 1 | |
Wed Jul 21 16:57:30 2021 PDT | 4.14 | 1 | |
(5 rows)

SELECT * FROM metric where medium is not null;
time | val | dev_id | medium | medium_1
------+-----+--------+--------+----------
(0 rows)

-- INSERTs operate normally on the added column
INSERT INTO metric (time, val, dev_id, medium)
SELECT s.*, 3.14+1, 1, 'medium_value_text'
FROM generate_series('2021-08-18 00:00:00'::timestamp,
'2021-08-19 00:02:00'::timestamp, '30 s'::interval) s;
SELECT * FROM metric where medium is not null ORDER BY time LIMIT 1;
time | val | dev_id | medium | medium_1
------------------------------+------+--------+-------------------+----------
Wed Aug 18 00:00:00 2021 PDT | 4.14 | 1 | medium_value_text |
(1 row)

22 changes: 22 additions & 0 deletions tsl/test/sql/compression_errors.sql
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,25 @@ SELECT config FROM _timescaledb_config.bgw_job WHERE id = :compressjob_id;

--should fail
CALL run_job(:compressjob_id);

-- test ADD COLUMN IF NOT EXISTS
CREATE TABLE metric (time TIMESTAMPTZ NOT NULL, val FLOAT8 NOT NULL, dev_id INT4 NOT NULL);
SELECT create_hypertable('metric', 'time', 'dev_id', 10);
ALTER TABLE metric SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'dev_id',
timescaledb.compress_orderby = 'time DESC'
);

INSERT INTO metric(time, val, dev_id)
SELECT s.*, 3.14+1, 1
FROM generate_series('2021-08-17 00:00:00'::timestamp,
'2021-08-17 00:02:00'::timestamp, '1 s'::interval) s;
SELECT compress_chunk(show_chunks('metric'));
-- column does not exist the first time
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" VARCHAR ;
-- column already exists the second time
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" VARCHAR ;
-- also add one without IF NOT EXISTS
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
\set ON_ERROR_STOP 0
ALTER TABLE metric ADD COLUMN "medium_1" VARCHAR ;
\set ON_ERROR_STOP 1

42 changes: 42 additions & 0 deletions tsl/test/sql/dist_compression.sql
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,45 @@ INNER JOIN _timescaledb_catalog.hypertable ht
WHERE attname NOT IN ('time','device','o1','o2')
ORDER BY 1;

-- test ADD COLUMN IF NOT EXISTS on a distributed hypertable
CREATE TABLE metric (time TIMESTAMPTZ NOT NULL, val FLOAT8 NOT NULL, dev_id INT4 NOT NULL);

SELECT create_distributed_hypertable('metric', 'time');
ALTER TABLE metric SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'dev_id',
timescaledb.compress_orderby = 'time DESC'
);

INSERT INTO metric(time, val, dev_id)
SELECT s.*, 3.14+1, 1
FROM generate_series('2021-07-01 00:00:00'::timestamp,
'2021-08-17 00:02:00'::timestamp, '30 s'::interval) s;

SELECT compress_chunk(chunk)
FROM show_chunks('metric') AS chunk
ORDER BY chunk;

-- make sure we have chunks on all data nodes
select * from timescaledb_information.chunks where hypertable_name like 'metric';
-- perform all combinations
-- [IF NOT EXISTS] - []
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" varchar;
-- [IF NOT EXISTS] - ["medium"]
ALTER TABLE metric ADD COLUMN IF NOT EXISTS "medium" varchar;
-- [] - []
ALTER TABLE metric ADD COLUMN "medium_1" varchar;
-- [] - ["medium_1"]
\set ON_ERROR_STOP 0
ALTER TABLE metric ADD COLUMN "medium_1" varchar;

SELECT * FROM metric limit 5;
SELECT * FROM metric where medium is not null;

-- INSERTs operate normally on the added column
INSERT INTO metric (time, val, dev_id, medium)
SELECT s.*, 3.14+1, 1, 'medium_value_text'
FROM generate_series('2021-08-18 00:00:00'::timestamp,
'2021-08-19 00:02:00'::timestamp, '30 s'::interval) s;

SELECT * FROM metric where medium is not null ORDER BY time LIMIT 1;