8181#include "utils/snapmgr.h"
8282#include "utils/syscache.h"
8383#include "utils/tqual.h"
84+ #include "utils/typcache.h"
8485
8586
8687/*
@@ -357,6 +358,9 @@ static void ATExecEnableDisableRule(Relation rel, char *rulename,
357358static void ATPrepAddInherit (Relation child_rel );
358359static void ATExecAddInherit (Relation child_rel ,RangeVar * parent ,LOCKMODE lockmode );
359360static void ATExecDropInherit (Relation rel ,RangeVar * parent ,LOCKMODE lockmode );
361+ static void drop_parent_dependency (Oid relid ,Oid refclassid ,Oid refobjid );
362+ static void ATExecAddOf (Relation rel ,const TypeName * ofTypename ,LOCKMODE lockmode );
363+ static void ATExecDropOf (Relation rel ,LOCKMODE lockmode );
360364static void ATExecGenericOptions (Relation rel ,List * options );
361365
362366static void copy_relation_data (SMgrRelation rel ,SMgrRelation dst ,
@@ -2683,6 +2687,16 @@ AlterTableGetLockLevel(List *cmds)
26832687cmd_lockmode = ShareUpdateExclusiveLock ;
26842688break ;
26852689
2690+ /*
2691+ * These subcommands affect implicit row type conversion. They
2692+ * have affects similar to CREATE/DROP CAST on queries. We
2693+ * don't provide for invalidating parse trees as a result of
2694+ * such changes. Do avoid concurrent pg_class updates, though.
2695+ */
2696+ case AT_AddOf :
2697+ case AT_DropOf :
2698+ cmd_lockmode = ShareUpdateExclusiveLock ;
2699+
26862700/*
26872701 * These subcommands affect general strategies for performance
26882702 * and maintenance, though don't change the semantic results
@@ -2942,13 +2956,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
29422956case AT_EnableAlwaysRule :
29432957case AT_EnableReplicaRule :
29442958case AT_DisableRule :
2945- ATSimplePermissions (rel ,ATT_TABLE );
2946- /* These commands never recurse */
2947- /* No command-specific prep needed */
2948- pass = AT_PASS_MISC ;
2949- break ;
29502959case AT_DropInherit :/* NO INHERIT */
2960+ case AT_AddOf :/* OF */
2961+ case AT_DropOf :/* NOT OF */
29512962ATSimplePermissions (rel ,ATT_TABLE );
2963+ /* These commands never recurse */
29522964/* No command-specific prep needed */
29532965pass = AT_PASS_MISC ;
29542966break ;
@@ -3211,6 +3223,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
32113223case AT_DropInherit :
32123224ATExecDropInherit (rel , (RangeVar * )cmd -> def ,lockmode );
32133225break ;
3226+ case AT_AddOf :
3227+ ATExecAddOf (rel , (TypeName * )cmd -> def ,lockmode );
3228+ break ;
3229+ case AT_DropOf :
3230+ ATExecDropOf (rel ,lockmode );
3231+ break ;
32143232case AT_GenericOptions :
32153233ATExecGenericOptions (rel , (List * )cmd -> def );
32163234break ;
@@ -4045,6 +4063,42 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
40454063}
40464064
40474065
4066+ /*
4067+ * check_of_type
4068+ *
4069+ * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
4070+ * isn't suitable, throw an error. Currently, we require that the type
4071+ * originated with CREATE TABLE AS. We could support any row type, but doing so
4072+ * would require handling a number of extra corner cases in the DDL commands.
4073+ */
4074+ void
4075+ check_of_type (HeapTuple typetuple )
4076+ {
4077+ Form_pg_type typ = (Form_pg_type )GETSTRUCT (typetuple );
4078+ bool typeOk = false;
4079+
4080+ if (typ -> typtype == TYPTYPE_COMPOSITE )
4081+ {
4082+ Relation typeRelation ;
4083+
4084+ Assert (OidIsValid (typ -> typrelid ));
4085+ typeRelation = relation_open (typ -> typrelid ,AccessShareLock );
4086+ typeOk = (typeRelation -> rd_rel -> relkind == RELKIND_COMPOSITE_TYPE );
4087+ /*
4088+ * Close the parent rel, but keep our AccessShareLock on it until xact
4089+ * commit.That will prevent someone else from deleting or ALTERing
4090+ * the type before the typed table creation/conversion commits.
4091+ */
4092+ relation_close (typeRelation ,NoLock );
4093+ }
4094+ if (!typeOk )
4095+ ereport (ERROR ,
4096+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
4097+ errmsg ("type %s is not a composite type" ,
4098+ format_type_be (HeapTupleGetOid (typetuple )))));
4099+ }
4100+
4101+
40484102/*
40494103 * ALTER TABLE ADD COLUMN
40504104 *
@@ -8355,8 +8409,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
83558409ScanKeyData key [3 ];
83568410HeapTuple inheritsTuple ,
83578411attributeTuple ,
8358- constraintTuple ,
8359- depTuple ;
8412+ constraintTuple ;
83608413List * connames ;
83618414bool found = false;
83628415
@@ -8522,11 +8575,29 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85228575systable_endscan (scan );
85238576heap_close (catalogRelation ,RowExclusiveLock );
85248577
8525- /*
8526- * Drop the dependency
8527- *
8528- * There's no convenient way to do this, so go trawling through pg_depend
8529- */
8578+ drop_parent_dependency (RelationGetRelid (rel ),
8579+ RelationRelationId ,
8580+ RelationGetRelid (parent_rel ));
8581+
8582+ /* keep our lock on the parent relation until commit */
8583+ heap_close (parent_rel ,NoLock );
8584+ }
8585+
8586+ /*
8587+ * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
8588+ * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
8589+ * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
8590+ * be TypeRelationId). There's no convenient way to do this, so go trawling
8591+ * through pg_depend.
8592+ */
8593+ static void
8594+ drop_parent_dependency (Oid relid ,Oid refclassid ,Oid refobjid )
8595+ {
8596+ Relation catalogRelation ;
8597+ SysScanDesc scan ;
8598+ ScanKeyData key [3 ];
8599+ HeapTuple depTuple ;
8600+
85308601catalogRelation = heap_open (DependRelationId ,RowExclusiveLock );
85318602
85328603ScanKeyInit (& key [0 ],
@@ -8536,7 +8607,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85368607ScanKeyInit (& key [1 ],
85378608Anum_pg_depend_objid ,
85388609BTEqualStrategyNumber ,F_OIDEQ ,
8539- ObjectIdGetDatum (RelationGetRelid ( rel ) ));
8610+ ObjectIdGetDatum (relid ));
85408611ScanKeyInit (& key [2 ],
85418612Anum_pg_depend_objsubid ,
85428613BTEqualStrategyNumber ,F_INT4EQ ,
@@ -8549,18 +8620,190 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85498620{
85508621Form_pg_depend dep = (Form_pg_depend )GETSTRUCT (depTuple );
85518622
8552- if (dep -> refclassid == RelationRelationId &&
8553- dep -> refobjid == RelationGetRelid ( parent_rel ) &&
8623+ if (dep -> refclassid == refclassid &&
8624+ dep -> refobjid == refobjid &&
85548625dep -> refobjsubid == 0 &&
85558626dep -> deptype == DEPENDENCY_NORMAL )
85568627simple_heap_delete (catalogRelation ,& depTuple -> t_self );
85578628}
85588629
85598630systable_endscan (scan );
85608631heap_close (catalogRelation ,RowExclusiveLock );
8632+ }
85618633
8562- /* keep our lock on the parent relation until commit */
8563- heap_close (parent_rel ,NoLock );
8634+ /*
8635+ * ALTER TABLE OF
8636+ *
8637+ * Attach a table to a composite type, as though it had been created with CREATE
8638+ * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
8639+ * subject table must not have inheritance parents. These restrictions ensure
8640+ * that you cannot create a configuration impossible with CREATE TABLE OF alone.
8641+ */
8642+ static void
8643+ ATExecAddOf (Relation rel ,const TypeName * ofTypename ,LOCKMODE lockmode )
8644+ {
8645+ Oid relid = RelationGetRelid (rel );
8646+ Type typetuple ;
8647+ Form_pg_type typ ;
8648+ Oid typeid ;
8649+ Relation inheritsRelation ,
8650+ relationRelation ;
8651+ SysScanDesc scan ;
8652+ ScanKeyData key ;
8653+ AttrNumber table_attno ,
8654+ type_attno ;
8655+ TupleDesc typeTupleDesc ,
8656+ tableTupleDesc ;
8657+ ObjectAddress tableobj ,
8658+ typeobj ;
8659+ HeapTuple classtuple ;
8660+
8661+ /* Validate the type. */
8662+ typetuple = typenameType (NULL ,ofTypename ,NULL );
8663+ check_of_type (typetuple );
8664+ typ = (Form_pg_type )GETSTRUCT (typetuple );
8665+ typeid = HeapTupleGetOid (typetuple );
8666+
8667+ /* Fail if the table has any inheritance parents. */
8668+ inheritsRelation = heap_open (InheritsRelationId ,AccessShareLock );
8669+ ScanKeyInit (& key ,
8670+ Anum_pg_inherits_inhrelid ,
8671+ BTEqualStrategyNumber ,F_OIDEQ ,
8672+ ObjectIdGetDatum (relid ));
8673+ scan = systable_beginscan (inheritsRelation ,InheritsRelidSeqnoIndexId ,
8674+ true,SnapshotNow ,1 ,& key );
8675+ if (HeapTupleIsValid (systable_getnext (scan )))
8676+ ereport (ERROR ,
8677+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
8678+ errmsg ("typed tables cannot inherit" )));
8679+ systable_endscan (scan );
8680+ heap_close (inheritsRelation ,AccessShareLock );
8681+
8682+ /*
8683+ * Check the tuple descriptors for compatibility. Unlike inheritance, we
8684+ * require that the order also match. However, attnotnull need not match.
8685+ * Also unlike inheritance, we do not require matching relhasoids.
8686+ */
8687+ typeTupleDesc = lookup_rowtype_tupdesc (typeid ,-1 );
8688+ tableTupleDesc = RelationGetDescr (rel );
8689+ table_attno = 1 ;
8690+ for (type_attno = 1 ;type_attno <=typeTupleDesc -> natts ;type_attno ++ )
8691+ {
8692+ Form_pg_attribute type_attr ,
8693+ table_attr ;
8694+ const char * type_attname ,
8695+ * table_attname ;
8696+
8697+ /* Get the next non-dropped type attribute. */
8698+ type_attr = typeTupleDesc -> attrs [type_attno - 1 ];
8699+ if (type_attr -> attisdropped )
8700+ continue ;
8701+ type_attname = NameStr (type_attr -> attname );
8702+
8703+ /* Get the next non-dropped table attribute. */
8704+ do
8705+ {
8706+ if (table_attno > tableTupleDesc -> natts )
8707+ ereport (ERROR ,
8708+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
8709+ errmsg ("table is missing column \"%s\"" ,
8710+ type_attname )));
8711+ table_attr = tableTupleDesc -> attrs [table_attno ++ - 1 ];
8712+ }while (table_attr -> attisdropped );
8713+ table_attname = NameStr (table_attr -> attname );
8714+
8715+ /* Compare name. */
8716+ if (strncmp (table_attname ,type_attname ,NAMEDATALEN )!= 0 )
8717+ ereport (ERROR ,
8718+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
8719+ errmsg ("table has column \"%s\" where type requires \"%s\"" ,
8720+ table_attname ,type_attname )));
8721+
8722+ /* Compare type. */
8723+ if (table_attr -> atttypid != type_attr -> atttypid ||
8724+ table_attr -> atttypmod != type_attr -> atttypmod ||
8725+ table_attr -> attcollation != type_attr -> attcollation )
8726+ ereport (ERROR ,
8727+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
8728+ errmsg ("table \"%s\" has different type for column \"%s\"" ,
8729+ RelationGetRelationName (rel ),type_attname )));
8730+ }
8731+ DecrTupleDescRefCount (typeTupleDesc );
8732+
8733+ /* Any remaining columns at the end of the table had better be dropped. */
8734+ for (;table_attno <=tableTupleDesc -> natts ;table_attno ++ )
8735+ {
8736+ Form_pg_attribute table_attr = tableTupleDesc -> attrs [table_attno - 1 ];
8737+ if (!table_attr -> attisdropped )
8738+ ereport (ERROR ,
8739+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
8740+ errmsg ("table has extra column \"%s\"" ,
8741+ NameStr (table_attr -> attname ))));
8742+ }
8743+
8744+ /* If the table was already typed, drop the existing dependency. */
8745+ if (rel -> rd_rel -> reloftype )
8746+ drop_parent_dependency (relid ,TypeRelationId ,rel -> rd_rel -> reloftype );
8747+
8748+ /* Record a dependency on the new type. */
8749+ tableobj .classId = RelationRelationId ;
8750+ tableobj .objectId = relid ;
8751+ tableobj .objectSubId = 0 ;
8752+ typeobj .classId = TypeRelationId ;
8753+ typeobj .objectId = typeid ;
8754+ typeobj .objectSubId = 0 ;
8755+ recordDependencyOn (& tableobj ,& typeobj ,DEPENDENCY_NORMAL );
8756+
8757+ /* Update pg_class.reloftype */
8758+ relationRelation = heap_open (RelationRelationId ,RowExclusiveLock );
8759+ classtuple = SearchSysCacheCopy1 (RELOID ,ObjectIdGetDatum (relid ));
8760+ if (!HeapTupleIsValid (classtuple ))
8761+ elog (ERROR ,"cache lookup failed for relation %u" ,relid );
8762+ ((Form_pg_class )GETSTRUCT (classtuple ))-> reloftype = typeid ;
8763+ simple_heap_update (relationRelation ,& classtuple -> t_self ,classtuple );
8764+ CatalogUpdateIndexes (relationRelation ,classtuple );
8765+ heap_freetuple (classtuple );
8766+ heap_close (relationRelation ,RowExclusiveLock );
8767+
8768+ ReleaseSysCache (typetuple );
8769+ }
8770+
8771+ /*
8772+ * ALTER TABLE NOT OF
8773+ *
8774+ * Detach a typed table from its originating type. Just clear reloftype and
8775+ * remove the dependency.
8776+ */
8777+ static void
8778+ ATExecDropOf (Relation rel ,LOCKMODE lockmode )
8779+ {
8780+ Oid relid = RelationGetRelid (rel );
8781+ Relation relationRelation ;
8782+ HeapTuple tuple ;
8783+
8784+ if (!OidIsValid (rel -> rd_rel -> reloftype ))
8785+ ereport (ERROR ,
8786+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
8787+ errmsg ("\"%s\" is not a typed table" ,
8788+ RelationGetRelationName (rel ))));
8789+
8790+ /*
8791+ * We don't bother to check ownership of the type --- ownership of the table
8792+ * is presumed enough rights. No lock required on the type, either.
8793+ */
8794+
8795+ drop_parent_dependency (relid ,TypeRelationId ,rel -> rd_rel -> reloftype );
8796+
8797+ /* Clear pg_class.reloftype */
8798+ relationRelation = heap_open (RelationRelationId ,RowExclusiveLock );
8799+ tuple = SearchSysCacheCopy1 (RELOID ,ObjectIdGetDatum (relid ));
8800+ if (!HeapTupleIsValid (tuple ))
8801+ elog (ERROR ,"cache lookup failed for relation %u" ,relid );
8802+ ((Form_pg_class )GETSTRUCT (tuple ))-> reloftype = InvalidOid ;
8803+ simple_heap_update (relationRelation ,& tuple -> t_self ,tuple );
8804+ CatalogUpdateIndexes (relationRelation ,tuple );
8805+ heap_freetuple (tuple );
8806+ heap_close (relationRelation ,RowExclusiveLock );
85648807}
85658808
85668809/*