@@ -305,7 +305,7 @@ static void truncate_check_activity(Relation rel);
305305static void RangeVarCallbackForTruncate (const RangeVar * relation ,
306306Oid 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 ,
447447const 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 ,
451451AlterTableType operation ,
452452LOCKMODE lockmode );
@@ -536,6 +536,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
536536static char * validnsps []= HEAP_RELOPT_NAMESPACES ;
537537Oid ofTypeId ;
538538ObjectAddress 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 ),
581582errmsg ("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{
589630tablespaceId = 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+ }
591651else
592652{
593653tablespaceId = GetDefaultTablespace (stmt -> relation -> relpersistence );
@@ -646,10 +706,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
646706 * modified by MergeAttributes.)
647707 */
648708stmt -> tableElts =
649- MergeAttributes (stmt -> tableElts ,stmt -> inhRelations ,
709+ MergeAttributes (stmt -> tableElts ,inheritOids ,
650710stmt -> relation -> relpersistence ,
651711stmt -> 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 ofnames (as RangeVar nodes) of parent relations.
1844+ * 'supers' is a list ofOIDs 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{
18391898ListCell * entry ;
18401899List * inhSchema = NIL ;
1841- List * parentOids = NIL ;
18421900List * constraints = NIL ;
18431901bool have_bogus_defaults = false;
18441902int child_attno ;
@@ -1939,31 +1997,15 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
19391997child_attno = 0 ;
19401998foreach (entry ,supers )
19411999{
1942- RangeVar * parent = ( RangeVar * ) lfirst (entry );
2000+ Oid parent = lfirst_oid (entry );
19432001Relation relation ;
19442002TupleDesc tupleDesc ;
19452003TupleConstr * constr ;
19462004AttrNumber * newattno ;
19472005AttrNumber 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,
19822024ereport (ERROR ,
19832025(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19842026errmsg ("cannot inherit from partitioned table \"%s\"" ,
1985- parent -> relname )));
2027+ RelationGetRelationName ( relation ) )));
19862028if (relation -> rd_rel -> relispartition && !is_partition )
19872029ereport (ERROR ,
19882030(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19892031errmsg ("cannot inherit from partition \"%s\"" ,
1990- parent -> relname )));
2032+ RelationGetRelationName ( relation ) )));
19912033
19922034if (relation -> rd_rel -> relkind != RELKIND_RELATION &&
19932035relation -> rd_rel -> relkind != RELKIND_FOREIGN_TABLE &&
19942036relation -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE )
19952037ereport (ERROR ,
19962038(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
19972039errmsg ("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,
20172059errmsg (!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 */
20232065if (relation -> rd_rel -> relpersistence == RELPERSISTENCE_TEMP &&
@@ -2036,17 +2078,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
20362078aclcheck_error (ACLCHECK_NOT_OWNER ,get_relkind_objtype (relation -> rd_rel -> relkind ),
20372079RelationGetRelationName (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-
20502081tupleDesc = RelationGetDescr (relation );
20512082constr = tupleDesc -> constr ;
20522083
@@ -2463,7 +2494,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
24632494}
24642495}
24652496
2466- * supOids = parentOids ;
24672497* supconstr = constraints ;
24682498return schema ;
24692499}
@@ -4157,11 +4187,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
41574187break ;
41584188case 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
41664198break ;
41674199case 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{
1094410979HeapTuple tuple ;
1094510980Oid oldTableSpace ;
1094610981Relation pg_class ;
1094710982Form_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 */
1095310992if (newTableSpace == GLOBALTABLESPACE_OID )
@@ -10962,24 +11001,23 @@ ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace)
1096211001if (newTableSpace == oldTableSpace ||
1096311002(newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0 ))
1096411003{
10965- InvokeObjectPostAlterHook (RelationRelationId ,
10966- indexOid ,0 );
11004+ InvokeObjectPostAlterHook (RelationRelationId ,reloid ,0 );
1096711005return ;
1096811006}
1096911007
1097011008/* Get a modifiable copy of the relation's pg_class row */
1097111009pg_class = heap_open (RelationRelationId ,RowExclusiveLock );
1097211010
10973- tuple = SearchSysCacheCopy1 (RELOID ,ObjectIdGetDatum (indexOid ));
11011+ tuple = SearchSysCacheCopy1 (RELOID ,ObjectIdGetDatum (reloid ));
1097411012if (!HeapTupleIsValid (tuple ))
10975- elog (ERROR ,"cache lookup failed for relation %u" ,indexOid );
11013+ elog (ERROR ,"cache lookup failed for relation %u" ,reloid );
1097611014rd_rel = (Form_pg_class )GETSTRUCT (tuple );
1097711015
1097811016/* update the pg_class row */
1097911017rd_rel -> reltablespace = (newTableSpace == MyDatabaseTableSpace ) ?InvalidOid :newTableSpace ;
1098011018CatalogTupleUpdate (pg_class ,& tuple -> t_self ,tuple );
1098111019
10982- InvokeObjectPostAlterHook (RelationRelationId ,indexOid ,0 );
11020+ InvokeObjectPostAlterHook (RelationRelationId ,reloid ,0 );
1098311021
1098411022heap_freetuple (tuple );
1098511023