Skip to content

Commit b1b2380

Browse files
committed
Add support for ALTER COLUMN SET NOT NULL
Since `SET NOT NULL` will only do a full table scan of the table to verify that there are no nulls, this should work without having to modify any data in the table. However, it does not work for chunks using the `heap` table access method if they are compressed since it uses a normal table scan, so adding a check for this.
1 parent 53299b9 commit b1b2380

10 files changed

+302
-74
lines changed

src/process_utility.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ check_alter_table_allowed_on_ht_with_compression(Hypertable *ht, AlterTableStmt
281281
case AT_ReAddStatistics:
282282
case AT_SetCompression:
283283
case AT_DropNotNull:
284+
case AT_SetNotNull:
284285
#if PG15_GE
285286
case AT_SetAccessMethod:
286287
#endif
@@ -2563,11 +2564,50 @@ process_altertable_validate_constraint_end(Hypertable *ht, AlterTableCmd *cmd)
25632564
foreach_chunk(ht, validate_hypertable_constraint, cmd);
25642565
}
25652566

2567+
/*
2568+
* Validate that SET NOT NULL is ok for this chunk.
2569+
*
2570+
* Throws an error if SET NOT NULL on this chunk is not allowed, right now,
2571+
* SET NOT NULL is allowed on chunks that are either a fully decompressed, or
2572+
* are using the Hypercore table access method.
2573+
*/
2574+
static void
2575+
validate_set_not_null(Hypertable *ht, Oid chunk_relid, void *arg)
2576+
{
2577+
Chunk *chunk = ts_chunk_get_by_relid(chunk_relid, true);
2578+
if (ts_chunk_is_compressed(chunk) && !ts_is_hypercore_am(chunk->amoid))
2579+
{
2580+
ereport(ERROR,
2581+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2582+
errmsg("operation not supported on compressed chunks not using the "
2583+
"\"hypercore\" table access method"),
2584+
errdetail("Chunk %s.%s is using the heap table access method and has compressed "
2585+
"data.",
2586+
NameStr(chunk->fd.schema_name),
2587+
NameStr(chunk->fd.table_name)),
2588+
errhint("Either decompress all chunks of the hypertable or use \"ALTER TABLE "
2589+
"%s.%s SET ACCESS METHOD hypercore\" on all chunks to change access "
2590+
"method.",
2591+
NameStr(chunk->fd.schema_name),
2592+
NameStr(chunk->fd.table_name))));
2593+
}
2594+
}
2595+
2596+
/*
2597+
* This function checks that we are not dropping NOT NULL from bad columns and
2598+
* that all chunks support the modification.
2599+
*/
25662600
static void
2567-
process_altertable_drop_not_null(Hypertable *ht, AlterTableCmd *cmd)
2601+
process_altertable_alter_not_null_start(Hypertable *ht, AlterTableCmd *cmd)
25682602
{
25692603
int i;
25702604

2605+
if (cmd->subtype == AT_SetNotNull)
2606+
foreach_chunk(ht, validate_set_not_null, cmd);
2607+
2608+
if (cmd->subtype != AT_DropNotNull)
2609+
return;
2610+
25712611
for (i = 0; i < ht->space->num_dimensions; i++)
25722612
{
25732613
Dimension *dim = &ht->space->dimensions[i];
@@ -3803,9 +3843,10 @@ process_altertable_start_table(ProcessUtilityArgs *args)
38033843
verify_constraint_hypertable(ht, cmd->def);
38043844
}
38053845
break;
3846+
case AT_SetNotNull:
38063847
case AT_DropNotNull:
38073848
if (ht != NULL)
3808-
process_altertable_drop_not_null(ht, cmd);
3849+
process_altertable_alter_not_null_start(ht, cmd);
38093850
break;
38103851
case AT_AddColumn:
38113852
#if PG16_LT
@@ -4187,6 +4228,8 @@ process_altertable_end_subcmd(Hypertable *ht, Node *parsetree, ObjectAddress *ob
41874228
case AT_DropCluster:
41884229
foreach_chunk(ht, process_altertable_chunk, cmd);
41894230
break;
4231+
case AT_SetNotNull:
4232+
case AT_DropNotNull:
41904233
case AT_SetRelOptions:
41914234
case AT_ResetRelOptions:
41924235
case AT_ReplaceRelOptions:
@@ -4213,8 +4256,6 @@ process_altertable_end_subcmd(Hypertable *ht, Node *parsetree, ObjectAddress *ob
42134256
case AT_SetStorage:
42144257
case AT_ColumnDefault:
42154258
case AT_CookedColumnDefault:
4216-
case AT_SetNotNull:
4217-
case AT_DropNotNull:
42184259
case AT_AddOf:
42194260
case AT_DropOf:
42204261
case AT_AddIdentity:
@@ -4494,8 +4535,8 @@ process_reassign_owned_start(ProcessUtilityArgs *args)
44944535
Oid newrole_oid = get_rolespec_oid(stmt->newrole, false);
44954536
HeapTuple tuple = ts_scanner_fetch_heap_tuple(ti, false, &should_free);
44964537

4497-
/* We do not need to check privileges here since ReassignOwnedObjects() will check the
4498-
* privileges and error out if they are not correct. */
4538+
/* We do not need to check privileges here since ReassignOwnedObjects() will check
4539+
* the privileges and error out if they are not correct. */
44994540
ts_bgw_job_update_owner(ti->scanrel, tuple, ts_scanner_get_tupledesc(ti), newrole_oid);
45004541

45014542
if (should_free)
@@ -4631,8 +4672,8 @@ process_create_stmt(ProcessUtilityArgs *args)
46314672
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
46324673
errmsg("hypercore access method not supported on \"%s\"", stmt->relation->relname),
46334674
errdetail("The hypercore access method is only supported for hypertables."),
4634-
errhint("It does not make sense to set the default access method for all tables "
4635-
"to \"%s\" since it is only supported for hypertables.",
4675+
errhint("It does not make sense to set the default access method for all "
4676+
"tables to \"%s\" since it is only supported for hypertables.",
46364677
TS_HYPERCORE_TAM_NAME));
46374678

46384679
return DDL_CONTINUE;

tsl/src/hypercore/utils.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <catalog/pg_class.h>
1313
#include <commands/defrem.h>
1414
#include <nodes/makefuncs.h>
15+
#include <postgres_ext.h>
1516
#include <storage/lmgr.h>
1617
#include <storage/lockdefs.h>
1718
#include <utils/builtins.h>

tsl/test/expected/compression_ddl.out

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2591,4 +2591,28 @@ SELECT count(*) FROM test2 WHERE i IS NULL;
25912591
1
25922592
(1 row)
25932593

2594-
SET client_min_messages = NOTICE;
2594+
SELECT count(compress_chunk(ch)) FROM show_chunks('test2') ch;
2595+
count
2596+
-------
2597+
28
2598+
(1 row)
2599+
2600+
SELECT count(*) FROM test2 WHERE i IS NULL;
2601+
count
2602+
-------
2603+
1
2604+
(1 row)
2605+
2606+
\set ON_ERROR_STOP 0
2607+
ALTER TABLE test2 ALTER COLUMN i SET NOT NULL;
2608+
ERROR: operation not supported on compressed chunks not using the "hypercore" table access method
2609+
DELETE FROM test2 WHERE i IS NULL;
2610+
SELECT count(*) FROM test2 WHERE i IS NULL;
2611+
count
2612+
-------
2613+
0
2614+
(1 row)
2615+
2616+
ALTER TABLE test2 ALTER COLUMN i SET NOT NULL;
2617+
ERROR: operation not supported on compressed chunks not using the "hypercore" table access method
2618+
\set ON_ERROR_STOP 1

tsl/test/expected/compression_errors-14.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ DETAIL: Cannot drop column that is a hypertable partitioning (space or time) di
239239
ALTER TABLE foo DROP COLUMN b;
240240
ERROR: cannot drop orderby or segmentby column from a hypertable with compression enabled
241241
ALTER TABLE foo ALTER COLUMN t SET NOT NULL;
242-
ERROR: operation not supported on hypertables that have compression enabled
242+
ERROR: column "t" of relation "_hyper_10_2_chunk" contains null values
243243
ALTER TABLE foo RESET (timescaledb.compress);
244244
ERROR: compression options cannot be reset
245245
--can add constraints as long as no data is compressed

tsl/test/expected/compression_errors-15.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ DETAIL: Cannot drop column that is a hypertable partitioning (space or time) di
239239
ALTER TABLE foo DROP COLUMN b;
240240
ERROR: cannot drop orderby or segmentby column from a hypertable with compression enabled
241241
ALTER TABLE foo ALTER COLUMN t SET NOT NULL;
242-
ERROR: operation not supported on hypertables that have compression enabled
242+
ERROR: column "t" of relation "_hyper_10_2_chunk" contains null values
243243
ALTER TABLE foo RESET (timescaledb.compress);
244244
ERROR: compression options cannot be reset
245245
--can add constraints as long as no data is compressed

tsl/test/expected/compression_errors-16.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ DETAIL: Cannot drop column that is a hypertable partitioning (space or time) di
239239
ALTER TABLE foo DROP COLUMN b;
240240
ERROR: cannot drop orderby or segmentby column from a hypertable with compression enabled
241241
ALTER TABLE foo ALTER COLUMN t SET NOT NULL;
242-
ERROR: operation not supported on hypertables that have compression enabled
242+
ERROR: column "t" of relation "_hyper_10_2_chunk" contains null values
243243
ALTER TABLE foo RESET (timescaledb.compress);
244244
ERROR: compression options cannot be reset
245245
--can add constraints as long as no data is compressed

tsl/test/expected/compression_errors-17.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ DETAIL: Cannot drop column that is a hypertable partitioning (space or time) di
239239
ALTER TABLE foo DROP COLUMN b;
240240
ERROR: cannot drop orderby or segmentby column from a hypertable with compression enabled
241241
ALTER TABLE foo ALTER COLUMN t SET NOT NULL;
242-
ERROR: operation not supported on hypertables that have compression enabled
242+
ERROR: column "t" of relation "_hyper_10_2_chunk" contains null values
243243
ALTER TABLE foo RESET (timescaledb.compress);
244244
ERROR: compression options cannot be reset
245245
--can add constraints as long as no data is compressed

0 commit comments

Comments
 (0)