On tis, 2010-09-21 at 17:53 +0900, KaiGai Kohei wrote: > Sorry, I missed a bug when we create a typed table using composite > type which has been altered.
> Perhaps, we also need to patch at transformOfType() to > skip attributes with attisdropped. Fixed. > An additional question. It seems me we can remove all the attributes > from the composite type, although CREATE TYPE prohibits to create > a composite type without any attribute. > What does it mean a composite type with no attribute? > Or, do we need a restriction to prevent the last one attribute? We need to allow the creation of zero-attribute types then; same as with CREATE TABLE. I have fixed that now.
diff --git a/doc/src/sgml/ref/alter_type.sgml b/doc/src/sgml/ref/alter_type.sgml index e2fec32..e984e48 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> @@ -119,14 +225,30 @@ ALTER TYPE email OWNER TO joe; 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 b481073..6c497ca 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 753ef2b..9096e66 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 788a4db..9da2690 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 1e5ac13..3f1581e 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), @@ -3759,6 +3785,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 * * Adds an additional attribute to a relation making the assumption that @@ -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,6 +6105,8 @@ ATPrepAlterColumnType(List **wqueue, /* make sure datatype is legal for a column */ 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 @@ -6103,6 +6177,21 @@ ATPrepAlterColumnType(List **wqueue, newval->expr = (Expr *) transform; tab->newvals = lappend(tab->newvals, newval); + } + + 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)); + + 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 7bad4b3..98b3fbc 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 b38b2b1..0423553 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 @@ -1970,6 +1972,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 : * close <portalname> * @@ -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 @@ -11858,6 +11914,47 @@ TableFuncTypeName(List *columns) } /* + * 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 af25c21..0d0a0ac 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 44cc401..0b46247 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/psql/tab-complete.c b/src/bin/psql/tab-complete.c index e5ea553..62de203 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 e896dc7..0543576 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 974bb7a..51c3b1f 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 83e24fd..ab19a8e 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 760670c..3e1646a 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;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers