Support disabling compression when foreign keys are present

Fix failure with disabling compression when no
compressed chunks are present and the table has foreign
key constraints
This commit is contained in:
gayyappan 2020-02-11 17:21:45 -05:00 committed by gayyappan
parent 2702140fa3
commit 565cca795a
4 changed files with 167 additions and 80 deletions

View File

@ -15,9 +15,11 @@ accidentally triggering the load of a previous DB version.**
* #1665 Add ignore_invalidation_older_than to timescaledb_information.continuous_aggregates view
* #1668 Cannot add dimension if hypertable has empty chunks
* #1674 Fix time_bucket_gapfill's interaction with GROUP BY
* #1687 Fix issue with disabling compression when foreign keys are present
**Thanks**
* @RJPhillips01 for reporting an issue with drop chunks.
* @b4eEx for reporting an issue with disabling compression.
## 1.6.0 (2020-01-14)

View File

@ -806,75 +806,18 @@ validate_existing_constraints(Hypertable *ht, CompressColInfo *colinfo)
return conlist;
}
/*
* enables compression for the passed in table by
* creating a compression hypertable with special properties
Note:
caller should check security permissions
*/
bool
tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht,
WithClauseResult *with_clause_options)
static void
check_modify_compression_options(Hypertable *ht, WithClauseResult *with_clause_options)
{
int32 compress_htid;
struct CompressColInfo compress_cols;
bool compress_enable = DatumGetBool(with_clause_options[CompressEnabled].parsed);
Oid ownerid;
List *segmentby_cols;
List *orderby_cols;
bool compression_already_enabled;
bool compression_has_policy;
bool compressed_chunks_exist;
ContinuousAggHypertableStatus caggstat;
List *constraint_list = NIL;
/*check this is not a special internally created hypertable
* continuous agg table
* compression hypertable
*/
caggstat = ts_continuous_agg_hypertable_status(ht->fd.id);
if (!(caggstat == HypertableIsRawTable || caggstat == HypertableIsNotContinuousAgg))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("continuous aggregate tables do not support compression")));
}
if (ht->fd.compressed)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot compress internal compression hypertable")));
}
/* Lock the uncompressed ht in exclusive mode and keep till end of txn */
LockRelationOid(ht->main_table_relid, AccessExclusiveLock);
/* reload info after lock */
ht = ts_hypertable_get_by_id(ht->fd.id);
ownerid = ts_rel_get_owner(ht->main_table_relid);
segmentby_cols = ts_compress_hypertable_parse_segment_by(with_clause_options, ht);
orderby_cols = ts_compress_hypertable_parse_order_by(with_clause_options, ht);
orderby_cols = add_time_to_order_by_if_not_included(orderby_cols, segmentby_cols, ht);
compression_already_enabled = TS_HYPERTABLE_HAS_COMPRESSION(ht);
bool compression_already_enabled = TS_HYPERTABLE_HAS_COMPRESSION(ht);
compressed_chunks_exist =
compression_already_enabled && ts_chunk_exists_with_compression(ht->fd.id);
compression_has_policy =
compression_already_enabled && ts_bgw_policy_compress_chunks_find_by_hypertable(ht->fd.id);
if (!compress_enable)
{
if (!with_clause_options[CompressOrderBy].is_default ||
!with_clause_options[CompressSegmentBy].is_default)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg(
"cannot set additional compression options when disabling compression")));
if (!compression_already_enabled)
/* compression is not enabled, so just return */
return false;
}
if (compressed_chunks_exist)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -917,7 +860,98 @@ tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht,
errmsg("need to specify timescaledb.compress_segmentby if it was previously "
"set")));
}
}
static void
drop_existing_compression_table(Hypertable *ht)
{
Hypertable *compressed = ts_hypertable_get_by_id(ht->fd.compressed_hypertable_id);
if (compressed == NULL)
elog(ERROR, "compression enabled but no compressed hypertable found");
/* need to drop the old compressed hypertable in case the segment by columns changed (and
* thus the column types of compressed hypertable need to change) */
ts_hypertable_drop(compressed, DROP_RESTRICT);
ts_hypertable_compression_delete_by_hypertable_id(ht->fd.id);
ts_hypertable_unset_compressed_id(ht);
}
static bool
disable_compression(Hypertable *ht, WithClauseResult *with_clause_options)
{
bool compression_already_enabled = TS_HYPERTABLE_HAS_COMPRESSION(ht);
if (!with_clause_options[CompressOrderBy].is_default ||
!with_clause_options[CompressSegmentBy].is_default)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set additional compression options when disabling compression")));
if (!compression_already_enabled)
/* compression is not enabled, so just return */
return false;
/* compression is enabled. can we turn it off? */
check_modify_compression_options(ht, with_clause_options);
drop_existing_compression_table(ht);
ts_hypertable_unset_compressed_id(ht);
return true;
}
/*
* enables compression for the passed in table by
* creating a compression hypertable with special properties
Note:
caller should check security permissions
*/
bool
tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht,
WithClauseResult *with_clause_options)
{
int32 compress_htid;
struct CompressColInfo compress_cols;
bool compress_enable = DatumGetBool(with_clause_options[CompressEnabled].parsed);
Oid ownerid;
List *segmentby_cols;
List *orderby_cols;
ContinuousAggHypertableStatus caggstat;
List *constraint_list = NIL;
/*check this is not a special internally created hypertable
* i.e. continuous agg table or compression hypertable
*/
caggstat = ts_continuous_agg_hypertable_status(ht->fd.id);
if (!(caggstat == HypertableIsRawTable || caggstat == HypertableIsNotContinuousAgg))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("continuous aggregate tables do not support compression")));
}
if (ht->fd.compressed)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot compress internal compression hypertable")));
}
/*check row security settings for the table */
if (ts_has_row_security(ht->main_table_relid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("compression cannot be used on table with row security")));
/* Lock the uncompressed ht in exclusive mode and keep till end of txn */
LockRelationOid(ht->main_table_relid, AccessExclusiveLock);
/* reload info after lock */
ht = ts_hypertable_get_by_id(ht->fd.id);
if (!compress_enable)
{
return disable_compression(ht, with_clause_options);
}
if (TS_HYPERTABLE_HAS_COMPRESSION(ht))
check_modify_compression_options(ht, with_clause_options);
ownerid = ts_rel_get_owner(ht->main_table_relid);
segmentby_cols = ts_compress_hypertable_parse_segment_by(with_clause_options, ht);
orderby_cols = ts_compress_hypertable_parse_order_by(with_clause_options, ht);
orderby_cols = add_time_to_order_by_if_not_included(orderby_cols, segmentby_cols, ht);
compresscolinfo_init(&compress_cols, ht->main_table_relid, segmentby_cols, orderby_cols);
/* check if we can create a compressed hypertable with existing constraints */
constraint_list = validate_existing_constraints(ht, &compress_cols);
@ -927,27 +961,10 @@ tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht,
LockRelationOid(catalog_get_table_id(ts_catalog_get(), HYPERTABLE_COMPRESSION),
RowExclusiveLock);
/*check row security settings for the table */
if (ts_has_row_security(ht->main_table_relid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("compression cannot be used on table with row security")));
if (compression_already_enabled || !compress_enable)
if (TS_HYPERTABLE_HAS_COMPRESSION(ht))
{
Hypertable *compressed = ts_hypertable_get_by_id(ht->fd.compressed_hypertable_id);
if (compressed == NULL)
elog(ERROR, "compression enabled but no compressed hypertable found");
/* need to drop the old compressed hypertable in case the segment by columns changed (and
* thus the column types of compressed hypertable need to change) */
ts_hypertable_drop(compressed, DROP_RESTRICT);
ts_hypertable_compression_delete_by_hypertable_id(ht->fd.id);
}
if (!compress_enable)
{
ts_hypertable_unset_compressed_id(ht);
return true;
/* compression is enabled */
drop_existing_compression_table(ht);
}
compress_htid = create_compression_table(ownerid, &compress_cols);

View File

@ -354,3 +354,44 @@ select device_id, d from table_constr order by device_id, d;
-----------+---
(0 rows)
--github issue 1661
--disable compression after enabling it on a table that has fk constraints
CREATE TABLE table_constr2( device_id integer,
timec integer ,
location integer ,
d integer references fortable(col),
primary key ( device_id, timec)
);
SELECT table_name from create_hypertable('table_constr2', 'timec', chunk_time_interval=> 10);
table_name
---------------
table_constr2
(1 row)
INSERT INTO fortable VALUES( 99 );
INSERT INTO table_constr2 VALUES( 1000, 10, 5, 99);
ALTER TABLE table_constr2 SET (timescaledb.compress, timescaledb.compress_segmentby = 'device_id');
ERROR: constraint "table_constr2_d_fkey" requires column "d" to be a timescaledb.compress_segmentby column for compression
ALTER TABLE table_constr2 SET (timescaledb.compress, timescaledb.compress_segmentby = 'device_id, d');
NOTICE: adding index _compressed_hypertable_21_device_id__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_21 USING BTREE(device_id, _ts_meta_sequence_num)
NOTICE: adding index _compressed_hypertable_21_d__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_21 USING BTREE(d, _ts_meta_sequence_num)
--compress a chunk and try to disable compression, it should fail --
SELECT ch1.schema_name|| '.' || ch1.table_name AS "CHUNK_NAME"
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht
WHERE ch1.hypertable_id = ht.id and ht.table_name like 'table_constr2' \gset
SELECT compress_chunk(:'CHUNK_NAME');
compress_chunk
------------------------------------------
_timescaledb_internal._hyper_20_10_chunk
(1 row)
ALTER TABLE table_constr2 set (timescaledb.compress=false);
ERROR: cannot change compression options as compressed chunks already exist for this table
--decompress all chunks and disable compression.
SELECT decompress_chunk(:'CHUNK_NAME');
decompress_chunk
------------------------------------------
_timescaledb_internal._hyper_20_10_chunk
(1 row)
ALTER TABLE table_constr2 SET (timescaledb.compress=false);

View File

@ -209,3 +209,30 @@ select device_id, d from table_constr order by device_id, d;
delete from fortable where col = 1 or col = 10;
select device_id, d from table_constr order by device_id, d;
--github issue 1661
--disable compression after enabling it on a table that has fk constraints
CREATE TABLE table_constr2( device_id integer,
timec integer ,
location integer ,
d integer references fortable(col),
primary key ( device_id, timec)
);
SELECT table_name from create_hypertable('table_constr2', 'timec', chunk_time_interval=> 10);
INSERT INTO fortable VALUES( 99 );
INSERT INTO table_constr2 VALUES( 1000, 10, 5, 99);
ALTER TABLE table_constr2 SET (timescaledb.compress, timescaledb.compress_segmentby = 'device_id');
ALTER TABLE table_constr2 SET (timescaledb.compress, timescaledb.compress_segmentby = 'device_id, d');
--compress a chunk and try to disable compression, it should fail --
SELECT ch1.schema_name|| '.' || ch1.table_name AS "CHUNK_NAME"
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht
WHERE ch1.hypertable_id = ht.id and ht.table_name like 'table_constr2' \gset
SELECT compress_chunk(:'CHUNK_NAME');
ALTER TABLE table_constr2 set (timescaledb.compress=false);
--decompress all chunks and disable compression.
SELECT decompress_chunk(:'CHUNK_NAME');
ALTER TABLE table_constr2 SET (timescaledb.compress=false);