Add ALTER TYPE ... ADD/DROP/ALTER/RENAME ATTRIBUTE

Like with tables, this also requires allowing the existence of
composite types with zero attributes.

reviewed by KaiGai Kohei
This commit is contained in:
Peter Eisentraut 2010-09-26 14:41:03 +03:00
parent 899beb7894
commit e440e12c56
15 changed files with 636 additions and 148 deletions

View File

@ -23,9 +23,17 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable> ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable> ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
ADD ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable>
DROP ATTRIBUTE [ IF EXISTS ] <replaceable class="PARAMETER">attribute_name</replaceable>
ALTER ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable>
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -34,6 +42,76 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
<para> <para>
<command>ALTER TYPE</command> changes the definition of an existing type. <command>ALTER TYPE</command> changes the definition of an existing type.
There are several subforms:
<variablelist>
<varlistentry>
<term><literal>ADD ATTRIBUTE</literal></term>
<listitem>
<para>
This form adds a new attribute to a composite type, using the same syntax as
<xref linkend="SQL-CREATETYPE">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>DROP ATTRIBUTE [ IF EXISTS ]</literal></term>
<listitem>
<para>
This form drops an attribute from a composite type.
If <literal>IF EXISTS</literal> is specified and the attribute
does not exist, no error is thrown. In this case a notice
is issued instead.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>SET DATA TYPE</literal></term>
<listitem>
<para>
This form changes the type of an attribute of a composite type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>OWNER</literal></term>
<listitem>
<para>
This form changes the owner of the type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
This form changes the name of the type or the name of an
individual attribute of a composite type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>SET SCHEMA</literal></term>
<listitem>
<para>
This form moves the type into another schema.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
The <literal>ADD ATTRIBUTE</literal>, <literal>DROP
ATTRIBUTE</literal>, and <literal>ALTER ATTRIBUTE</literal> actions
can be combined into a list of multiple alterations to apply in
parallel. For example, it is possible to add several attributes
and/or alter the type of several attributes in a single command.
</para> </para>
<para> <para>
@ -90,6 +168,34 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">attribute_name</replaceable></term>
<listitem>
<para>
The name of the attribute to add, alter, or drop.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">new_attribute_name</replaceable></term>
<listitem>
<para>
The new name of the attribute begin renamed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">data_type</replaceable></term>
<listitem>
<para>
The data type of the attribute to add, or the new type of the
attribute to alter.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</para> </para>
</refsect1> </refsect1>
@ -117,6 +223,13 @@ ALTER TYPE email OWNER TO joe;
to <literal>customers</literal>: to <literal>customers</literal>:
<programlisting> <programlisting>
ALTER TYPE email SET SCHEMA customers; ALTER TYPE email SET SCHEMA customers;
</programlisting>
</para>
<para>
To add a new attribute to a type:
<programlisting>
ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
</programlisting> </programlisting>
</para> </para>
</refsect1> </refsect1>
@ -125,8 +238,17 @@ ALTER TYPE email SET SCHEMA customers;
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
There is no <command>ALTER TYPE</command> statement in the SQL The variants to add and drop attributes are part of the SQL
standard. standard; the other variants are PostgreSQL extensions.
</para> </para>
</refsect1> </refsect1>
<refsect1 id="SQL-ALTERTYPE-see-also">
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-createtype"></member>
<member><xref linkend="sql-droptype"></member>
</simplelist>
</refsect1>
</refentry> </refentry>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE TYPE <replaceable class="parameter">name</replaceable> AS CREATE TYPE <replaceable class="parameter">name</replaceable> AS
( <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ) ( [ <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ] )
CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] ) ( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
@ -768,10 +768,18 @@ CREATE TABLE big_objs (
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
This <command>CREATE TYPE</command> command is a The first form of the <command>CREATE TYPE</command> command, which
<productname>PostgreSQL</productname> extension. There is a creates a composite type, conforms to the <acronym>SQL</> standard.
<command>CREATE TYPE</command> statement in the <acronym>SQL</> standard The other forms are <productname>PostgreSQL</productname>
that is rather different in detail. extensions. The <command>CREATE TYPE</command> statement in
the <acronym>SQL</> standard also defines other forms that are not
implemented in <productname>PostgreSQL</>.
</para>
<para>
The ability to create a composite type with zero attributes is
a <productname>PostgreSQL</productname>-specific deviation from the
standard (analogous to <command>CREATE TABLE</command>).
</para> </para>
</refsect1> </refsect1>
@ -779,10 +787,10 @@ CREATE TABLE big_objs (
<title>See Also</title> <title>See Also</title>
<simplelist type="inline"> <simplelist type="inline">
<member><xref linkend="sql-createfunction"></member>
<member><xref linkend="sql-droptype"></member>
<member><xref linkend="sql-altertype"></member> <member><xref linkend="sql-altertype"></member>
<member><xref linkend="sql-createdomain"></member> <member><xref linkend="sql-createdomain"></member>
<member><xref linkend="sql-createfunction"></member>
<member><xref linkend="sql-droptype"></member>
</simplelist> </simplelist>
</refsect1> </refsect1>

View File

@ -97,7 +97,7 @@ DROP TYPE box;
This command is similar to the corresponding command in the SQL This command is similar to the corresponding command in the SQL
standard, apart from the <literal>IF EXISTS</> standard, apart from the <literal>IF EXISTS</>
option, which is a <productname>PostgreSQL</> extension. option, which is a <productname>PostgreSQL</> extension.
But note that the <command>CREATE TYPE</command> command But note that much of the <command>CREATE TYPE</command> command
and the data type extension mechanisms in and the data type extension mechanisms in
<productname>PostgreSQL</productname> differ from the SQL standard. <productname>PostgreSQL</productname> differ from the SQL standard.
</para> </para>
@ -107,8 +107,8 @@ DROP TYPE box;
<title>See Also</title> <title>See Also</title>
<simplelist type="inline"> <simplelist type="inline">
<member><xref linkend="sql-createtype"></member>
<member><xref linkend="sql-altertype"></member> <member><xref linkend="sql-altertype"></member>
<member><xref linkend="sql-createtype"></member>
</simplelist> </simplelist>
</refsect1> </refsect1>

View File

@ -89,6 +89,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_VIEW: case OBJECT_VIEW:
case OBJECT_INDEX: case OBJECT_INDEX:
case OBJECT_COLUMN: case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_TRIGGER: case OBJECT_TRIGGER:
{ {
Oid relid; Oid relid;
@ -123,6 +124,7 @@ ExecRenameStmt(RenameStmt *stmt)
break; break;
} }
case OBJECT_COLUMN: case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
renameatt(relid, renameatt(relid,
stmt->subname, /* old att name */ stmt->subname, /* old att name */
stmt->newname, /* new att name */ stmt->newname, /* new att name */

View File

@ -263,12 +263,13 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
static void ATRewriteTables(List **wqueue, LOCKMODE lockmode); static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
static void ATSimplePermissions(Relation rel, bool allowView); static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
static void ATSimplePermissionsRelationOrIndex(Relation rel); static void ATSimplePermissionsRelationOrIndex(Relation rel);
static void ATSimpleRecursion(List **wqueue, Relation rel, static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode); AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel, static void ATOneLevelRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, LOCKMODE lockmode); AlterTableCmd *cmd, LOCKMODE lockmode);
static void find_typed_table_dependencies(Oid typeOid, const char *typeName);
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd, LOCKMODE lockmode); AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
@ -1978,6 +1979,10 @@ renameatt(Oid myrelid,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot rename column of typed table"))); errmsg("cannot rename column of typed table")));
if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
find_typed_table_dependencies(targetrelation->rd_rel->reltype,
RelationGetRelationName(targetrelation));
/* /*
* Renaming the columns of sequences or toast tables doesn't actually * Renaming the columns of sequences or toast tables doesn't actually
* break anything from the system's point of view, since internal * break anything from the system's point of view, since internal
@ -2368,8 +2373,13 @@ AlterTable(AlterTableStmt *stmt)
/* /*
* For mostly-historical reasons, we allow ALTER TABLE to apply to * For mostly-historical reasons, we allow ALTER TABLE to apply to
* all relation types. * almost all relation types.
*/ */
if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
RelationGetRelationName(rel))));
break; break;
case OBJECT_INDEX: case OBJECT_INDEX:
@ -2388,6 +2398,14 @@ AlterTable(AlterTableStmt *stmt)
RelationGetRelationName(rel)))); RelationGetRelationName(rel))));
break; break;
case OBJECT_TYPE:
if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a composite type",
RelationGetRelationName(rel))));
break;
case OBJECT_VIEW: case OBJECT_VIEW:
if (rel->rd_rel->relkind != RELKIND_VIEW) if (rel->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR, ereport(ERROR,
@ -2639,14 +2657,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
switch (cmd->subtype) switch (cmd->subtype)
{ {
case AT_AddColumn: /* ADD COLUMN */ case AT_AddColumn: /* ADD COLUMN */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, true);
/* Performs own recursion */ /* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode); ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL; pass = AT_PASS_ADD_COL;
break; break;
case AT_AddColumnToView: /* add column via CREATE OR REPLACE case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */ * VIEW */
ATSimplePermissions(rel, true); ATSimplePermissions(rel, true, false);
/* Performs own recursion */ /* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode); ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL; pass = AT_PASS_ADD_COL;
@ -2659,19 +2677,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
* substitutes default values into INSERTs before it expands * substitutes default values into INSERTs before it expands
* rules. * rules.
*/ */
ATSimplePermissions(rel, true); ATSimplePermissions(rel, true, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break; break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_DROP; pass = AT_PASS_DROP;
break; break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR; pass = AT_PASS_ADD_CONSTR;
@ -2689,25 +2707,25 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
break; break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */ case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
break; break;
case AT_DropColumn: /* DROP COLUMN */ case AT_DropColumn: /* DROP COLUMN */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, true);
ATPrepDropColumn(rel, recurse, cmd); ATPrepDropColumn(rel, recurse, cmd);
/* Recursion occurs during execution phase */ /* Recursion occurs during execution phase */
pass = AT_PASS_DROP; pass = AT_PASS_DROP;
break; break;
case AT_AddIndex: /* ADD INDEX */ case AT_AddIndex: /* ADD INDEX */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* This command never recurses */ /* This command never recurses */
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_ADD_INDEX; pass = AT_PASS_ADD_INDEX;
break; break;
case AT_AddConstraint: /* ADD CONSTRAINT */ case AT_AddConstraint: /* ADD CONSTRAINT */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* Recursion occurs during execution phase */ /* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */ /* No command-specific prep needed except saving recurse flag */
if (recurse) if (recurse)
@ -2715,7 +2733,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_ADD_CONSTR; pass = AT_PASS_ADD_CONSTR;
break; break;
case AT_DropConstraint: /* DROP CONSTRAINT */ case AT_DropConstraint: /* DROP CONSTRAINT */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* Recursion occurs during execution phase */ /* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */ /* No command-specific prep needed except saving recurse flag */
if (recurse) if (recurse)
@ -2723,7 +2741,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_DROP; pass = AT_PASS_DROP;
break; break;
case AT_AlterColumnType: /* ALTER COLUMN TYPE */ case AT_AlterColumnType: /* ALTER COLUMN TYPE */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, true);
/* Performs own recursion */ /* Performs own recursion */
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode); ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE; pass = AT_PASS_ALTER_TYPE;
@ -2735,20 +2753,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
break; break;
case AT_ClusterOn: /* CLUSTER ON */ case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */ case AT_DropCluster: /* SET WITHOUT CLUSTER */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* These commands never recurse */ /* These commands never recurse */
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
break; break;
case AT_AddOids: /* SET WITH OIDS */ case AT_AddOids: /* SET WITH OIDS */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* Performs own recursion */ /* Performs own recursion */
if (!rel->rd_rel->relhasoids || recursing) if (!rel->rd_rel->relhasoids || recursing)
ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode); ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL; pass = AT_PASS_ADD_COL;
break; break;
case AT_DropOids: /* SET WITHOUT OIDS */ case AT_DropOids: /* SET WITHOUT OIDS */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* Performs own recursion */ /* Performs own recursion */
if (rel->rd_rel->relhasoids) if (rel->rd_rel->relhasoids)
{ {
@ -2775,7 +2793,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
break; break;
case AT_AddInherit: /* INHERIT */ case AT_AddInherit: /* INHERIT */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* This command never recurses */ /* This command never recurses */
ATPrepAddInherit(rel); ATPrepAddInherit(rel);
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
@ -2793,7 +2811,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_EnableReplicaRule: case AT_EnableReplicaRule:
case AT_DisableRule: case AT_DisableRule:
case AT_DropInherit: /* NO INHERIT */ case AT_DropInherit: /* NO INHERIT */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* These commands never recurse */ /* These commands never recurse */
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
@ -3519,7 +3537,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
* - Ensure that it is not a system table * - Ensure that it is not a system table
*/ */
static void static void
ATSimplePermissions(Relation rel, bool allowView) ATSimplePermissions(Relation rel, bool allowView, bool allowType)
{ {
if (rel->rd_rel->relkind != RELKIND_RELATION) if (rel->rd_rel->relkind != RELKIND_RELATION)
{ {
@ -3531,6 +3549,14 @@ ATSimplePermissions(Relation rel, bool allowView)
errmsg("\"%s\" is not a table or view", errmsg("\"%s\" is not a table or view",
RelationGetRelationName(rel)))); RelationGetRelationName(rel))));
} }
else if (allowType)
{
if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or composite type",
RelationGetRelationName(rel))));
}
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -3758,6 +3784,44 @@ find_composite_type_dependencies(Oid typeOid,
} }
/*
* find_typed_table_dependencies
*
* Check to see if a composite type is being used as the type of a
* typed table. Eventually, we'd like to propagate the alter
* operation into such tables, but for now, just error out if we find
* any.
*/
static void
find_typed_table_dependencies(Oid typeOid, const char *typeName)
{
Relation classRel;
ScanKeyData key[1];
HeapScanDesc scan;
HeapTuple tuple;
classRel = heap_open(RelationRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_class_reloftype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
scan = heap_beginscan(classRel, SnapshotNow, 1, key);
if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type \"%s\" because it is the type of a typed table",
typeName)));
}
heap_endscan(scan);
heap_close(classRel, AccessShareLock);
}
/* /*
* ALTER TABLE ADD COLUMN * ALTER TABLE ADD COLUMN
* *
@ -3804,6 +3868,10 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column must be added to child tables too"))); errmsg("column must be added to child tables too")));
} }
if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
find_typed_table_dependencies(rel->rd_rel->reltype,
RelationGetRelationName(rel));
} }
static void static void
@ -4007,7 +4075,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
* defaults, not even for domain-typed columns. And in any case we * defaults, not even for domain-typed columns. And in any case we
* mustn't invoke Phase 3 on a view, since it has no storage. * mustn't invoke Phase 3 on a view, since it has no storage.
*/ */
if (relkind != RELKIND_VIEW && attribute.attnum > 0) if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && attribute.attnum > 0)
{ {
defval = (Expr *) build_column_default(rel, attribute.attnum); defval = (Expr *) build_column_default(rel, attribute.attnum);
@ -4535,6 +4603,10 @@ ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd)
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot drop column from typed table"))); errmsg("cannot drop column from typed table")));
if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
find_typed_table_dependencies(rel->rd_rel->reltype,
RelationGetRelationName(rel));
/* No command-specific prep needed except saving recurse flag */ /* No command-specific prep needed except saving recurse flag */
if (recurse) if (recurse)
cmd->subtype = AT_DropColumnRecurse; cmd->subtype = AT_DropColumnRecurse;
@ -4554,7 +4626,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
/* At top level, permission check was done in ATPrepCmd, else do it */ /* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing) if (recursing)
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, true);
/* /*
* get the number of the attribute * get the number of the attribute
@ -4858,7 +4930,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* At top level, permission check was done in ATPrepCmd, else do it */ /* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing) if (recursing)
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
/* /*
* Call AddRelationNewConstraints to do the work, making sure it works on * Call AddRelationNewConstraints to do the work, making sure it works on
@ -5801,7 +5873,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
/* At top level, permission check was done in ATPrepCmd, else do it */ /* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing) if (recursing)
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false, false);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock); conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
@ -6033,77 +6105,94 @@ ATPrepAlterColumnType(List **wqueue,
/* make sure datatype is legal for a column */ /* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype, false); CheckAttributeType(colName, targettype, false);
/* if (tab->relkind == RELKIND_RELATION)
* Set up an expression to transform the old data value to the new type.
* If a USING option was given, transform and use that expression, else
* just take the old value and try to coerce it. We do this first so that
* type incompatibility can be detected before we waste effort, and
* because we need the expression to be parsed against the original table
* rowtype.
*/
if (cmd->transform)
{ {
RangeTblEntry *rte; /*
* Set up an expression to transform the old data value to the new type.
* If a USING option was given, transform and use that expression, else
* just take the old value and try to coerce it. We do this first so that
* type incompatibility can be detected before we waste effort, and
* because we need the expression to be parsed against the original table
* rowtype.
*/
if (cmd->transform)
{
RangeTblEntry *rte;
/* Expression must be able to access vars of old table */ /* Expression must be able to access vars of old table */
rte = addRangeTableEntryForRelation(pstate, rte = addRangeTableEntryForRelation(pstate,
rel, rel,
NULL, NULL,
false, false,
true); true);
addRTEtoQuery(pstate, rte, false, true, true); addRTEtoQuery(pstate, rte, false, true, true);
transform = transformExpr(pstate, cmd->transform); transform = transformExpr(pstate, cmd->transform);
/* It can't return a set */ /* It can't return a set */
if (expression_returns_set(transform)) if (expression_returns_set(transform))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("transform expression must not return a set")));
/* No subplans or aggregates, either... */
if (pstate->p_hasSubLinks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in transform expression")));
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in transform expression")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in transform expression")));
}
else
{
transform = (Node *) makeVar(1, attnum,
attTup->atttypid, attTup->atttypmod,
0);
}
transform = coerce_to_target_type(pstate,
transform, exprType(transform),
targettype, targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
if (transform == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("transform expression must not return a set"))); errmsg("column \"%s\" cannot be cast to type %s",
colName, format_type_be(targettype))));
/* No subplans or aggregates, either... */ /*
if (pstate->p_hasSubLinks) * Add a work queue item to make ATRewriteTable update the column
ereport(ERROR, * contents.
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), */
errmsg("cannot use subquery in transform expression"))); newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
if (pstate->p_hasAggs) newval->attnum = attnum;
ereport(ERROR, newval->expr = (Expr *) transform;
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in transform expression"))); tab->newvals = lappend(tab->newvals, newval);
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in transform expression")));
} }
else
if (tab->relkind == RELKIND_COMPOSITE_TYPE)
{ {
transform = (Node *) makeVar(1, attnum, /*
attTup->atttypid, attTup->atttypmod, * For composite types, do this check now. Tables will check
0); * it later when the table is being rewritten.
*/
find_composite_type_dependencies(rel->rd_rel->reltype,
NULL,
RelationGetRelationName(rel));
find_typed_table_dependencies(rel->rd_rel->reltype,
RelationGetRelationName(rel));
} }
transform = coerce_to_target_type(pstate,
transform, exprType(transform),
targettype, targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
if (transform == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" cannot be cast to type %s",
colName, format_type_be(targettype))));
/*
* Add a work queue item to make ATRewriteTable update the column
* contents.
*/
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
newval->attnum = attnum;
newval->expr = (Expr *) transform;
tab->newvals = lappend(tab->newvals, newval);
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
/* /*
@ -7367,7 +7456,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
* Must be owner of both parent and child -- child was checked by * Must be owner of both parent and child -- child was checked by
* ATSimplePermissions call in ATPrepCmd * ATSimplePermissions call in ATPrepCmd
*/ */
ATSimplePermissions(parent_rel, false); ATSimplePermissions(parent_rel, false, false);
/* Permanent rels cannot inherit from temporary ones */ /* Permanent rels cannot inherit from temporary ones */
if (parent_rel->rd_istemp && !child_rel->rd_istemp) if (parent_rel->rd_istemp && !child_rel->rd_istemp)

View File

@ -1508,11 +1508,6 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
Oid typeNamespace; Oid typeNamespace;
Oid relid; Oid relid;
if (coldeflist == NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("composite type must have at least one attribute")));
/* /*
* now set the parameters for keys/inheritance etc. All of these are * now set the parameters for keys/inheritance etc. All of these are
* uninteresting for composite types... * uninteresting for composite types...

View File

@ -131,6 +131,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
List *args, int location); List *args, int location);
static List *mergeTableFuncParameters(List *func_args, List *columns); static List *mergeTableFuncParameters(List *func_args, List *columns);
static TypeName *TableFuncTypeName(List *columns); static TypeName *TableFuncTypeName(List *columns);
static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
%} %}
@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns);
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt AlterRoleStmt AlterRoleSetStmt
AlterDefaultPrivilegesStmt DefACLAction AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
@ -218,8 +219,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> alter_column_default opclass_item opclass_drop alter_using %type <node> alter_column_default opclass_item opclass_drop alter_using
%type <ival> add_drop opt_asc_desc opt_nulls_order %type <ival> add_drop opt_asc_desc opt_nulls_order
%type <node> alter_table_cmd %type <node> alter_table_cmd alter_type_cmd
%type <list> alter_table_cmds %type <list> alter_table_cmds alter_type_cmds
%type <dbehavior> opt_drop_behavior %type <dbehavior> opt_drop_behavior
@ -295,7 +296,7 @@ static TypeName *TableFuncTypeName(List *columns);
reloption_list group_clause TriggerFuncArgs select_limit reloption_list group_clause TriggerFuncArgs select_limit
opt_select_limit opclass_item_list opclass_drop_list opt_select_limit opclass_item_list opclass_drop_list
opt_opfamily transaction_mode_list_or_empty opt_opfamily transaction_mode_list_or_empty
TableFuncElementList opt_type_modifiers OptTableFuncElementList TableFuncElementList opt_type_modifiers
prep_type_clause prep_type_clause
execute_param_clause using_clause returning_clause execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list opt_enum_val_list enum_val_list table_func_column_list
@ -462,7 +463,7 @@ static TypeName *TableFuncTypeName(List *columns);
/* ordinary key words in alphabetical order */ /* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
BOOLEAN_P BOTH BY BOOLEAN_P BOTH BY
@ -657,6 +658,7 @@ stmt :
| AlterOwnerStmt | AlterOwnerStmt
| AlterSeqStmt | AlterSeqStmt
| AlterTableStmt | AlterTableStmt
| AlterCompositeTypeStmt
| AlterRoleSetStmt | AlterRoleSetStmt
| AlterRoleStmt | AlterRoleStmt
| AlterTSConfigurationStmt | AlterTSConfigurationStmt
@ -1968,6 +1970,72 @@ reloption_elem:
; ;
/*****************************************************************************
*
* ALTER TYPE
*
* really variants of the ALTER TABLE subcommands with different spellings
*****************************************************************************/
AlterCompositeTypeStmt:
ALTER TYPE_P any_name alter_type_cmds
{
AlterTableStmt *n = makeNode(AlterTableStmt);
/* can't use qualified_name, sigh */
n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
n->cmds = $4;
n->relkind = OBJECT_TYPE;
$$ = (Node *)n;
}
;
alter_type_cmds:
alter_type_cmd { $$ = list_make1($1); }
| alter_type_cmds ',' alter_type_cmd { $$ = lappend($1, $3); }
;
alter_type_cmd:
/* ALTER TYPE <name> ADD ATTRIBUTE <coldef> */
ADD_P ATTRIBUTE TableFuncElement
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AddColumn;
n->def = $3;
$$ = (Node *)n;
}
/* ALTER TYPE <name> DROP ATTRIBUTE IF EXISTS <attname> */
| DROP ATTRIBUTE IF_P EXISTS ColId
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_DropColumn;
n->name = $5;
n->behavior = DROP_RESTRICT; /* currently no effect */
n->missing_ok = TRUE;
$$ = (Node *)n;
}
/* ALTER TYPE <name> DROP ATTRIBUTE <attname> */
| DROP ATTRIBUTE ColId opt_drop_behavior
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_DropColumn;
n->name = $3;
n->behavior = DROP_RESTRICT; /* currently no effect */
n->missing_ok = FALSE;
$$ = (Node *)n;
}
/* ALTER TYPE <name> ALTER ATTRIBUTE <attname> [SET DATA] TYPE <typename> */
| ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AlterColumnType;
n->name = $3;
n->def = (Node *) $6;
$$ = (Node *)n;
}
;
/***************************************************************************** /*****************************************************************************
* *
* QUERY : * QUERY :
@ -3678,39 +3746,12 @@ DefineStmt:
n->definition = NIL; n->definition = NIL;
$$ = (Node *)n; $$ = (Node *)n;
} }
| CREATE TYPE_P any_name AS '(' TableFuncElementList ')' | CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')'
{ {
CompositeTypeStmt *n = makeNode(CompositeTypeStmt); CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
RangeVar *r = makeNode(RangeVar);
/* can't use qualified_name, sigh */ /* can't use qualified_name, sigh */
switch (list_length($3)) n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner);
{
case 1:
r->catalogname = NULL;
r->schemaname = NULL;
r->relname = strVal(linitial($3));
break;
case 2:
r->catalogname = NULL;
r->schemaname = strVal(linitial($3));
r->relname = strVal(lsecond($3));
break;
case 3:
r->catalogname = strVal(linitial($3));
r->schemaname = strVal(lsecond($3));
r->relname = strVal(lthird($3));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString($3)),
parser_errposition(@3)));
break;
}
r->location = @3;
n->typevar = r;
n->coldeflist = $6; n->coldeflist = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
@ -5836,6 +5877,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->newname = $6; n->newname = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER TYPE_P any_name RENAME ATTRIBUTE name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_ATTRIBUTE;
n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
n->subname = $6;
n->newname = $8;
$$ = (Node *)n;
}
; ;
opt_column: COLUMN { $$ = COLUMN; } opt_column: COLUMN { $$ = COLUMN; }
@ -8216,6 +8266,11 @@ where_or_current_clause:
; ;
OptTableFuncElementList:
TableFuncElementList { $$ = $1; }
| /*EMPTY*/ { $$ = NIL; }
;
TableFuncElementList: TableFuncElementList:
TableFuncElement TableFuncElement
{ {
@ -10897,6 +10952,7 @@ unreserved_keyword:
| ASSERTION | ASSERTION
| ASSIGNMENT | ASSIGNMENT
| AT | AT
| ATTRIBUTE
| BACKWARD | BACKWARD
| BEFORE | BEFORE
| BEGIN_P | BEGIN_P
@ -11857,6 +11913,47 @@ TableFuncTypeName(List *columns)
return result; return result;
} }
/*
* Convert a list of (dotted) names to a RangeVar (like
* makeRangeVarFromNameList, but with position support). The
* "AnyName" refers to the any_name production in the grammar.
*/
static RangeVar *
makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
{
RangeVar *r = makeNode(RangeVar);
switch (list_length(names))
{
case 1:
r->catalogname = NULL;
r->schemaname = NULL;
r->relname = strVal(linitial(names));
break;
case 2:
r->catalogname = NULL;
r->schemaname = strVal(linitial(names));
r->relname = strVal(lsecond(names));
break;
case 3:
r->catalogname = strVal(linitial(names));;
r->schemaname = strVal(lsecond(names));
r->relname = strVal(lthird(names));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(names)),
parser_errposition(position)));
break;
}
r->location = position;
return r;
}
/* /*
* Must undefine this stuff before including scan.c, since it has different * Must undefine this stuff before including scan.c, since it has different
* definitions for these macros. * definitions for these macros.

View File

@ -839,6 +839,9 @@ transformOfType(ParseState *pstate, CreateStmtContext *cxt, TypeName *ofTypename
Form_pg_attribute attr = tupdesc->attrs[i]; Form_pg_attribute attr = tupdesc->attrs[i];
ColumnDef *n = makeNode(ColumnDef); ColumnDef *n = makeNode(ColumnDef);
if (attr->attisdropped)
continue;
n->colname = pstrdup(NameStr(attr->attname)); n->colname = pstrdup(NameStr(attr->attname));
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod); n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
n->constraints = NULL; n->constraints = NULL;

View File

@ -1657,6 +1657,7 @@ CreateCommandTag(Node *parsetree)
case OBJECT_TSCONFIGURATION: case OBJECT_TSCONFIGURATION:
tag = "ALTER TEXT SEARCH CONFIGURATION"; tag = "ALTER TEXT SEARCH CONFIGURATION";
break; break;
case OBJECT_ATTRIBUTE:
case OBJECT_TYPE: case OBJECT_TYPE:
tag = "ALTER TYPE"; tag = "ALTER TYPE";
break; break;
@ -1780,6 +1781,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_SEQUENCE: case OBJECT_SEQUENCE:
tag = "ALTER SEQUENCE"; tag = "ALTER SEQUENCE";
break; break;
case OBJECT_TYPE:
tag = "ALTER TYPE";
break;
case OBJECT_VIEW: case OBJECT_VIEW:
tag = "ALTER VIEW"; tag = "ALTER VIEW";
break; break;

View File

@ -7249,13 +7249,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
res = PQexec(g_conn, query->data); res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting at least a single result */
ntups = PQntuples(res); ntups = PQntuples(res);
if (ntups < 1)
{
write_msg(NULL, "query returned no rows: %s\n", query->data);
exit_nicely();
}
i_attname = PQfnumber(res, "attname"); i_attname = PQfnumber(res, "attname");
i_atttypdefn = PQfnumber(res, "atttypdefn"); i_atttypdefn = PQfnumber(res, "atttypdefn");
@ -7356,12 +7350,12 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
res = PQexec(g_conn, query->data); res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting at least a single result */
ntups = PQntuples(res); ntups = PQntuples(res);
if (ntups < 1) if (ntups < 1)
{ {
write_msg(NULL, "query returned no rows: %s\n", query->data); PQclear(res);
exit_nicely(); destroyPQExpBuffer(query);
return;
} }
pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid"))); pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid")));

View File

@ -1253,15 +1253,45 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3); COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
} }
/* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */ /* complete ALTER TYPE <foo> with actions */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "TYPE") == 0) pg_strcasecmp(prev2_wd, "TYPE") == 0)
{ {
static const char *const list_ALTERTYPE[] = static const char *const list_ALTERTYPE[] =
{"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; {"ADD ATTRIBUTE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
"OWNER TO", "RENAME", "SET SCHEMA", NULL};
COMPLETE_WITH_LIST(list_ALTERTYPE); COMPLETE_WITH_LIST(list_ALTERTYPE);
} }
/* ALTER TYPE <foo> RENAME */
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
pg_strcasecmp(prev_wd, "RENAME") == 0)
{
static const char *const list_ALTERTYPE[] =
{"ATTRIBUTE", "TO", NULL};
COMPLETE_WITH_LIST(list_ALTERTYPE);
}
/* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 &&
pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)
COMPLETE_WITH_CONST("TO");
/* If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list of attributes */
else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 &&
(pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
pg_strcasecmp(prev2_wd, "DROP") == 0 ||
pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0)
COMPLETE_WITH_ATTR(prev3_wd, "");
/* ALTER TYPE ALTER ATTRIBUTE <foo> */
else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0))
{
COMPLETE_WITH_CONST("TYPE");
}
/* complete ALTER GROUP <foo> */ /* complete ALTER GROUP <foo> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "GROUP") == 0) pg_strcasecmp(prev2_wd, "GROUP") == 0)

View File

@ -1046,6 +1046,7 @@ typedef struct SetOperationStmt
typedef enum ObjectType typedef enum ObjectType
{ {
OBJECT_AGGREGATE, OBJECT_AGGREGATE,
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST, OBJECT_CAST,
OBJECT_COLUMN, OBJECT_COLUMN,
OBJECT_CONSTRAINT, OBJECT_CONSTRAINT,

View File

@ -49,6 +49,7 @@ PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD)
PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD) PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD) PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
PG_KEYWORD("at", AT, UNRESERVED_KEYWORD) PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD) PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD) PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)

View File

@ -854,7 +854,7 @@ select * from myview;
(0 rows) (0 rows)
alter table myview drop d; alter table myview drop d;
ERROR: "myview" is not a table ERROR: "myview" is not a table or composite type
drop view myview; drop view myview;
-- test some commands to make sure they fail on the dropped column -- test some commands to make sure they fail on the dropped column
analyze atacc1(a); analyze atacc1(a);
@ -1472,6 +1472,11 @@ select * from another;
(3 rows) (3 rows)
drop table another; drop table another;
-- table's row type
create table tab1 (a int, b text);
create table tab2 (x int, y tab1);
alter table tab1 alter column b type varchar; -- fails
ERROR: cannot alter table "tab1" because column "tab2"."y" uses its rowtype
-- --
-- lock levels -- lock levels
-- --
@ -1683,3 +1688,85 @@ drop cascades to view alter2.v1
drop cascades to function alter2.plus1(integer) drop cascades to function alter2.plus1(integer)
drop cascades to type alter2.posint drop cascades to type alter2.posint
drop cascades to type alter2.ctype drop cascades to type alter2.ctype
--
-- composite types
--
CREATE TYPE test_type AS (a int);
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
a | integer
ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails
ERROR: relation "nosuchtype" does not exist
ALTER TYPE test_type ADD ATTRIBUTE b text;
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
a | integer
b | text
ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails
ERROR: column "b" of relation "test_type" already exists
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar;
\d test_type
Composite type "public.test_type"
Column | Type
--------+-------------------
a | integer
b | character varying
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer;
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
a | integer
b | integer
ALTER TYPE test_type DROP ATTRIBUTE b;
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
a | integer
ALTER TYPE test_type DROP ATTRIBUTE c; -- fails
ERROR: column "c" of relation "test_type" does not exist
ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c;
NOTICE: column "c" of relation "test_type" does not exist, skipping
ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean;
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
d | boolean
ALTER TYPE test_type RENAME ATTRIBUTE a TO aa;
ERROR: column "a" does not exist
ALTER TYPE test_type RENAME ATTRIBUTE d TO dd;
\d test_type
Composite type "public.test_type"
Column | Type
--------+---------
dd | boolean
DROP TYPE test_type;
CREATE TYPE test_type1 AS (a int, b text);
CREATE TABLE test_tbl1 (x int, y test_type1);
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
ERROR: cannot alter type "test_type1" because column "test_tbl1"."y" uses it
CREATE TYPE test_type2 AS (a int, b text);
CREATE TABLE test_tbl2 OF test_type2;
ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails
ERROR: cannot alter type "test_type2" because it is the type of a typed table
ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails
ERROR: cannot alter type "test_type2" because it is the type of a typed table
ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails
ERROR: cannot alter type "test_type2" because it is the type of a typed table
ALTER TYPE test_type2 RENAME ATTRIBUTE b TO bb; -- fails
ERROR: cannot alter type "test_type2" because it is the type of a typed table
CREATE TYPE test_type_empty AS ();
DROP TYPE test_type_empty;

View File

@ -1090,6 +1090,11 @@ select * from another;
drop table another; drop table another;
-- table's row type
create table tab1 (a int, b text);
create table tab2 (x int, y tab1);
alter table tab1 alter column b type varchar; -- fails
-- --
-- lock levels -- lock levels
-- --
@ -1224,3 +1229,53 @@ select alter2.plus1(41);
-- clean up -- clean up
drop schema alter2 cascade; drop schema alter2 cascade;
--
-- composite types
--
CREATE TYPE test_type AS (a int);
\d test_type
ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails
ALTER TYPE test_type ADD ATTRIBUTE b text;
\d test_type
ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar;
\d test_type
ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer;
\d test_type
ALTER TYPE test_type DROP ATTRIBUTE b;
\d test_type
ALTER TYPE test_type DROP ATTRIBUTE c; -- fails
ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c;
ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean;
\d test_type
ALTER TYPE test_type RENAME ATTRIBUTE a TO aa;
ALTER TYPE test_type RENAME ATTRIBUTE d TO dd;
\d test_type
DROP TYPE test_type;
CREATE TYPE test_type1 AS (a int, b text);
CREATE TABLE test_tbl1 (x int, y test_type1);
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
CREATE TYPE test_type2 AS (a int, b text);
CREATE TABLE test_tbl2 OF test_type2;
ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails
ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails
ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails
ALTER TYPE test_type2 RENAME ATTRIBUTE b TO bb; -- fails
CREATE TYPE test_type_empty AS ();
DROP TYPE test_type_empty;