diff --git a/doc/src/sgml/ref/alter_type.sgml b/doc/src/sgml/ref/alter_type.sgml index d76feb48c30ee2108a4a4f2e10d3524f9be3cf0c..315922ea8365c482cdd787d1a775e0220ba1625c 100644 --- a/doc/src/sgml/ref/alter_type.sgml +++ b/doc/src/sgml/ref/alter_type.sgml @@ -23,9 +23,17 @@ PostgreSQL documentation <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> @@ -34,6 +42,76 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace <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> @@ -90,6 +168,34 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace </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> @@ -117,16 +223,32 @@ ALTER TYPE email OWNER TO joe; 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> diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index ca521b387b341dfee96c6c25b158789a0d0cbc7c..a3c75b51d0fb3f461005249b9fbfc65a12adf35a 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -22,7 +22,7 @@ PostgreSQL documentation <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>' [, ... ] ] ) @@ -768,10 +768,18 @@ CREATE TABLE big_objs ( <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> @@ -779,10 +787,10 @@ CREATE TABLE big_objs ( <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> diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml index 10e3e143bc2de43ac8c8fb0ac1b1b0704c9d11fd..76e31517a1a6bf252da0585f865b56b44b6cf7df 100644 --- a/doc/src/sgml/ref/drop_type.sgml +++ b/doc/src/sgml/ref/drop_type.sgml @@ -97,7 +97,7 @@ DROP TYPE box; 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> @@ -107,8 +107,8 @@ DROP TYPE box; <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> diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index a315096fa4392928ffe6cc9510b44b5f08b927ab..794d355944d3bec337deef3f4fa59336587c2fd3 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -89,6 +89,7 @@ ExecRenameStmt(RenameStmt *stmt) case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_COLUMN: + case OBJECT_ATTRIBUTE: case OBJECT_TRIGGER: { Oid relid; @@ -123,6 +124,7 @@ ExecRenameStmt(RenameStmt *stmt) break; } case OBJECT_COLUMN: + case OBJECT_ATTRIBUTE: renameatt(relid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 82143682d582d7b9110006c83d3a98d0d2d1c18e..a5d7af604209c9fa9bc23a981f402011bf2cba5f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -263,12 +263,13 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, 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, @@ -1978,6 +1979,10 @@ renameatt(Oid myrelid, (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 @@ -2368,8 +2373,13 @@ AlterTable(AlterTableStmt *stmt) /* * 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: @@ -2388,6 +2398,14 @@ AlterTable(AlterTableStmt *stmt) 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, @@ -2639,14 +2657,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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; @@ -2659,19 +2677,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * 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; @@ -2689,25 +2707,25 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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) @@ -2715,7 +2733,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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) @@ -2723,7 +2741,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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; @@ -2735,20 +2753,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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) { @@ -2775,7 +2793,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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; @@ -2793,7 +2811,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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; @@ -3519,7 +3537,7 @@ ATGetQueueEntry(List **wqueue, Relation rel) * - 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) { @@ -3531,6 +3549,14 @@ ATSimplePermissions(Relation rel, bool allowView) 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), @@ -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 * @@ -3804,6 +3868,10 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, (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 @@ -4007,7 +4075,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * 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); @@ -4535,6 +4603,10 @@ ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd) (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; @@ -4554,7 +4626,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, /* 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 @@ -4858,7 +4930,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* 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 @@ -5801,7 +5873,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* 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); @@ -6033,76 +6105,93 @@ ATPrepAlterColumnType(List **wqueue, /* 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); @@ -7367,7 +7456,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode) * 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) diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 87fcf29bf2ff78174d8977b317c8fc7e68e51067..25503bda4f48661575ea5782c088b41dd92b8831 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1508,11 +1508,6 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist) 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... diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 22ce35382bc65566ed1b6e02b3ce57885649cfa3..40bd7a393251cd2ed2b3c6f713ce1ce0cc47a599 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -131,6 +131,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, 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); %} @@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns); 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 @@ -218,8 +219,8 @@ static TypeName *TableFuncTypeName(List *columns); %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 @@ -295,7 +296,7 @@ static TypeName *TableFuncTypeName(List *columns); 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 @@ -462,7 +463,7 @@ static TypeName *TableFuncTypeName(List *columns); /* 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 @@ -657,6 +658,7 @@ stmt : | AlterOwnerStmt | AlterSeqStmt | AlterTableStmt + | AlterCompositeTypeStmt | AlterRoleSetStmt | AlterRoleStmt | 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 : @@ -3678,39 +3746,12 @@ DefineStmt: 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; } @@ -5836,6 +5877,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name 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; } @@ -8216,6 +8266,11 @@ where_or_current_clause: ; +OptTableFuncElementList: + TableFuncElementList { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + TableFuncElementList: TableFuncElement { @@ -10897,6 +10952,7 @@ unreserved_keyword: | ASSERTION | ASSIGNMENT | AT + | ATTRIBUTE | BACKWARD | BEFORE | BEGIN_P @@ -11857,6 +11913,47 @@ TableFuncTypeName(List *columns) 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. diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 552a6af8f874563fabf87f1d0c1f9da7fc0aca32..37ca331c215661fde72d5e0a4bc3be613c801588 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -839,6 +839,9 @@ transformOfType(ParseState *pstate, CreateStmtContext *cxt, TypeName *ofTypename 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; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 1865f843d2a0807ff468c5252ee81cc4cbc7cfb5..f69559f0e438849d2c3b5d709b7e0313ee4c2fe0 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1657,6 +1657,7 @@ CreateCommandTag(Node *parsetree) case OBJECT_TSCONFIGURATION: tag = "ALTER TEXT SEARCH CONFIGURATION"; break; + case OBJECT_ATTRIBUTE: case OBJECT_TYPE: tag = "ALTER TYPE"; break; @@ -1780,6 +1781,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_SEQUENCE: tag = "ALTER SEQUENCE"; break; + case OBJECT_TYPE: + tag = "ALTER TYPE"; + break; case OBJECT_VIEW: tag = "ALTER VIEW"; break; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 06c1f3ae605103593f2af3d9a8d858b098658914..2e1486f221680f435c77c313465dbd2c625ed6b7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7249,13 +7249,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) 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"); @@ -7356,12 +7350,12 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) 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"))); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 3efa97ad88f3160d9757d1a15e77a5c64dd0e688..04c81844da59e1f4a288b82f100c6a82a8b270f2 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1253,15 +1253,45 @@ psql_completion(char *text, int start, int end) 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) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b67ab68298bcc28e6526eead1489421d2b6c957b..68de95e49fd1ed93963d001fdb9052b05496fd1f 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1046,6 +1046,7 @@ typedef struct SetOperationStmt typedef enum ObjectType { OBJECT_AGGREGATE, + OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */ OBJECT_CAST, OBJECT_COLUMN, OBJECT_CONSTRAINT, diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 5e3ccd5ee2fb145bc1c63b7a6576c9c8ac92c0c9..01005d83c2c5dc1d288485a5d53720edf2e2e285 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -49,6 +49,7 @@ PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD) 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) diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 83e24fd8c96415d73f0af3053c9faa14e30ecf22..ab19a8e4fc8a9818b1f1386e513548f75452fbb1 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -854,7 +854,7 @@ select * from myview; (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); @@ -1472,6 +1472,11 @@ select * from another; (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 -- @@ -1683,3 +1688,85 @@ drop cascades to view alter2.v1 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; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 760670cd0693b22ebd05ba8927595c0e126ef5ca..3e1646a96dde940ce5db3f4f221c6865ce72a699 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1090,6 +1090,11 @@ select * from 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 -- @@ -1224,3 +1229,53 @@ select alter2.plus1(41); -- 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;