@@ -305,7 +305,7 @@ static void truncate_check_activity(Relation rel);
305305static void RangeVarCallbackForTruncate (const RangeVar * relation ,
306306 Oid relId , Oid oldRelId , void * arg );
307307static List * MergeAttributes (List * schema , List * supers , char relpersistence ,
308- bool is_partition , List * * supOids , List * * supconstr );
308+ bool is_partition , List * * supconstr );
309309static bool MergeCheckConstraint (List * constraints , char * name , Node * expr );
310310static void MergeAttributesIntoExisting (Relation child_rel , Relation parent_rel );
311311static void MergeConstraintsIntoExisting (Relation child_rel , Relation parent_rel );
@@ -446,7 +446,7 @@ static bool ATPrepChangePersistence(Relation rel, bool toLogged);
446446static void ATPrepSetTableSpace (AlteredTableInfo * tab , Relation rel ,
447447 const char * tablespacename , LOCKMODE lockmode );
448448static void ATExecSetTableSpace (Oid tableOid , Oid newTableSpace , LOCKMODE lockmode );
449- static void ATExecPartedIdxSetTableSpace (Relation rel , Oid newTableSpace );
449+ static void ATExecSetTableSpaceNoStorage (Relation rel , Oid newTableSpace );
450450static void ATExecSetRelOptions (Relation rel , List * defList ,
451451 AlterTableType operation ,
452452 LOCKMODE lockmode );
@@ -536,6 +536,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
536536 static char * validnsps [] = HEAP_RELOPT_NAMESPACES ;
537537 Oid ofTypeId ;
538538 ObjectAddress address ;
539+ LOCKMODE parentLockmode ;
539540
540541 /*
541542 * Truncate relname to appropriate length (probably a waste of time, as
@@ -580,6 +581,46 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
580581 (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
581582 errmsg ("cannot create temporary table within security-restricted operation" )));
582583
584+ /*
585+ * Determine the lockmode to use when scanning parents. A self-exclusive
586+ * lock is needed here.
587+ *
588+ * For regular inheritance, if two backends attempt to add children to the
589+ * same parent simultaneously, and that parent has no pre-existing
590+ * children, then both will attempt to update the parent's relhassubclass
591+ * field, leading to a "tuple concurrently updated" error. Also, this
592+ * interlocks against a concurrent ANALYZE on the parent table, which
593+ * might otherwise be attempting to clear the parent's relhassubclass
594+ * field, if its previous children were recently dropped.
595+ *
596+ * If the child table is a partition, then we instead grab an exclusive
597+ * lock on the parent because its partition descriptor will be changed by
598+ * addition of the new partition.
599+ */
600+ parentLockmode = (stmt -> partbound != NULL ? AccessExclusiveLock :
601+ ShareUpdateExclusiveLock );
602+
603+ /* Determine the list of OIDs of the parents. */
604+ inheritOids = NIL ;
605+ foreach (listptr , stmt -> inhRelations )
606+ {
607+ RangeVar * rv = (RangeVar * ) lfirst (listptr );
608+ Oid parentOid ;
609+
610+ parentOid = RangeVarGetRelid (rv , parentLockmode , false);
611+
612+ /*
613+ * Reject duplications in the list of parents.
614+ */
615+ if (list_member_oid (inheritOids , parentOid ))
616+ ereport (ERROR ,
617+ (errcode (ERRCODE_DUPLICATE_TABLE ),
618+ errmsg ("relation \"%s\" would be inherited from more than once" ,
619+ get_rel_name (parentOid ))));
620+
621+ inheritOids = lappend_oid (inheritOids , parentOid );
622+ }
623+
583624 /*
584625 * Select tablespace to use. If not specified, use default tablespace
585626 * (which may in turn default to database's default).
@@ -588,6 +629,25 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
588629 {
589630 tablespaceId = get_tablespace_oid (stmt -> tablespacename , false);
590631 }
632+ else if (stmt -> partbound )
633+ {
634+ HeapTuple tup ;
635+
636+ /*
637+ * For partitions, when no other tablespace is specified, we default
638+ * the tablespace to the parent partitioned table's.
639+ */
640+ Assert (list_length (inheritOids ) == 1 );
641+ tup = SearchSysCache1 (RELOID ,
642+ DatumGetObjectId (linitial_oid (inheritOids )));
643+
644+ tablespaceId = ((Form_pg_class ) GETSTRUCT (tup ))-> reltablespace ;
645+
646+ if (!OidIsValid (tablespaceId ))
647+ tablespaceId = GetDefaultTablespace (stmt -> relation -> relpersistence );
648+
649+ ReleaseSysCache (tup );
650+ }
591651 else
592652 {
593653 tablespaceId = GetDefaultTablespace (stmt -> relation -> relpersistence );
@@ -646,10 +706,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
646706 * modified by MergeAttributes.)
647707 */
648708 stmt -> tableElts =
649- MergeAttributes (stmt -> tableElts , stmt -> inhRelations ,
709+ MergeAttributes (stmt -> tableElts , inheritOids ,
650710 stmt -> relation -> relpersistence ,
651711 stmt -> partbound != NULL ,
652- & inheritOids , & old_constraints );
712+ & old_constraints );
653713
654714 /*
655715 * Create a tuple descriptor from the relation schema. Note that this
@@ -1781,12 +1841,11 @@ storage_name(char c)
17811841 * Input arguments:
17821842 * 'schema' is the column/attribute definition for the table. (It's a list
17831843 * of ColumnDef's.) It is destructively changed.
1784- * 'supers' is a list of names (as RangeVar nodes) of parent relations.
1844+ * 'supers' is a list of OIDs of parent relations, already locked by caller .
17851845 * 'relpersistence' is a persistence type of the table.
17861846 * 'is_partition' tells if the table is a partition
17871847 *
17881848 * Output arguments:
1789- * 'supOids' receives a list of the OIDs of the parent relations.
17901849 * 'supconstr' receives a list of constraints belonging to the parents,
17911850 * updated as necessary to be valid for the child.
17921851 *
@@ -1834,11 +1893,10 @@ storage_name(char c)
18341893 */
18351894static List *
18361895MergeAttributes (List * schema , List * supers , char relpersistence ,
1837- bool is_partition , List * * supOids , List * * supconstr )
1896+ bool is_partition , List * * supconstr )
18381897{
18391898 ListCell * entry ;
18401899 List * inhSchema = NIL ;
1841- List * parentOids = NIL ;
18421900 List * constraints = NIL ;
18431901 bool have_bogus_defaults = false;
18441902 int child_attno ;
@@ -1939,31 +1997,15 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
19391997 child_attno = 0 ;
19401998 foreach (entry , supers )
19411999 {
1942- RangeVar * parent = ( RangeVar * ) lfirst (entry );
2000+ Oid parent = lfirst_oid (entry );
19432001 Relation relation ;
19442002 TupleDesc tupleDesc ;
19452003 TupleConstr * constr ;
19462004 AttrNumber * newattno ;
19472005 AttrNumber parent_attno ;
19482006
1949- /*
1950- * A self-exclusive lock is needed here. If two backends attempt to
1951- * add children to the same parent simultaneously, and that parent has
1952- * no pre-existing children, then both will attempt to update the
1953- * parent's relhassubclass field, leading to a "tuple concurrently
1954- * updated" error. Also, this interlocks against a concurrent ANALYZE
1955- * on the parent table, which might otherwise be attempting to clear
1956- * the parent's relhassubclass field, if its previous children were
1957- * recently dropped.
1958- *
1959- * If the child table is a partition, then we instead grab an
1960- * exclusive lock on the parent because its partition descriptor will
1961- * be changed by addition of the new partition.
1962- */
1963- if (!is_partition )
1964- relation = heap_openrv (parent , ShareUpdateExclusiveLock );
1965- else
1966- relation = heap_openrv (parent , AccessExclusiveLock );
2007+ /* caller already got lock */
2008+ relation = heap_open (parent , NoLock );
19672009
19682010 /*
19692011 * Check for active uses of the parent partitioned table in the
@@ -1982,20 +2024,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
19822024 ereport (ERROR ,
19832025 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19842026 errmsg ("cannot inherit from partitioned table \"%s\"" ,
1985- parent -> relname )));
2027+ RelationGetRelationName ( relation ) )));
19862028 if (relation -> rd_rel -> relispartition && !is_partition )
19872029 ereport (ERROR ,
19882030 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19892031 errmsg ("cannot inherit from partition \"%s\"" ,
1990- parent -> relname )));
2032+ RelationGetRelationName ( relation ) )));
19912033
19922034 if (relation -> rd_rel -> relkind != RELKIND_RELATION &&
19932035 relation -> rd_rel -> relkind != RELKIND_FOREIGN_TABLE &&
19942036 relation -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE )
19952037 ereport (ERROR ,
19962038 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19972039 errmsg ("inherited relation \"%s\" is not a table or foreign table" ,
1998- parent -> relname )));
2040+ RelationGetRelationName ( relation ) )));
19992041
20002042 /*
20012043 * If the parent is permanent, so must be all of its partitions. Note
@@ -2017,7 +2059,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
20172059 errmsg (!is_partition
20182060 ? "cannot inherit from temporary relation \"%s\""
20192061 : "cannot create a permanent relation as partition of temporary relation \"%s\"" ,
2020- parent -> relname )));
2062+ RelationGetRelationName ( relation ) )));
20212063
20222064 /* If existing rel is temp, it must belong to this session */
20232065 if (relation -> rd_rel -> relpersistence == RELPERSISTENCE_TEMP &&
@@ -2036,17 +2078,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
20362078 aclcheck_error (ACLCHECK_NOT_OWNER , get_relkind_objtype (relation -> rd_rel -> relkind ),
20372079 RelationGetRelationName (relation ));
20382080
2039- /*
2040- * Reject duplications in the list of parents.
2041- */
2042- if (list_member_oid (parentOids , RelationGetRelid (relation )))
2043- ereport (ERROR ,
2044- (errcode (ERRCODE_DUPLICATE_TABLE ),
2045- errmsg ("relation \"%s\" would be inherited from more than once" ,
2046- parent -> relname )));
2047-
2048- parentOids = lappend_oid (parentOids , RelationGetRelid (relation ));
2049-
20502081 tupleDesc = RelationGetDescr (relation );
20512082 constr = tupleDesc -> constr ;
20522083
@@ -2463,7 +2494,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
24632494 }
24642495 }
24652496
2466- * supOids = parentOids ;
24672497 * supconstr = constraints ;
24682498 return schema ;
24692499}
@@ -4157,11 +4187,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
41574187 break ;
41584188 case AT_SetTableSpace : /* SET TABLESPACE */
41594189 /*
4160- * Only do this for partitioned indexes, for which this is just
4161- * a catalog change. Other relation types are handled by Phase 3.
4190+ * Only do this for partitioned tables and indexes, for which this
4191+ * is just a catalog change. Other relation types which have
4192+ * storage are handled by Phase 3.
41624193 */
4163- if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_INDEX )
4164- ATExecPartedIdxSetTableSpace (rel , tab -> newTableSpace );
4194+ if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE ||
4195+ rel -> rd_rel -> relkind == RELKIND_PARTITIONED_INDEX )
4196+ ATExecSetTableSpaceNoStorage (rel , tab -> newTableSpace );
41654197
41664198 break ;
41674199 case AT_SetRelOptions : /* SET (...) */
@@ -10935,19 +10967,26 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1093510967}
1093610968
1093710969/*
10938- * Special handling of ALTER TABLE SET TABLESPACE for partitioned indexes,
10939- * which have no storage (so not handled in Phase 3 like other relation types)
10970+ * Special handling of ALTER TABLE SET TABLESPACE for relations with no
10971+ * storage that have an interest in preserving tablespace.
10972+ *
10973+ * Since these have no storage the tablespace can be updated with a simple
10974+ * metadata only operation to update the tablespace.
1094010975 */
1094110976static void
10942- ATExecPartedIdxSetTableSpace (Relation rel , Oid newTableSpace )
10977+ ATExecSetTableSpaceNoStorage (Relation rel , Oid newTableSpace )
1094310978{
1094410979 HeapTuple tuple ;
1094510980 Oid oldTableSpace ;
1094610981 Relation pg_class ;
1094710982 Form_pg_class rd_rel ;
10948- Oid indexOid = RelationGetRelid (rel );
10983+ Oid reloid = RelationGetRelid (rel );
1094910984
10950- Assert (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_INDEX );
10985+ /*
10986+ * Shouldn't be called on relations having storage; these are processed
10987+ * in phase 3.
10988+ */
10989+ Assert (!RELKIND_CAN_HAVE_STORAGE (rel -> rd_rel -> relkind ));
1095110990
1095210991 /* Can't allow a non-shared relation in pg_global */
1095310992 if (newTableSpace == GLOBALTABLESPACE_OID )
@@ -10962,24 +11001,23 @@ ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace)
1096211001 if (newTableSpace == oldTableSpace ||
1096311002 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0 ))
1096411003 {
10965- InvokeObjectPostAlterHook (RelationRelationId ,
10966- indexOid , 0 );
11004+ InvokeObjectPostAlterHook (RelationRelationId , reloid , 0 );
1096711005 return ;
1096811006 }
1096911007
1097011008 /* Get a modifiable copy of the relation's pg_class row */
1097111009 pg_class = heap_open (RelationRelationId , RowExclusiveLock );
1097211010
10973- tuple = SearchSysCacheCopy1 (RELOID , ObjectIdGetDatum (indexOid ));
11011+ tuple = SearchSysCacheCopy1 (RELOID , ObjectIdGetDatum (reloid ));
1097411012 if (!HeapTupleIsValid (tuple ))
10975- elog (ERROR , "cache lookup failed for relation %u" , indexOid );
11013+ elog (ERROR , "cache lookup failed for relation %u" , reloid );
1097611014 rd_rel = (Form_pg_class ) GETSTRUCT (tuple );
1097711015
1097811016 /* update the pg_class row */
1097911017 rd_rel -> reltablespace = (newTableSpace == MyDatabaseTableSpace ) ? InvalidOid : newTableSpace ;
1098011018 CatalogTupleUpdate (pg_class , & tuple -> t_self , tuple );
1098111019
10982- InvokeObjectPostAlterHook (RelationRelationId , indexOid , 0 );
11020+ InvokeObjectPostAlterHook (RelationRelationId , reloid , 0 );
1098311021
1098411022 heap_freetuple (tuple );
1098511023
0 commit comments