<refsynopsisdiv>
<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> 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>
+
+<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>
</refsynopsisdiv>
<para>
<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>
</listitem>
</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>
</para>
</refsect1>
to <literal>customers</literal>:
<programlisting>
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>
</para>
</refsect1>
<refsect1>
<title>Compatibility</title>
-
+
<para>
- There is no <command>ALTER TYPE</command> statement in the SQL
- standard.
+ The variants to add and drop attributes are part of the SQL
+ standard; the other variants are PostgreSQL extensions.
</para>
</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>
<refsynopsisdiv>
<synopsis>
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
( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
<title>Compatibility</title>
<para>
- This <command>CREATE TYPE</command> command is a
- <productname>PostgreSQL</productname> extension. There is a
- <command>CREATE TYPE</command> statement in the <acronym>SQL</> standard
- that is rather different in detail.
+ The first form of the <command>CREATE TYPE</command> command, which
+ creates a composite type, conforms to the <acronym>SQL</> standard.
+ The other forms are <productname>PostgreSQL</productname>
+ 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>
</refsect1>
<title>See Also</title>
<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-createdomain"></member>
+ <member><xref linkend="sql-createfunction"></member>
+ <member><xref linkend="sql-droptype"></member>
</simplelist>
</refsect1>
This command is similar to the corresponding command in the SQL
standard, apart from the <literal>IF EXISTS</>
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
<productname>PostgreSQL</productname> differ from the SQL standard.
</para>
<title>See Also</title>
<simplelist type="inline">
- <member><xref linkend="sql-createtype"></member>
<member><xref linkend="sql-altertype"></member>
+ <member><xref linkend="sql-createtype"></member>
</simplelist>
</refsect1>
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_COLUMN:
+ case OBJECT_ATTRIBUTE:
case OBJECT_TRIGGER:
{
Oid relid;
break;
}
case OBJECT_COLUMN:
+ case OBJECT_ATTRIBUTE:
renameatt(relid,
stmt->subname, /* old att name */
stmt->newname, /* new att name */
static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
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 ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, LOCKMODE lockmode);
+static void find_typed_table_dependencies(Oid typeOid, const char *typeName);
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
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
* break anything from the system's point of view, since internal
/*
* 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;
case OBJECT_INDEX:
RelationGetRelationName(rel))));
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:
if (rel->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR,
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, true);
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
- ATSimplePermissions(rel, true);
+ ATSimplePermissions(rel, true, false);
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
* substitutes default values into INSERTs before it expands
* rules.
*/
- ATSimplePermissions(rel, true);
+ ATSimplePermissions(rel, true, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_DROP;
break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR;
pass = AT_PASS_MISC;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_DropColumn: /* DROP COLUMN */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, true);
ATPrepDropColumn(rel, recurse, cmd);
/* Recursion occurs during execution phase */
pass = AT_PASS_DROP;
break;
case AT_AddIndex: /* ADD INDEX */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_ADD_INDEX;
break;
case AT_AddConstraint: /* ADD CONSTRAINT */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_ADD_CONSTR;
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_DROP;
break;
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, true);
/* Performs own recursion */
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE;
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddOids: /* SET WITH OIDS */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* Performs own recursion */
if (!rel->rd_rel->relhasoids || recursing)
ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_DropOids: /* SET WITHOUT OIDS */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* Performs own recursion */
if (rel->rd_rel->relhasoids)
{
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* This command never recurses */
ATPrepAddInherit(rel);
pass = AT_PASS_MISC;
case AT_EnableReplicaRule:
case AT_DisableRule:
case AT_DropInherit: /* NO INHERIT */
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
* - Ensure that it is not a system table
*/
static void
-ATSimplePermissions(Relation rel, bool allowView)
+ATSimplePermissions(Relation rel, bool allowView, bool allowType)
{
if (rel->rd_rel->relkind != RELKIND_RELATION)
{
errmsg("\"%s\" is not a table or view",
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
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
}
+/*
+ * 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
*
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
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
* 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.
*/
- 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);
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
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 */
if (recurse)
cmd->subtype = AT_DropColumnRecurse;
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, true);
/*
* get the number of the attribute
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
/*
* Call AddRelationNewConstraints to do the work, making sure it works on
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false);
+ ATSimplePermissions(rel, false, false);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
/* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype, false);
- /*
- * 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)
+ if (tab->relkind == RELKIND_RELATION)
{
- 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 */
- rte = addRangeTableEntryForRelation(pstate,
- rel,
- NULL,
- false,
- true);
- addRTEtoQuery(pstate, rte, false, true, true);
+ /* Expression must be able to access vars of old table */
+ rte = addRangeTableEntryForRelation(pstate,
+ rel,
+ NULL,
+ false,
+ true);
+ addRTEtoQuery(pstate, rte, false, true, true);
- transform = transformExpr(pstate, cmd->transform);
+ transform = transformExpr(pstate, cmd->transform);
- /* It can't return a set */
- if (expression_returns_set(transform))
+ /* It can't return a set */
+ 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,
(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)
- 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);
- }
+ /*
+ * Add a work queue item to make ATRewriteTable update the column
+ * contents.
+ */
+ newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
+ newval->attnum = attnum;
+ newval->expr = (Expr *) transform;
- 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))));
+ tab->newvals = lappend(tab->newvals, newval);
+ }
- /*
- * Add a work queue item to make ATRewriteTable update the column
- * contents.
- */
- newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
- newval->attnum = attnum;
- newval->expr = (Expr *) transform;
+ if (tab->relkind == RELKIND_COMPOSITE_TYPE)
+ {
+ /*
+ * For composite types, do this check now. Tables will check
+ * it later when the table is being rewritten.
+ */
+ find_composite_type_dependencies(rel->rd_rel->reltype,
+ NULL,
+ RelationGetRelationName(rel));
- tab->newvals = lappend(tab->newvals, newval);
+ find_typed_table_dependencies(rel->rd_rel->reltype,
+ RelationGetRelationName(rel));
+ }
ReleaseSysCache(tuple);
* Must be owner of both parent and child -- child was checked by
* ATSimplePermissions call in ATPrepCmd
*/
- ATSimplePermissions(parent_rel, false);
+ ATSimplePermissions(parent_rel, false, false);
/* Permanent rels cannot inherit from temporary ones */
if (parent_rel->rd_istemp && !child_rel->rd_istemp)
Oid typeNamespace;
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
* uninteresting for composite types...
List *args, int location);
static List *mergeTableFuncParameters(List *func_args, List *columns);
static TypeName *TableFuncTypeName(List *columns);
+static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
%}
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+ AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
%type <node> alter_column_default opclass_item opclass_drop alter_using
%type <ival> add_drop opt_asc_desc opt_nulls_order
-%type <node> alter_table_cmd
-%type <list> alter_table_cmds
+%type <node> alter_table_cmd alter_type_cmd
+%type <list> alter_table_cmds alter_type_cmds
%type <dbehavior> opt_drop_behavior
reloption_list group_clause TriggerFuncArgs select_limit
opt_select_limit opclass_item_list opclass_drop_list
opt_opfamily transaction_mode_list_or_empty
- TableFuncElementList opt_type_modifiers
+ OptTableFuncElementList TableFuncElementList opt_type_modifiers
prep_type_clause
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
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
BOOLEAN_P BOTH BY
| AlterOwnerStmt
| AlterSeqStmt
| AlterTableStmt
+ | AlterCompositeTypeStmt
| AlterRoleSetStmt
| AlterRoleStmt
| AlterTSConfigurationStmt
;
+/*****************************************************************************
+ *
+ * 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 :
n->definition = NIL;
$$ = (Node *)n;
}
- | CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
+ | CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')'
{
CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
- RangeVar *r = makeNode(RangeVar);
/* can't use qualified_name, sigh */
- switch (list_length($3))
- {
- 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->typevar = makeRangeVarFromAnyName($3, @3, yyscanner);
n->coldeflist = $6;
$$ = (Node *)n;
}
n->newname = $6;
$$ = (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; }
;
+OptTableFuncElementList:
+ TableFuncElementList { $$ = $1; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
TableFuncElementList:
TableFuncElement
{
| ASSERTION
| ASSIGNMENT
| AT
+ | ATTRIBUTE
| BACKWARD
| BEFORE
| BEGIN_P
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
* definitions for these macros.
Form_pg_attribute attr = tupdesc->attrs[i];
ColumnDef *n = makeNode(ColumnDef);
+ if (attr->attisdropped)
+ continue;
+
n->colname = pstrdup(NameStr(attr->attname));
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
n->constraints = NULL;
case OBJECT_TSCONFIGURATION:
tag = "ALTER TEXT SEARCH CONFIGURATION";
break;
+ case OBJECT_ATTRIBUTE:
case OBJECT_TYPE:
tag = "ALTER TYPE";
break;
case OBJECT_SEQUENCE:
tag = "ALTER SEQUENCE";
break;
+ case OBJECT_TYPE:
+ tag = "ALTER TYPE";
+ break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
- /* Expecting at least a single result */
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_atttypdefn = PQfnumber(res, "atttypdefn");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
- /* Expecting at least a single result */
ntups = PQntuples(res);
if (ntups < 1)
{
- write_msg(NULL, "query returned no rows: %s\n", query->data);
- exit_nicely();
+ PQclear(res);
+ destroyPQExpBuffer(query);
+ return;
}
pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid")));
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 &&
pg_strcasecmp(prev2_wd, "TYPE") == 0)
{
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);
}
+ /* 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> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "GROUP") == 0)
typedef enum ObjectType
{
OBJECT_AGGREGATE,
+ OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_CONSTRAINT,
PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
+PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
(0 rows)
alter table myview drop d;
-ERROR: "myview" is not a table
+ERROR: "myview" is not a table or composite type
drop view myview;
-- test some commands to make sure they fail on the dropped column
analyze atacc1(a);
(3 rows)
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
--
drop cascades to function alter2.plus1(integer)
drop cascades to type alter2.posint
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;
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
--
-- clean up
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;