Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit68739ba

Browse files
committed
Allow ALTER TABLE name {OF type | NOT OF}.
This syntax allows a standalone table to be made into a typed table,or a typed table to be made standalone. This is possibly a mildlyuseful feature in its own right, but the real motivation for thischange is that we need it to make pg_upgrade work with typed tables.This doesn't actually fix that problem, but it's necessaryinfrastructure.Noah Misch
1 parent520bcd9 commit68739ba

File tree

8 files changed

+376
-38
lines changed

8 files changed

+376
-38
lines changed

‎doc/src/sgml/ref/alter_table.sgml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
6363
RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
6464
INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
6565
NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
66+
OF <replaceable class="PARAMETER">type_name</replaceable>
67+
NOT OF
6668
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
6769
SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
6870

@@ -490,6 +492,30 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
490492
</listitem>
491493
</varlistentry>
492494

495+
<varlistentry>
496+
<term><literal>OF <replaceable class="PARAMETER">type_name</replaceable></literal></term>
497+
<listitem>
498+
<para>
499+
This form links the table to a composite type as though <command>CREATE
500+
TABLE OF</> had formed it. The table's list of column names and types
501+
must precisely match that of the composite type; the presence of
502+
an <literal>oid</> system column is permitted to differ. The table must
503+
not inherit from any other table. These restrictions ensure
504+
that <command>CREATE TABLE OF</> would permit an equivalent table
505+
definition.
506+
</para>
507+
</listitem>
508+
</varlistentry>
509+
510+
<varlistentry>
511+
<term><literal>NOT OF</literal></term>
512+
<listitem>
513+
<para>
514+
This form dissociates a typed table from its type.
515+
</para>
516+
</listitem>
517+
</varlistentry>
518+
493519
<varlistentry>
494520
<term><literal>OWNER</literal></term>
495521
<listitem>

‎src/backend/commands/tablecmds.c

Lines changed: 260 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
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,
357358
staticvoidATPrepAddInherit(Relationchild_rel);
358359
staticvoidATExecAddInherit(Relationchild_rel,RangeVar*parent,LOCKMODElockmode);
359360
staticvoidATExecDropInherit(Relationrel,RangeVar*parent,LOCKMODElockmode);
361+
staticvoiddrop_parent_dependency(Oidrelid,Oidrefclassid,Oidrefobjid);
362+
staticvoidATExecAddOf(Relationrel,constTypeName*ofTypename,LOCKMODElockmode);
363+
staticvoidATExecDropOf(Relationrel,LOCKMODElockmode);
360364
staticvoidATExecGenericOptions(Relationrel,List*options);
361365

362366
staticvoidcopy_relation_data(SMgrRelationrel,SMgrRelationdst,
@@ -2683,6 +2687,16 @@ AlterTableGetLockLevel(List *cmds)
26832687
cmd_lockmode=ShareUpdateExclusiveLock;
26842688
break;
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+
caseAT_AddOf:
2697+
caseAT_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,
29422956
caseAT_EnableAlwaysRule:
29432957
caseAT_EnableReplicaRule:
29442958
caseAT_DisableRule:
2945-
ATSimplePermissions(rel,ATT_TABLE);
2946-
/* These commands never recurse */
2947-
/* No command-specific prep needed */
2948-
pass=AT_PASS_MISC;
2949-
break;
29502959
caseAT_DropInherit:/* NO INHERIT */
2960+
caseAT_AddOf:/* OF */
2961+
caseAT_DropOf:/* NOT OF */
29512962
ATSimplePermissions(rel,ATT_TABLE);
2963+
/* These commands never recurse */
29522964
/* No command-specific prep needed */
29532965
pass=AT_PASS_MISC;
29542966
break;
@@ -3211,6 +3223,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
32113223
caseAT_DropInherit:
32123224
ATExecDropInherit(rel, (RangeVar*)cmd->def,lockmode);
32133225
break;
3226+
caseAT_AddOf:
3227+
ATExecAddOf(rel, (TypeName*)cmd->def,lockmode);
3228+
break;
3229+
caseAT_DropOf:
3230+
ATExecDropOf(rel,lockmode);
3231+
break;
32143232
caseAT_GenericOptions:
32153233
ATExecGenericOptions(rel, (List*)cmd->def);
32163234
break;
@@ -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(HeapTupletypetuple)
4076+
{
4077+
Form_pg_typetyp= (Form_pg_type)GETSTRUCT(typetuple);
4078+
booltypeOk= false;
4079+
4080+
if (typ->typtype==TYPTYPE_COMPOSITE)
4081+
{
4082+
RelationtypeRelation;
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)
83558409
ScanKeyDatakey[3];
83568410
HeapTupleinheritsTuple,
83578411
attributeTuple,
8358-
constraintTuple,
8359-
depTuple;
8412+
constraintTuple;
83608413
List*connames;
83618414
boolfound= false;
83628415

@@ -8522,11 +8575,29 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85228575
systable_endscan(scan);
85238576
heap_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+
staticvoid
8594+
drop_parent_dependency(Oidrelid,Oidrefclassid,Oidrefobjid)
8595+
{
8596+
RelationcatalogRelation;
8597+
SysScanDescscan;
8598+
ScanKeyDatakey[3];
8599+
HeapTupledepTuple;
8600+
85308601
catalogRelation=heap_open(DependRelationId,RowExclusiveLock);
85318602

85328603
ScanKeyInit(&key[0],
@@ -8536,7 +8607,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85368607
ScanKeyInit(&key[1],
85378608
Anum_pg_depend_objid,
85388609
BTEqualStrategyNumber,F_OIDEQ,
8539-
ObjectIdGetDatum(RelationGetRelid(rel)));
8610+
ObjectIdGetDatum(relid));
85408611
ScanKeyInit(&key[2],
85418612
Anum_pg_depend_objsubid,
85428613
BTEqualStrategyNumber,F_INT4EQ,
@@ -8549,18 +8620,190 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
85498620
{
85508621
Form_pg_dependdep= (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&&
85548625
dep->refobjsubid==0&&
85558626
dep->deptype==DEPENDENCY_NORMAL)
85568627
simple_heap_delete(catalogRelation,&depTuple->t_self);
85578628
}
85588629

85598630
systable_endscan(scan);
85608631
heap_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+
staticvoid
8643+
ATExecAddOf(Relationrel,constTypeName*ofTypename,LOCKMODElockmode)
8644+
{
8645+
Oidrelid=RelationGetRelid(rel);
8646+
Typetypetuple;
8647+
Form_pg_typetyp;
8648+
Oidtypeid;
8649+
RelationinheritsRelation,
8650+
relationRelation;
8651+
SysScanDescscan;
8652+
ScanKeyDatakey;
8653+
AttrNumbertable_attno,
8654+
type_attno;
8655+
TupleDesctypeTupleDesc,
8656+
tableTupleDesc;
8657+
ObjectAddresstableobj,
8658+
typeobj;
8659+
HeapTupleclasstuple;
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_attributetype_attr,
8693+
table_attr;
8694+
constchar*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_attributetable_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+
staticvoid
8778+
ATExecDropOf(Relationrel,LOCKMODElockmode)
8779+
{
8780+
Oidrelid=RelationGetRelid(rel);
8781+
RelationrelationRelation;
8782+
HeapTupletuple;
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
/*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp