@ -305,7 +305,7 @@ static void truncate_check_activity(Relation rel);
static void RangeVarCallbackForTruncate ( const RangeVar * relation ,
Oid relId , Oid oldRelId , void * arg ) ;
static List * MergeAttributes ( List * schema , List * supers , char relpersistence ,
bool is_partition , List * * supOids , List * * sup constr ) ;
bool is_partition , List * * supconstr ) ;
static bool MergeCheckConstraint ( List * constraints , char * name , Node * expr ) ;
static void MergeAttributesIntoExisting ( Relation child_rel , Relation parent_rel ) ;
static void MergeConstraintsIntoExisting ( Relation child_rel , Relation parent_rel ) ;
@ -446,7 +446,7 @@ static bool ATPrepChangePersistence(Relation rel, bool toLogged);
static void ATPrepSetTableSpace ( AlteredTableInfo * tab , Relation rel ,
const char * tablespacename , LOCKMODE lockmode ) ;
static void ATExecSetTableSpace ( Oid tableOid , Oid newTableSpace , LOCKMODE lockmode ) ;
static void ATExecPartedIdx SetTableSpace ( Relation rel , Oid newTableSpace ) ;
static void ATExecSetTableSpaceNoStorag e ( Relation rel , Oid newTableSpace ) ;
static void ATExecSetRelOptions ( Relation rel , List * defList ,
AlterTableType operation ,
LOCKMODE lockmode ) ;
@ -536,6 +536,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
static char * validnsps [ ] = HEAP_RELOPT_NAMESPACES ;
Oid ofTypeId ;
ObjectAddress address ;
LOCKMODE parentLockmode ;
/*
* Truncate relname to appropriate length ( probably a waste of time , as
@ -580,6 +581,46 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
( errcode ( ERRCODE_INSUFFICIENT_PRIVILEGE ) ,
errmsg ( " cannot create temporary table within security-restricted operation " ) ) ) ;
/*
* Determine the lockmode to use when scanning parents . A self - exclusive
* lock is needed here .
*
* For regular inheritance , if two backends attempt to add children to the
* same parent simultaneously , and that parent has no pre - existing
* children , then both will attempt to update the parent ' s relhassubclass
* field , leading to a " tuple concurrently updated " error . Also , this
* interlocks against a concurrent ANALYZE on the parent table , which
* might otherwise be attempting to clear the parent ' s relhassubclass
* field , if its previous children were recently dropped .
*
* If the child table is a partition , then we instead grab an exclusive
* lock on the parent because its partition descriptor will be changed by
* addition of the new partition .
*/
parentLockmode = ( stmt - > partbound ! = NULL ? AccessExclusiveLock :
ShareUpdateExclusiveLock ) ;
/* Determine the list of OIDs of the parents. */
inheritOids = NIL ;
foreach ( listptr , stmt - > inhRelations )
{
RangeVar * rv = ( RangeVar * ) lfirst ( listptr ) ;
Oid parentOid ;
parentOid = RangeVarGetRelid ( rv , parentLockmode , false ) ;
/*
* Reject duplications in the list of parents .
*/
if ( list_member_oid ( inheritOids , parentOid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DUPLICATE_TABLE ) ,
errmsg ( " relation \" %s \" would be inherited from more than once " ,
get_rel_name ( parentOid ) ) ) ) ;
inheritOids = lappend_oid ( inheritOids , parentOid ) ;
}
/*
* Select tablespace to use . If not specified , use default tablespace
* ( which may in turn default to database ' s default ) .
@ -588,6 +629,25 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
{
tablespaceId = get_tablespace_oid ( stmt - > tablespacename , false ) ;
}
else if ( stmt - > partbound )
{
HeapTuple tup ;
/*
* For partitions , when no other tablespace is specified , we default
* the tablespace to the parent partitioned table ' s .
*/
Assert ( list_length ( inheritOids ) = = 1 ) ;
tup = SearchSysCache1 ( RELOID ,
DatumGetObjectId ( linitial_oid ( inheritOids ) ) ) ;
tablespaceId = ( ( Form_pg_class ) GETSTRUCT ( tup ) ) - > reltablespace ;
if ( ! OidIsValid ( tablespaceId ) )
tablespaceId = GetDefaultTablespace ( stmt - > relation - > relpersistence ) ;
ReleaseSysCache ( tup ) ;
}
else
{
tablespaceId = GetDefaultTablespace ( stmt - > relation - > relpersistence ) ;
@ -646,10 +706,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* modified by MergeAttributes . )
*/
stmt - > tableElts =
MergeAttributes ( stmt - > tableElts , stmt - > inhRelation s,
MergeAttributes ( stmt - > tableElts , inheritOid s,
stmt - > relation - > relpersistence ,
stmt - > partbound ! = NULL ,
& inheritOids , & old_constraints ) ;
& old_constraints ) ;
/*
* Create a tuple descriptor from the relation schema . Note that this
@ -1781,12 +1841,11 @@ storage_name(char c)
* Input arguments :
* ' schema ' is the column / attribute definition for the table . ( It ' s a list
* of ColumnDef ' s . ) It is destructively changed .
* ' supers ' is a list of names ( as RangeVar nodes ) of parent relations .
* ' supers ' is a list of OIDs of parent relations , already locked by caller .
* ' relpersistence ' is a persistence type of the table .
* ' is_partition ' tells if the table is a partition
*
* Output arguments :
* ' supOids ' receives a list of the OIDs of the parent relations .
* ' supconstr ' receives a list of constraints belonging to the parents ,
* updated as necessary to be valid for the child .
*
@ -1834,11 +1893,10 @@ storage_name(char c)
*/
static List *
MergeAttributes ( List * schema , List * supers , char relpersistence ,
bool is_partition , List * * supOids , List * * sup constr )
bool is_partition , List * * supconstr )
{
ListCell * entry ;
List * inhSchema = NIL ;
List * parentOids = NIL ;
List * constraints = NIL ;
bool have_bogus_defaults = false ;
int child_attno ;
@ -1939,31 +1997,15 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
child_attno = 0 ;
foreach ( entry , supers )
{
RangeVar * parent = ( RangeVar * ) lfirst ( entry ) ;
Oid parent = lfirst_oid ( entry ) ;
Relation relation ;
TupleDesc tupleDesc ;
TupleConstr * constr ;
AttrNumber * newattno ;
AttrNumber parent_attno ;
/*
* A self - exclusive lock is needed here . If two backends attempt to
* add children to the same parent simultaneously , and that parent has
* no pre - existing children , then both will attempt to update the
* parent ' s relhassubclass field , leading to a " tuple concurrently
* updated " error. Also, this interlocks against a concurrent ANALYZE
* on the parent table , which might otherwise be attempting to clear
* the parent ' s relhassubclass field , if its previous children were
* recently dropped .
*
* If the child table is a partition , then we instead grab an
* exclusive lock on the parent because its partition descriptor will
* be changed by addition of the new partition .
*/
if ( ! is_partition )
relation = heap_openrv ( parent , ShareUpdateExclusiveLock ) ;
else
relation = heap_openrv ( parent , AccessExclusiveLock ) ;
/* caller already got lock */
relation = heap_open ( parent , NoLock ) ;
/*
* Check for active uses of the parent partitioned table in the
@ -1982,12 +2024,12 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " cannot inherit from partitioned table \" %s \" " ,
parent - > relname ) ) ) ;
RelationGetRelationName ( relation ) ) ) ) ;
if ( relation - > rd_rel - > relispartition & & ! is_partition )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " cannot inherit from partition \" %s \" " ,
parent - > relname ) ) ) ;
RelationGetRelationName ( relation ) ) ) ) ;
if ( relation - > rd_rel - > relkind ! = RELKIND_RELATION & &
relation - > rd_rel - > relkind ! = RELKIND_FOREIGN_TABLE & &
@ -1995,7 +2037,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " inherited relation \" %s \" is not a table or foreign table " ,
parent - > relname ) ) ) ;
RelationGetRelationName ( relation ) ) ) ) ;
/*
* If the parent is permanent , so must be all of its partitions . Note
@ -2017,7 +2059,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
errmsg ( ! is_partition
? " cannot inherit from temporary relation \" %s \" "
: " cannot create a permanent relation as partition of temporary relation \" %s \" " ,
parent - > relname ) ) ) ;
RelationGetRelationName ( relation ) ) ) ) ;
/* If existing rel is temp, it must belong to this session */
if ( relation - > rd_rel - > relpersistence = = RELPERSISTENCE_TEMP & &
@ -2036,17 +2078,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
aclcheck_error ( ACLCHECK_NOT_OWNER , get_relkind_objtype ( relation - > rd_rel - > relkind ) ,
RelationGetRelationName ( relation ) ) ;
/*
* Reject duplications in the list of parents .
*/
if ( list_member_oid ( parentOids , RelationGetRelid ( relation ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DUPLICATE_TABLE ) ,
errmsg ( " relation \" %s \" would be inherited from more than once " ,
parent - > relname ) ) ) ;
parentOids = lappend_oid ( parentOids , RelationGetRelid ( relation ) ) ;
tupleDesc = RelationGetDescr ( relation ) ;
constr = tupleDesc - > constr ;
@ -2463,7 +2494,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
}
}
* supOids = parentOids ;
* supconstr = constraints ;
return schema ;
}
@ -4157,11 +4187,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
break ;
case AT_SetTableSpace : /* SET TABLESPACE */
/*
* Only do this for partitioned indexes , for which this is just
* a catalog change . Other relation types are handled by Phase 3.
* Only do this for partitioned tables and indexes , for which this
* is just a catalog change . Other relation types which have
* storage are handled by Phase 3.
*/
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_INDEX )
ATExecPartedIdxSetTableSpace ( rel , tab - > newTableSpace ) ;
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE | |
rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_INDEX )
ATExecSetTableSpaceNoStorage ( rel , tab - > newTableSpace ) ;
break ;
case AT_SetRelOptions : /* SET (...) */
@ -10935,19 +10967,26 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
}
/*
* Special handling of ALTER TABLE SET TABLESPACE for partitioned indexes ,
* which have no storage ( so not handled in Phase 3 like other relation types )
* Special handling of ALTER TABLE SET TABLESPACE for relations with no
* storage that have an interest in preserving tablespace .
*
* Since these have no storage the tablespace can be updated with a simple
* metadata only operation to update the tablespace .
*/
static void
ATExecPartedIdxSetTableSpace ( Relation rel , Oid newTableSpace )
ATExecSetTableSpaceNoStorag e ( Relation rel , Oid newTableSpace )
{
HeapTuple tuple ;
Oid oldTableSpace ;
Relation pg_class ;
Form_pg_class rd_rel ;
Oid indexO id = RelationGetRelid ( rel ) ;
Oid relo id = RelationGetRelid ( rel ) ;
Assert ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_INDEX ) ;
/*
* Shouldn ' t be called on relations having storage ; these are processed
* in phase 3.
*/
Assert ( ! RELKIND_CAN_HAVE_STORAGE ( rel - > rd_rel - > relkind ) ) ;
/* Can't allow a non-shared relation in pg_global */
if ( newTableSpace = = GLOBALTABLESPACE_OID )
@ -10962,24 +11001,23 @@ ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace)
if ( newTableSpace = = oldTableSpace | |
( newTableSpace = = MyDatabaseTableSpace & & oldTableSpace = = 0 ) )
{
InvokeObjectPostAlterHook ( RelationRelationId ,
indexOid , 0 ) ;
InvokeObjectPostAlterHook ( RelationRelationId , reloid , 0 ) ;
return ;
}
/* Get a modifiable copy of the relation's pg_class row */
pg_class = heap_open ( RelationRelationId , RowExclusiveLock ) ;
tuple = SearchSysCacheCopy1 ( RELOID , ObjectIdGetDatum ( indexO id) ) ;
tuple = SearchSysCacheCopy1 ( RELOID , ObjectIdGetDatum ( relo id) ) ;
if ( ! HeapTupleIsValid ( tuple ) )
elog ( ERROR , " cache lookup failed for relation %u " , indexO id) ;
elog ( ERROR , " cache lookup failed for relation %u " , relo id) ;
rd_rel = ( Form_pg_class ) GETSTRUCT ( tuple ) ;
/* update the pg_class row */
rd_rel - > reltablespace = ( newTableSpace = = MyDatabaseTableSpace ) ? InvalidOid : newTableSpace ;
CatalogTupleUpdate ( pg_class , & tuple - > t_self , tuple ) ;
InvokeObjectPostAlterHook ( RelationRelationId , indexO id, 0 ) ;
InvokeObjectPostAlterHook ( RelationRelationId , relo id, 0 ) ;
heap_freetuple ( tuple ) ;