@@ -160,7 +160,7 @@ typedef struct AlteredTableInfo
160
160
bool new_notnull ; /* T if we added new NOT NULL constraints */
161
161
int rewrite ; /* Reason for forced rewrite, if any */
162
162
Oid newTableSpace ; /* new tablespace; 0 means no change */
163
- bool chgPersistence ; /* T if SET LOGGED/UNLOGGED is used */
163
+ bool chgPersistence ; /* T if SET LOGGED/UNLOGGED/CONSTANT is used */
164
164
char newrelpersistence ; /* if above is true */
165
165
/* Objects to rebuild after completing ALTER TYPE operations */
166
166
List * changedConstraintOids ; /* OIDs of constraints to rebuild */
@@ -402,7 +402,8 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
402
402
static ObjectAddress ATExecClusterOn (Relation rel , const char * indexName ,
403
403
LOCKMODE lockmode );
404
404
static void ATExecDropCluster (Relation rel , LOCKMODE lockmode );
405
- static bool ATPrepChangePersistence (Relation rel , bool toLogged );
405
+ static void ATExecSetConstant (Relation rel , LOCKMODE lockmode );
406
+ static bool ATPrepChangePersistence (Relation rel , char newrelpersistence );
406
407
static void ATPrepSetTableSpace (AlteredTableInfo * tab , Relation rel ,
407
408
char * tablespacename , LOCKMODE lockmode );
408
409
static void ATExecSetTableSpace (Oid tableOid , Oid newTableSpace , LOCKMODE lockmode );
@@ -3036,6 +3037,7 @@ AlterTableGetLockLevel(List *cmds)
3036
3037
3037
3038
case AT_SetLogged :
3038
3039
case AT_SetUnLogged :
3040
+ case AT_SetConstant :
3039
3041
cmd_lockmode = AccessExclusiveLock ;
3040
3042
break ;
3041
3043
@@ -3258,7 +3260,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3258
3260
break ;
3259
3261
case AT_SetLogged : /* SET LOGGED */
3260
3262
ATSimplePermissions (rel , ATT_TABLE );
3261
- tab -> chgPersistence = ATPrepChangePersistence (rel , true );
3263
+ tab -> chgPersistence = ATPrepChangePersistence (rel , RELPERSISTENCE_PERMANENT );
3262
3264
/* force rewrite if necessary; see comment in ATRewriteTables */
3263
3265
if (tab -> chgPersistence )
3264
3266
{
@@ -3269,7 +3271,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3269
3271
break ;
3270
3272
case AT_SetUnLogged : /* SET UNLOGGED */
3271
3273
ATSimplePermissions (rel , ATT_TABLE );
3272
- tab -> chgPersistence = ATPrepChangePersistence (rel , false );
3274
+ tab -> chgPersistence = ATPrepChangePersistence (rel , RELPERSISTENCE_UNLOGGED );
3273
3275
/* force rewrite if necessary; see comment in ATRewriteTables */
3274
3276
if (tab -> chgPersistence )
3275
3277
{
@@ -3278,6 +3280,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3278
3280
}
3279
3281
pass = AT_PASS_MISC ;
3280
3282
break ;
3283
+ case AT_SetConstant : /* SET CONSTANT */
3284
+ ATSimplePermissions (rel , ATT_TABLE );
3285
+ tab -> chgPersistence = ATPrepChangePersistence (rel , RELPERSISTENCE_CONSTANT );
3286
+ /* We do not rewrite relation, just freeze it in ATExecSetConstant. */
3287
+ if (tab -> chgPersistence )
3288
+ tab -> newrelpersistence = RELPERSISTENCE_CONSTANT ;
3289
+ pass = AT_PASS_MISC ;
3290
+ break ;
3281
3291
case AT_AddOids : /* SET WITH OIDS */
3282
3292
ATSimplePermissions (rel , ATT_TABLE | ATT_FOREIGN_TABLE );
3283
3293
if (!rel -> rd_rel -> relhasoids || recursing )
@@ -3579,6 +3589,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
3579
3589
case AT_SetLogged : /* SET LOGGED */
3580
3590
case AT_SetUnLogged : /* SET UNLOGGED */
3581
3591
break ;
3592
+ case AT_SetConstant : /* SET CONSTANT */
3593
+ ATExecSetConstant (rel , lockmode );
3594
+ break ;
3582
3595
case AT_AddOids : /* SET WITH OIDS */
3583
3596
/* Use the ADD COLUMN code, unless prep decided to do nothing */
3584
3597
if (cmd -> def != NULL )
@@ -6312,6 +6325,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
6312
6325
(errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
6313
6326
errmsg ("constraints on temporary tables must involve temporary tables of this session" )));
6314
6327
break ;
6328
+ case RELPERSISTENCE_CONSTANT :
6329
+ ereport (ERROR ,
6330
+ (errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
6331
+ errmsg ("constraints on CONSTANT tables are not supported" )));
6332
+ break ;
6333
+
6315
6334
}
6316
6335
6317
6336
/*
@@ -9350,6 +9369,74 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
9350
9369
mark_index_clustered (rel , InvalidOid , false);
9351
9370
}
9352
9371
9372
+
9373
+ static void
9374
+ setRelpersistenceConstant (Relation rel )
9375
+ {
9376
+ Relation pg_class ;
9377
+ Oid relid ;
9378
+ HeapTuple tuple ;
9379
+
9380
+ relid = RelationGetRelid (rel );
9381
+
9382
+ pg_class = heap_open (RelationRelationId , RowExclusiveLock );
9383
+
9384
+ tuple = SearchSysCacheCopy1 (RELOID , ObjectIdGetDatum (relid ));
9385
+
9386
+ if (!HeapTupleIsValid (tuple ))
9387
+ elog (ERROR , "cache lookup failed for relation %u" , relid );
9388
+
9389
+ ((Form_pg_class ) GETSTRUCT (tuple ))-> relpersistence = RELPERSISTENCE_CONSTANT ;
9390
+ simple_heap_update (pg_class , & tuple -> t_self , tuple );
9391
+
9392
+ /* keep catalog indexes current */
9393
+ CatalogUpdateIndexes (pg_class , tuple );
9394
+ /* Invalidate cached info about relation. */
9395
+ CacheInvalidateRelcache (rel );
9396
+
9397
+ heap_close (pg_class , RowExclusiveLock );
9398
+ heap_freetuple (tuple );
9399
+ }
9400
+
9401
+ /*
9402
+ * Change relation relpersistence to RELPERSISTENCE_CONSTANT.
9403
+ */
9404
+ static void
9405
+ ATExecSetConstant (Relation rel , LOCKMODE lockmode )
9406
+ {
9407
+ List * index_oid_list ;
9408
+ ListCell * i ;
9409
+
9410
+ if (rel -> rd_rel -> relkind != RELKIND_RELATION
9411
+ && rel -> rd_rel -> relkind != RELKIND_TOASTVALUE )
9412
+ elog (ERROR , "cannot apply SET CONSTANT to relation %s, because it's not a table." ,
9413
+ NameStr (rel -> rd_rel -> relname ));
9414
+ setRelpersistenceConstant (rel );
9415
+
9416
+ /* Find all the indexes belonging to this relation */
9417
+ index_oid_list = RelationGetIndexList (rel );
9418
+
9419
+ /* For each index, change its relpersistence */
9420
+ foreach (i , index_oid_list )
9421
+ {
9422
+ Relation indexRelation = index_open (lfirst_oid (i ), lockmode );
9423
+ setRelpersistenceConstant (indexRelation );
9424
+ index_close (indexRelation , NoLock );
9425
+ }
9426
+
9427
+ list_free (index_oid_list );
9428
+
9429
+ /* If it has a toast table, change its relpersistence.
9430
+ * And also recursevily for toast_index
9431
+ */
9432
+ if (rel -> rd_rel -> reltoastrelid != InvalidOid )
9433
+ {
9434
+ Relation toastRelation = heap_open (rel -> rd_rel -> reltoastrelid , lockmode );
9435
+ ATExecSetConstant (toastRelation , lockmode );
9436
+ heap_close (toastRelation , NoLock );
9437
+ }
9438
+ }
9439
+
9353
9440
/*
9354
9441
* ALTER TABLE SET TABLESPACE
9355
9442
*/
@@ -11290,46 +11377,55 @@ ATExecGenericOptions(Relation rel, List *options)
11290
11377
}
11291
11378
11292
11379
/*
11293
- * Preparation phase for SET LOGGED/UNLOGGED
11380
+ * Preparation phase for SET LOGGED/UNLOGGED/CONSTANT
11294
11381
*
11295
11382
* This verifies that we're not trying to change a temp table. Also,
11296
11383
* existing foreign key constraints are checked to avoid ending up with
11297
11384
* permanent tables referencing unlogged tables.
11385
+ * Foreign key constraints on CONSTANT tables are not allowed.
11298
11386
*
11299
11387
* Return value is false if the operation is a no-op (in which case the
11300
11388
* checks are skipped), otherwise true.
11301
11389
*/
11302
11390
static bool
11303
- ATPrepChangePersistence (Relation rel , bool toLogged )
11391
+ ATPrepChangePersistence (Relation rel , char newrelpersistence )
11304
11392
{
11305
11393
Relation pg_constraint ;
11306
11394
HeapTuple tuple ;
11307
11395
SysScanDesc scan ;
11308
11396
ScanKeyData skey [1 ];
11397
+ bool toLogged = false;
11398
+
11399
+ /*
11400
+ * When we track constraints, constant tables behaves just like
11401
+ * permanent ones.
11402
+ */
11403
+ if (newrelpersistence == RELPERSISTENCE_PERMANENT
11404
+ || newrelpersistence == RELPERSISTENCE_CONSTANT )
11405
+ toLogged = true;
11406
+
11407
+ /* Nothing to do */
11408
+ if (rel -> rd_rel -> relpersistence == newrelpersistence )
11409
+ return false;
11309
11410
11310
11411
/*
11311
- * Disallow changing status for a temp table. Also verify whether we can
11312
- * get away with doing nothing; in such cases we don't need to run the
11313
- * checks below, either.
11412
+ * Disallow changing status for a temp and constant tables.
11314
11413
*/
11315
11414
switch (rel -> rd_rel -> relpersistence )
11316
11415
{
11317
11416
case RELPERSISTENCE_TEMP :
11318
11417
ereport (ERROR ,
11319
11418
(errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
11320
- errmsg ("cannot change logged status of table \"%s\" because it is temporary" ,
11419
+ errmsg ("cannot change persistence of table \"%s\" because it is temporary" ,
11321
11420
RelationGetRelationName (rel )),
11322
11421
errtable (rel )));
11323
11422
break ;
11324
- case RELPERSISTENCE_PERMANENT :
11325
- if (toLogged )
11326
- /* nothing to do */
11327
- return false;
11328
- break ;
11329
- case RELPERSISTENCE_UNLOGGED :
11330
- if (!toLogged )
11331
- /* nothing to do */
11332
- return false;
11423
+ case RELPERSISTENCE_CONSTANT :
11424
+ ereport (ERROR ,
11425
+ (errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
11426
+ errmsg ("cannot change persistence of table \"%s\" because it is constant" ,
11427
+ RelationGetRelationName (rel )),
11428
+ errtable (rel )));
11333
11429
break ;
11334
11430
}
11335
11431
@@ -11363,15 +11459,23 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
11363
11459
Relation foreignrel ;
11364
11460
11365
11461
/* the opposite end of what we used as scankey */
11366
- foreignrelid = toLogged ? con -> confrelid : con -> conrelid ;
11462
+ foreignrelid = toLogged ? con -> confrelid : con -> conrelid ;
11367
11463
11368
11464
/* ignore if self-referencing */
11369
11465
if (RelationGetRelid (rel ) == foreignrelid )
11370
11466
continue ;
11371
11467
11372
11468
foreignrel = relation_open (foreignrelid , AccessShareLock );
11373
11469
11374
- if (toLogged )
11470
+ if (newrelpersistence == RELPERSISTENCE_CONSTANT )
11471
+ ereport (ERROR ,
11472
+ (errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
11473
+ errmsg ("could not change table \"%s\" to constant because it references table \"%s\"" ,
11474
+ RelationGetRelationName (rel ),
11475
+ RelationGetRelationName (foreignrel )),
11476
+ errtableconstraint (rel , NameStr (con -> conname ))));
11477
+
11478
+ if (newrelpersistence == RELPERSISTENCE_PERMANENT )
11375
11479
{
11376
11480
if (foreignrel -> rd_rel -> relpersistence != RELPERSISTENCE_PERMANENT )
11377
11481
ereport (ERROR ,
@@ -11381,7 +11485,7 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
11381
11485
RelationGetRelationName (foreignrel )),
11382
11486
errtableconstraint (rel , NameStr (con -> conname ))));
11383
11487
}
11384
- else
11488
+ if ( newrelpersistence == RELPERSISTENCE_UNLOGGED )
11385
11489
{
11386
11490
if (foreignrel -> rd_rel -> relpersistence == RELPERSISTENCE_PERMANENT )
11387
11491
ereport (ERROR ,
0 commit comments