]> granicus.if.org Git - postgresql/commitdiff
Remove collation information from TypeName, where it does not belong.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Mar 2011 03:38:52 +0000 (22:38 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Mar 2011 03:39:20 +0000 (22:39 -0500)
The initial collations patch treated a COLLATE spec as part of a TypeName,
following what can only be described as brain fade on the part of the SQL
committee.  It's a lot more reasonable to treat COLLATE as a syntactically
separate object, so that it can be added in only the productions where it
actually belongs, rather than needing to reject it in a boatload of places
where it doesn't belong (something the original patch mostly failed to do).
In addition this change lets us meet the spec's requirement to allow
COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly
behavior for constructs such as "foo::type COLLATE collation".

To do this, pull collation information out of TypeName and put it in
ColumnDef instead, thus reverting most of the collation-related changes in
parse_type.c's API.  I made one additional structural change, which was to
use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd
nodes.  This provides enough room to get rid of the "transform" wart in
AlterTableCmd too, since the ColumnDef can carry the USING expression
easily enough.

Also fix some other minor bugs that have crept in in the same areas,
like failure to copy recently-added fields of ColumnDef in copyfuncs.c.

While at it, document the formerly secret ability to specify a collation
in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and
ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about
what the default collation selection will be when COLLATE is omitted.

BTW, the three-parameter form of format_type() should go away too,
since it just contributes to the confusion in this area; but I'll do
that in a separate patch.

28 files changed:
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/alter_type.sgml
doc/src/sgml/ref/create_domain.sgml
doc/src/sgml/ref/create_table.sgml
src/backend/access/common/tupdesc.c
src/backend/commands/collationcmds.c
src/backend/commands/functioncmds.c
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/commands/view.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/makefuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_type.c
src/backend/parser/parse_utilcmd.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/makefuncs.h
src/include/nodes/parsenodes.h
src/include/parser/parse_type.h
src/pl/plpgsql/src/pl_comp.c
src/test/regress/expected/collate.linux.utf8.out
src/test/regress/expected/foreign_data.out

index b8c4c507a2c5ec68ded8f102e2267cbeea450bbf..c1948624d7033c7b038bbe304ebd2890a2bda614 100644 (file)
@@ -32,9 +32,9 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
 
 <phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
 
-    ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+    ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable class="PARAMETER">collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
     DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
-    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable> [ USING <replaceable class="PARAMETER">expression</replaceable> ]
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable class="PARAMETER">collation</replaceable> ] [ USING <replaceable class="PARAMETER">expression</replaceable> ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
@@ -115,7 +115,11 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
       This form changes the type of a column of a table. Indexes and
       simple table constraints involving the column will be automatically
       converted to use the new column type by reparsing the originally
-      supplied expression.  The optional <literal>USING</literal>
+      supplied expression.
+      The optional <literal>COLLATE</literal> clause specifies a collation
+      for the new column; if omitted, the collation is the default for the
+      new column type.
+      The optional <literal>USING</literal>
       clause specifies how to compute the new column value from the old;
       if omitted, the default conversion is the same as an assignment
       cast from old data type to new.  A  <literal>USING</literal>
index 09db0cc8b2d7aa380460e4f7e16f54928a2f8970..e889ffbc352e41724d0d238f90c50ac13f06ca8e 100644 (file)
@@ -32,9 +32,9 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD VALUE <replacea
 
 <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> [ CASCADE | RESTRICT ]
+    ADD ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable class="PARAMETER">collation</replaceable> ] [ CASCADE | RESTRICT ]
     DROP ATTRIBUTE [ IF EXISTS ] <replaceable class="PARAMETER">attribute_name</replaceable> [ CASCADE | RESTRICT ]
-    ALTER ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable> [ CASCADE | RESTRICT ]
+    ALTER ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable class="PARAMETER">collation</replaceable> ] [ CASCADE | RESTRICT ]
 </synopsis>
  </refsynopsisdiv>
 
index 83be889c6d9110cc44fa7918dcf274d6825b0f22..2300edefe3a0d8acc7d98170b2aa4560138b0a54 100644 (file)
@@ -89,8 +89,9 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
       <listitem>
        <para>
         An optional collation for the domain.  If no collation is
-        specified, the database default collation is used (which can
-        be overridden when the domain is used to define a column).
+        specified, the underlying data type's default collation is used.
+        The underlying type must be collatable when <literal>COLLATE</>
+        is specified.
        </para>
       </listitem>
      </varlistentry>
index 9d2d99ad2e3774654ff9acd3d496de1e6f9b34ee..0fbe1160972e2cc711e57d20a92b23009ca2d6e7 100644 (file)
@@ -248,9 +248,9 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     <term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
     <listitem>
      <para>
-      The <literal>COLLATE</> clause assigns a nondefault collation to
-      the column.  By default, the locale settings of the database are
-      used.
+      The <literal>COLLATE</> clause assigns a collation to
+      the column (which must be of a collatable data type).
+      If not specified, the column data type's default collation is used.
      </para>
     </listitem>
    </varlistentry>
index 0cd1f941e3fe9bb146190aaaedc79c922bb3448c..d78b08381e00d4d76ce5481c8d536193bfce5de2 100644 (file)
@@ -559,7 +559,8 @@ BuildDescForRelation(List *schema)
                attnum++;
 
                attname = entry->colname;
-               typenameTypeIdModColl(NULL, entry->typeName, &atttypid, &atttypmod, &attcollation);
+               typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
+               attcollation = GetColumnDefCollation(NULL, entry, atttypid);
                attdim = list_length(entry->typeName->arrayBounds);
 
                if (entry->typeName->setof)
index 18e88d2653fc465fb8c6a072a88f8a5f2da55feb..a52cb351ac83219e4f39276c691f8ad0ab7f919b 100644 (file)
@@ -98,7 +98,7 @@ DefineCollation(List *names, List *parameters)
                Oid                     collid;
                HeapTuple       tp;
 
-               collid = LookupCollation(NULL, defGetQualifiedName(fromEl), -1);
+               collid =  get_collation_oid(defGetQualifiedName(fromEl), false);
                tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
                if (!HeapTupleIsValid(tp))
                        elog(ERROR, "cache lookup failed for collation %u", collid);
index 3f25b3bf02a232e4a57bf5679a1e66970335b5c4..a8ef947240e3d7ff178ee42d15bff7f7b6be9a77 100644 (file)
@@ -87,7 +87,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
        Oid                     rettype;
        Type            typtup;
 
-       typtup = LookupTypeName(NULL, returnType, NULL, NULL);
+       typtup = LookupTypeName(NULL, returnType, NULL);
 
        if (typtup)
        {
@@ -207,7 +207,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
                Oid                     toid;
                Type            typtup;
 
-               typtup = LookupTypeName(NULL, t, NULL, NULL);
+               typtup = LookupTypeName(NULL, t, NULL);
                if (typtup)
                {
                        if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
index e71c311faf732a3387bd308690a4014a373cf1c4..5c6212c64cd792c0b272f35f26d214cd6fb85f9d 100644 (file)
@@ -139,9 +139,12 @@ DefineSequence(CreateSeqStmt *seq)
                coldef->inhcount = 0;
                coldef->is_local = true;
                coldef->is_not_null = true;
+               coldef->is_from_type = false;
                coldef->storage = 0;
                coldef->raw_default = NULL;
                coldef->cooked_default = NULL;
+               coldef->collClause = NULL;
+               coldef->collOid = InvalidOid;
                coldef->constraints = NIL;
 
                null[i - 1] = false;
@@ -149,53 +152,53 @@ DefineSequence(CreateSeqStmt *seq)
                switch (i)
                {
                        case SEQ_COL_NAME:
-                               coldef->typeName = makeTypeNameFromOid(NAMEOID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
                                coldef->colname = "sequence_name";
                                namestrcpy(&name, seq->sequence->relname);
                                value[i - 1] = NameGetDatum(&name);
                                break;
                        case SEQ_COL_LASTVAL:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "last_value";
                                value[i - 1] = Int64GetDatumFast(new.last_value);
                                break;
                        case SEQ_COL_STARTVAL:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "start_value";
                                value[i - 1] = Int64GetDatumFast(new.start_value);
                                break;
                        case SEQ_COL_INCBY:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "increment_by";
                                value[i - 1] = Int64GetDatumFast(new.increment_by);
                                break;
                        case SEQ_COL_MAXVALUE:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "max_value";
                                value[i - 1] = Int64GetDatumFast(new.max_value);
                                break;
                        case SEQ_COL_MINVALUE:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "min_value";
                                value[i - 1] = Int64GetDatumFast(new.min_value);
                                break;
                        case SEQ_COL_CACHE:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "cache_value";
                                value[i - 1] = Int64GetDatumFast(new.cache_value);
                                break;
                        case SEQ_COL_LOG:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
                                coldef->colname = "log_cnt";
                                value[i - 1] = Int64GetDatum((int64) 1);
                                break;
                        case SEQ_COL_CYCLE:
-                               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
                                coldef->colname = "is_cycled";
                                value[i - 1] = BoolGetDatum(new.is_cycled);
                                break;
                        case SEQ_COL_CALLED:
-                               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
+                               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
                                coldef->colname = "is_called";
                                value[i - 1] = BoolGetDatum(false);
                                break;
index 3be9a6f34815cd05de00668b61c7ca9eabbf7074..f1264bfb66d61595eea92274bd107674f8898397 100644 (file)
@@ -339,7 +339,7 @@ static void ATPrepAlterColumnType(List **wqueue,
                                          AlterTableCmd *cmd, LOCKMODE lockmode);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-                                         const char *colName, TypeName *typeName, LOCKMODE lockmode);
+                                                                 AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
 static void ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode);
 static void change_owner_recurse_to_sequences(Oid relationOid,
@@ -1433,7 +1433,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                                (errmsg("merging multiple inherited definitions of column \"%s\"",
                                                                attributeName)));
                                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-                               typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defCollId);
+                               typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
                                if (defTypeId != attribute->atttypid ||
                                        deftypmod != attribute->atttypmod)
                                        ereport(ERROR,
@@ -1443,6 +1443,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                                         errdetail("%s versus %s",
                                                                           TypeNameToString(def->typeName),
                                                                           format_type_be(attribute->atttypid))));
+                               defCollId = GetColumnDefCollation(NULL, def, defTypeId);
                                if (defCollId != attribute->attcollation)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_COLLATION_MISMATCH),
@@ -1478,14 +1479,16 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                def = makeNode(ColumnDef);
                                def->colname = pstrdup(attributeName);
                                def->typeName = makeTypeNameFromOid(attribute->atttypid,
-                                                                                                       attribute->atttypmod,
-                                                                                                       attribute->attcollation);
+                                                                                                       attribute->atttypmod);
                                def->inhcount = 1;
                                def->is_local = false;
                                def->is_not_null = attribute->attnotnull;
+                               def->is_from_type = false;
                                def->storage = attribute->attstorage;
                                def->raw_default = NULL;
                                def->cooked_default = NULL;
+                               def->collClause = NULL;
+                               def->collOid = attribute->attcollation;
                                def->constraints = NIL;
                                inhSchema = lappend(inhSchema, def);
                                newattno[parent_attno - 1] = ++child_attno;
@@ -1616,8 +1619,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                   (errmsg("merging column \"%s\" with inherited definition",
                                                   attributeName)));
                                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-                               typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defcollid);
-                               typenameTypeIdModColl(NULL, newdef->typeName, &newTypeId, &newtypmod, &newcollid);
+                               typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
+                               typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
                                if (defTypeId != newTypeId || deftypmod != newtypmod)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1626,6 +1629,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                                         errdetail("%s versus %s",
                                                                           TypeNameToString(def->typeName),
                                                                           TypeNameToString(newdef->typeName))));
+                               defcollid = GetColumnDefCollation(NULL, def, defTypeId);
+                               newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
                                if (defcollid != newcollid)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_COLLATION_MISMATCH),
@@ -3092,7 +3097,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                                                 cmd->missing_ok, lockmode);
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
-                       ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def, lockmode);
+                       ATExecAlterColumnType(tab, rel, cmd, lockmode);
                        break;
                case AT_ChangeOwner:    /* ALTER OWNER */
                        ATExecChangeOwner(RelationGetRelid(rel),
@@ -4129,13 +4134,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
                        Oid                     ccollid;
 
                        /* Child column must match by type */
-                       typenameTypeIdModColl(NULL, colDef->typeName, &ctypeId, &ctypmod, &ccollid);
+                       typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
                        if (ctypeId != childatt->atttypid ||
                                ctypmod != childatt->atttypmod)
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                                 errmsg("child table \"%s\" has different type for column \"%s\"",
                                                        RelationGetRelationName(rel), colDef->colname)));
+                       ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
                        if (ccollid != childatt->attcollation)
                                ereport(ERROR,
                                                (errcode(ERRCODE_COLLATION_MISMATCH),
@@ -4201,9 +4207,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
                                                        MaxHeapAttributeNumber)));
        }
 
-       typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid);
+       typeTuple = typenameType(NULL, colDef->typeName, &typmod);
        tform = (Form_pg_type) GETSTRUCT(typeTuple);
        typeOid = HeapTupleGetOid(typeTuple);
+       collOid = GetColumnDefCollation(NULL, colDef, typeOid);
 
        /* make sure datatype is legal for a column */
        CheckAttributeType(colDef->colname, typeOid, collOid, false);
@@ -4413,7 +4420,7 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
                ColumnDef  *cdef = makeNode(ColumnDef);
 
                cdef->colname = pstrdup("oid");
-               cdef->typeName = makeTypeNameFromOid(OIDOID, -1, InvalidOid);
+               cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
                cdef->inhcount = 0;
                cdef->is_local = true;
                cdef->is_not_null = true;
@@ -6471,14 +6478,15 @@ ATPrepAlterColumnType(List **wqueue,
                                          AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        char       *colName = cmd->name;
-       TypeName   *typeName = (TypeName *) cmd->def;
+       ColumnDef  *def = (ColumnDef *) cmd->def;
+       TypeName   *typeName = def->typeName;
+       Node       *transform = def->raw_default;
        HeapTuple       tuple;
        Form_pg_attribute attTup;
        AttrNumber      attnum;
        Oid                     targettype;
        int32           targettypmod;
        Oid                     targetcollid;
-       Node       *transform;
        NewColumnValue *newval;
        ParseState *pstate = make_parsestate(NULL);
 
@@ -6512,7 +6520,10 @@ ATPrepAlterColumnType(List **wqueue,
                                                colName)));
 
        /* Look up the target type */
-       typenameTypeIdModColl(NULL, typeName, &targettype, &targettypmod, &targetcollid);
+       typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+
+       /* And the collation */
+       targetcollid = GetColumnDefCollation(NULL, def, targettype);
 
        /* make sure datatype is legal for a column */
        CheckAttributeType(colName, targettype, targetcollid, false);
@@ -6527,7 +6538,7 @@ ATPrepAlterColumnType(List **wqueue,
                 * because we need the expression to be parsed against the original table
                 * rowtype.
                 */
-               if (cmd->transform)
+               if (transform)
                {
                        RangeTblEntry *rte;
 
@@ -6539,7 +6550,7 @@ ATPrepAlterColumnType(List **wqueue,
                                                                                                true);
                        addRTEtoQuery(pstate, rte, false, true, true);
 
-                       transform = transformExpr(pstate, cmd->transform);
+                       transform = transformExpr(pstate, transform);
 
                        /* It can't return a set */
                        if (expression_returns_set(transform))
@@ -6592,16 +6603,13 @@ ATPrepAlterColumnType(List **wqueue,
                if (ATColumnChangeRequiresRewrite(transform, attnum))
                        tab->rewrite = true;
        }
-       else if (tab->relkind == RELKIND_FOREIGN_TABLE)
-       {
-               if (cmd->transform)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                        errmsg("ALTER TYPE USING is not supported on foreign tables")));
-       }
+       else if (transform)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("ALTER TYPE USING is only supported on plain tables")));
 
-       if (tab->relkind == RELKIND_COMPOSITE_TYPE
-               || tab->relkind == RELKIND_FOREIGN_TABLE)
+       if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
+               tab->relkind == RELKIND_FOREIGN_TABLE)
        {
                /*
                 * For composite types, do this check now.  Tables will check
@@ -6667,8 +6675,11 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
 
 static void
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-                                         const char *colName, TypeName *typeName, LOCKMODE lockmode)
+                                         AlterTableCmd *cmd, LOCKMODE lockmode)
 {
+       char       *colName = cmd->name;
+       ColumnDef  *def = (ColumnDef *) cmd->def;
+       TypeName   *typeName = def->typeName;
        HeapTuple       heapTup;
        Form_pg_attribute attTup;
        AttrNumber      attnum;
@@ -6705,9 +6716,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                                colName)));
 
        /* Look up the target type (should not fail, since prep found it) */
-       typeTuple = typenameType(NULL, typeName, &targettypmod, &targetcollid);
+       typeTuple = typenameType(NULL, typeName, &targettypmod);
        tform = (Form_pg_type) GETSTRUCT(typeTuple);
        targettype = HeapTupleGetOid(typeTuple);
+       /* And the collation */
+       targetcollid = GetColumnDefCollation(NULL, def, targettype);
 
        /*
         * If there is a default expression for the column, get it and ensure we
index be1f1d791fdddaf86e091dfcc1b327717a36bd13..3513256b9a54874b9c42f1489aff67c1bbcb01ae 100644 (file)
@@ -292,7 +292,7 @@ DefineType(List *names, List *parameters)
                Type            likeType;
                Form_pg_type likeForm;
 
-               likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL, NULL);
+               likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
                likeForm = (Form_pg_type) GETSTRUCT(likeType);
                internalLength = likeForm->typlen;
                byValue = likeForm->typbyval;
@@ -649,7 +649,7 @@ RemoveTypes(DropStmt *drop)
                typename = makeTypeNameFromNameList(names);
 
                /* Use LookupTypeName here so that shell types can be removed. */
-               tup = LookupTypeName(NULL, typename, NULL, NULL);
+               tup = LookupTypeName(NULL, typename, NULL);
                if (tup == NULL)
                {
                        if (!drop->missing_ok)
@@ -774,6 +774,7 @@ DefineDomain(CreateDomainStmt *stmt)
        Oid                     basetypeoid;
        Oid                     domainoid;
        Oid                     old_type_oid;
+       Oid                     domaincoll;
        Form_pg_type baseType;
        int32           basetypeMod;
        Oid                     baseColl;
@@ -807,7 +808,7 @@ DefineDomain(CreateDomainStmt *stmt)
        /*
         * Look up the base type.
         */
-       typeTup = typenameType(NULL, stmt->typeName, &basetypeMod, &baseColl);
+       typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
        baseType = (Form_pg_type) GETSTRUCT(typeTup);
        basetypeoid = HeapTupleGetOid(typeTup);
 
@@ -825,6 +826,22 @@ DefineDomain(CreateDomainStmt *stmt)
                                 errmsg("\"%s\" is not a valid base type for a domain",
                                                TypeNameToString(stmt->typeName))));
 
+       /*
+        * Identify the collation if any
+        */
+       baseColl = baseType->typcollation;
+       if (stmt->collClause)
+               domaincoll = get_collation_oid(stmt->collClause->collnames, false);
+       else
+               domaincoll = baseColl;
+
+       /* Complain if COLLATE is applied to an uncollatable type */
+       if (OidIsValid(domaincoll) && !OidIsValid(baseColl))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("collations are not supported by type %s",
+                                               format_type_be(basetypeoid))));
+
        /* passed by value */
        byValue = baseType->typbyval;
 
@@ -1051,7 +1068,7 @@ DefineDomain(CreateDomainStmt *stmt)
                                   basetypeMod, /* typeMod value */
                                   typNDims,    /* Array dimensions for base type */
                                   typNotNull,  /* Type NOT NULL */
-                                  baseColl);
+                                  domaincoll);
 
        /*
         * Process constraints which refer to the domain ID returned by TypeCreate
@@ -2629,7 +2646,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
        typename = makeTypeNameFromNameList(names);
 
        /* Use LookupTypeName here so that shell types can be processed */
-       tup = LookupTypeName(NULL, typename, NULL, NULL);
+       tup = LookupTypeName(NULL, typename, NULL);
        if (tup == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
index 5576ea259f4bb1ee01c3a4daa9cd95d97481c3d2..794a56e84dee23c9fa3d6b794681e039cae14de9 100644 (file)
@@ -120,14 +120,23 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 
                        def->colname = pstrdup(tle->resname);
                        def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr),
-                                                                                               exprTypmod((Node *) tle->expr),
-                                                                                               exprCollation((Node *) tle->expr));
+                                                                                               exprTypmod((Node *) tle->expr));
                        def->inhcount = 0;
                        def->is_local = true;
                        def->is_not_null = false;
+                       def->is_from_type = false;
                        def->storage = 0;
                        def->raw_default = NULL;
                        def->cooked_default = NULL;
+                       def->collClause = NULL;
+                       /*
+                        * XXX Temporary kluge to make regression tests pass.  We should
+                        * be able to trust the result of exprCollation more than this.
+                        */
+                       if (type_is_collatable(exprType((Node *) tle->expr)))
+                               def->collOid = exprCollation((Node *) tle->expr);
+                       else
+                               def->collOid = InvalidOid;
                        def->constraints = NIL;
 
                        attrList = lappend(attrList, def);
index 86a16783f7174534b399f40e3e0761caaa4c558d..b948af604d8149b9d95f9c923cbb983b2df3eb40 100644 (file)
@@ -2183,8 +2183,6 @@ _copyTypeName(TypeName *from)
        COPY_NODE_FIELD(typmods);
        COPY_SCALAR_FIELD(typemod);
        COPY_NODE_FIELD(arrayBounds);
-       COPY_NODE_FIELD(collnames);
-       COPY_SCALAR_FIELD(collOid);
        COPY_LOCATION_FIELD(location);
 
        return newnode;
@@ -2295,9 +2293,12 @@ _copyColumnDef(ColumnDef *from)
        COPY_SCALAR_FIELD(inhcount);
        COPY_SCALAR_FIELD(is_local);
        COPY_SCALAR_FIELD(is_not_null);
+       COPY_SCALAR_FIELD(is_from_type);
        COPY_SCALAR_FIELD(storage);
        COPY_NODE_FIELD(raw_default);
        COPY_NODE_FIELD(cooked_default);
+       COPY_NODE_FIELD(collClause);
+       COPY_SCALAR_FIELD(collOid);
        COPY_NODE_FIELD(constraints);
 
        return newnode;
@@ -2515,7 +2516,6 @@ _copyAlterTableCmd(AlterTableCmd *from)
        COPY_SCALAR_FIELD(subtype);
        COPY_STRING_FIELD(name);
        COPY_NODE_FIELD(def);
-       COPY_NODE_FIELD(transform);
        COPY_SCALAR_FIELD(behavior);
        COPY_SCALAR_FIELD(missing_ok);
 
@@ -3063,6 +3063,7 @@ _copyCreateDomainStmt(CreateDomainStmt *from)
 
        COPY_NODE_FIELD(domainname);
        COPY_NODE_FIELD(typeName);
+       COPY_NODE_FIELD(collClause);
        COPY_NODE_FIELD(constraints);
 
        return newnode;
index c234416cf53022f93b556255b3f5041fa4621a2b..c8ee4744364e7b925415c66c23d0bc2d78b68ffa 100644 (file)
@@ -1007,7 +1007,6 @@ _equalAlterTableCmd(AlterTableCmd *a, AlterTableCmd *b)
        COMPARE_SCALAR_FIELD(subtype);
        COMPARE_STRING_FIELD(name);
        COMPARE_NODE_FIELD(def);
-       COMPARE_NODE_FIELD(transform);
        COMPARE_SCALAR_FIELD(behavior);
        COMPARE_SCALAR_FIELD(missing_ok);
 
@@ -1461,6 +1460,7 @@ _equalCreateDomainStmt(CreateDomainStmt *a, CreateDomainStmt *b)
 {
        COMPARE_NODE_FIELD(domainname);
        COMPARE_NODE_FIELD(typeName);
+       COMPARE_NODE_FIELD(collClause);
        COMPARE_NODE_FIELD(constraints);
 
        return true;
@@ -2130,8 +2130,6 @@ _equalTypeName(TypeName *a, TypeName *b)
        COMPARE_NODE_FIELD(typmods);
        COMPARE_SCALAR_FIELD(typemod);
        COMPARE_NODE_FIELD(arrayBounds);
-       COMPARE_NODE_FIELD(collnames);
-       COMPARE_SCALAR_FIELD(collOid);
        COMPARE_LOCATION_FIELD(location);
 
        return true;
@@ -2226,9 +2224,12 @@ _equalColumnDef(ColumnDef *a, ColumnDef *b)
        COMPARE_SCALAR_FIELD(inhcount);
        COMPARE_SCALAR_FIELD(is_local);
        COMPARE_SCALAR_FIELD(is_not_null);
+       COMPARE_SCALAR_FIELD(is_from_type);
        COMPARE_SCALAR_FIELD(storage);
        COMPARE_NODE_FIELD(raw_default);
        COMPARE_NODE_FIELD(cooked_default);
+       COMPARE_NODE_FIELD(collClause);
+       COMPARE_SCALAR_FIELD(collOid);
        COMPARE_NODE_FIELD(constraints);
 
        return true;
index 0225f19382a583e6dd0659a9f23d9605961f496a..d9f164523831512c332223dd378ef2603f7ed017 100644 (file)
@@ -427,16 +427,15 @@ makeTypeNameFromNameList(List *names)
 
 /*
  * makeTypeNameFromOid -
- *     build a TypeName node to represent a type already known by OID/typmod/collation.
+ *     build a TypeName node to represent a type already known by OID/typmod.
  */
 TypeName *
-makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid)
+makeTypeNameFromOid(Oid typeOid, int32 typmod)
 {
        TypeName   *n = makeNode(TypeName);
 
        n->typeOid = typeOid;
        n->typemod = typmod;
-       n->collOid = collOid;
        n->location = -1;
        return n;
 }
index 4aae2b33a6dedf658f511bdac1988a3f03a8998b..06fd7ff818e4ed45c7cbb42ab2ef1f38848839bb 100644 (file)
@@ -2067,9 +2067,12 @@ _outColumnDef(StringInfo str, ColumnDef *node)
        WRITE_INT_FIELD(inhcount);
        WRITE_BOOL_FIELD(is_local);
        WRITE_BOOL_FIELD(is_not_null);
+       WRITE_BOOL_FIELD(is_from_type);
        WRITE_INT_FIELD(storage);
        WRITE_NODE_FIELD(raw_default);
        WRITE_NODE_FIELD(cooked_default);
+       WRITE_NODE_FIELD(collClause);
+       WRITE_OID_FIELD(collOid);
        WRITE_NODE_FIELD(constraints);
 }
 
@@ -2085,8 +2088,6 @@ _outTypeName(StringInfo str, TypeName *node)
        WRITE_NODE_FIELD(typmods);
        WRITE_INT_FIELD(typemod);
        WRITE_NODE_FIELD(arrayBounds);
-       WRITE_NODE_FIELD(collnames);
-       WRITE_OID_FIELD(collOid);
        WRITE_LOCATION_FIELD(location);
 }
 
index 5b96b5b0df5e76b3720d7b11e604af5406ecc2ad..373d2adc71c90a58318e9c46e0c55024ab7caede 100644 (file)
@@ -132,6 +132,9 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
 static List *mergeTableFuncParameters(List *func_args, List *columns);
 static TypeName *TableFuncTypeName(List *columns);
 static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
+static void SplitColQualList(List *qualList,
+                                                        List **constraintList, CollateClause **collClause,
+                                                        core_yyscan_t yyscanner);
 
 %}
 
@@ -221,7 +224,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %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 alter_type_cmd
+%type <node>   alter_table_cmd alter_type_cmd opt_collate_clause
 %type <list>   alter_table_cmds alter_type_cmds
 
 %type <dbehavior>      opt_drop_behavior
@@ -400,8 +403,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <list>   copy_generic_opt_list copy_generic_opt_arg_list
 %type <list>   copy_options
 
-%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation
-                               ConstTypename
+%type <typnam> Typename SimpleTypename ConstTypename
                                GenericType Numeric opt_float
                                Character ConstCharacter
                                CharacterWithLength CharacterWithoutLength
@@ -619,6 +621,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %left          '^'
 /* Unary Operators */
 %left          AT ZONE                 /* sets precedence for AT TIME ZONE */
+%left          COLLATE
 %right         UMINUS
 %left          '[' ']'
 %left          '(' ')'
@@ -1746,13 +1749,17 @@ alter_table_cmd:
                         * ALTER TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename>
                         *              [ USING <expression> ]
                         */
-                       | ALTER opt_column ColId opt_set_data TYPE_P Typename alter_using
+                       | ALTER opt_column ColId opt_set_data TYPE_P Typename opt_collate_clause alter_using
                                {
                                        AlterTableCmd *n = makeNode(AlterTableCmd);
+                                       ColumnDef *def = makeNode(ColumnDef);
                                        n->subtype = AT_AlterColumnType;
                                        n->name = $3;
-                                       n->def = (Node *) $6;
-                                       n->transform = $7;
+                                       n->def = (Node *) def;
+                                       /* We only use these three fields of the ColumnDef node */
+                                       def->typeName = $6;
+                                       def->collClause = (CollateClause *) $7;
+                                       def->raw_default = $8;
                                        $$ = (Node *)n;
                                }
                        /* ALTER TABLE <name> ADD CONSTRAINT ... */
@@ -1981,6 +1988,19 @@ opt_drop_behavior:
                        | /* EMPTY */                           { $$ = DROP_RESTRICT; /* default */ }
                ;
 
+opt_collate_clause:
+                       COLLATE any_name
+                               {
+                                       CollateClause *n = makeNode(CollateClause);
+                                       n->arg = NULL;
+                                       n->collnames = $2;
+                                       n->collOid = InvalidOid;
+                                       n->location = @1;
+                                       $$ = (Node *) n;
+                               }
+                       | /* EMPTY */                           { $$ = NULL; }
+               ;
+
 alter_using:
                        USING a_expr                            { $$ = $2; }
                        | /* EMPTY */                           { $$ = NULL; }
@@ -2077,13 +2097,18 @@ alter_type_cmd:
                                        $$ = (Node *)n;
                                }
                        /* ALTER TYPE <name> ALTER ATTRIBUTE <attname> [SET DATA] TYPE <typename> [RESTRICT|CASCADE] */
-                       | ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename opt_drop_behavior
+                       | ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename opt_collate_clause opt_drop_behavior
                                {
                                        AlterTableCmd *n = makeNode(AlterTableCmd);
+                                       ColumnDef *def = makeNode(ColumnDef);
                                        n->subtype = AT_AlterColumnType;
                                        n->name = $3;
-                                       n->def = (Node *) $6;
-                                       n->behavior = $7;
+                                       n->def = (Node *) def;
+                                       n->behavior = $8;
+                                       /* We only use these three fields of the ColumnDef node */
+                                       def->typeName = $6;
+                                       def->collClause = (CollateClause *) $7;
+                                       def->raw_default = NULL;
                                        $$ = (Node *)n;
                                }
                ;
@@ -2454,8 +2479,16 @@ columnDef:       ColId Typename ColQualList
                                        ColumnDef *n = makeNode(ColumnDef);
                                        n->colname = $1;
                                        n->typeName = $2;
-                                       n->constraints = $3;
+                                       n->inhcount = 0;
                                        n->is_local = true;
+                                       n->is_not_null = false;
+                                       n->is_from_type = false;
+                                       n->storage = 0;
+                                       n->raw_default = NULL;
+                                       n->cooked_default = NULL;
+                                       n->collOid = InvalidOid;
+                                       SplitColQualList($3, &n->constraints, &n->collClause,
+                                                                        yyscanner);
                                        $$ = (Node *)n;
                                }
                ;
@@ -2464,8 +2497,17 @@ columnOptions:   ColId WITH OPTIONS ColQualList
                                {
                                        ColumnDef *n = makeNode(ColumnDef);
                                        n->colname = $1;
-                                       n->constraints = $4;
+                                       n->typeName = NULL;
+                                       n->inhcount = 0;
                                        n->is_local = true;
+                                       n->is_not_null = false;
+                                       n->is_from_type = false;
+                                       n->storage = 0;
+                                       n->raw_default = NULL;
+                                       n->cooked_default = NULL;
+                                       n->collOid = InvalidOid;
+                                       SplitColQualList($4, &n->constraints, &n->collClause,
+                                                                        yyscanner);
                                        $$ = (Node *)n;
                                }
                ;
@@ -2486,6 +2528,20 @@ ColConstraint:
                                }
                        | ColConstraintElem                                             { $$ = $1; }
                        | ConstraintAttr                                                { $$ = $1; }
+                       | COLLATE any_name
+                               {
+                                       /*
+                                        * Note: the CollateClause is momentarily included in
+                                        * the list built by ColQualList, but we split it out
+                                        * again in SplitColQualList.
+                                        */
+                                       CollateClause *n = makeNode(CollateClause);
+                                       n->arg = NULL;
+                                       n->collnames = $2;
+                                       n->collOid = InvalidOid;
+                                       n->location = @1;
+                                       $$ = (Node *) n;
+                               }
                ;
 
 /* DEFAULT NULL is already the default for Postgres.
@@ -2973,8 +3029,12 @@ CreateAsElement:
                                        n->inhcount = 0;
                                        n->is_local = true;
                                        n->is_not_null = false;
+                                       n->is_from_type = false;
+                                       n->storage = 0;
                                        n->raw_default = NULL;
                                        n->cooked_default = NULL;
+                                       n->collClause = NULL;
+                                       n->collOid = InvalidOid;
                                        n->constraints = NIL;
                                        $$ = (Node *)n;
                                }
@@ -6577,7 +6637,7 @@ opt_column: COLUMN                                                                        { $$ = COLUMN; }
                        | /*EMPTY*/                                                             { $$ = 0; }
                ;
 
-opt_set_data: SET DATA_P                                                                       { $$ = 1; }
+opt_set_data: SET DATA_P                                                       { $$ = 1; }
                        | /*EMPTY*/                                                             { $$ = 0; }
                ;
 
@@ -7443,7 +7503,8 @@ CreateDomainStmt:
                                        CreateDomainStmt *n = makeNode(CreateDomainStmt);
                                        n->domainname = $3;
                                        n->typeName = $5;
-                                       n->constraints = $6;
+                                       SplitColQualList($6, &n->constraints, &n->collClause,
+                                                                        yyscanner);
                                        $$ = (Node *)n;
                                }
                ;
@@ -9084,13 +9145,21 @@ TableFuncElementList:
                                }
                ;
 
-TableFuncElement:      ColId Typename
+TableFuncElement:      ColId Typename opt_collate_clause
                                {
                                        ColumnDef *n = makeNode(ColumnDef);
                                        n->colname = $1;
                                        n->typeName = $2;
-                                       n->constraints = NIL;
+                                       n->inhcount = 0;
                                        n->is_local = true;
+                                       n->is_not_null = false;
+                                       n->is_from_type = false;
+                                       n->storage = 0;
+                                       n->raw_default = NULL;
+                                       n->cooked_default = NULL;
+                                       n->collClause = (CollateClause *) $3;
+                                       n->collOid = InvalidOid;
+                                       n->constraints = NIL;
                                        $$ = (Node *)n;
                                }
                ;
@@ -9151,13 +9220,6 @@ opt_array_bounds:
                ;
 
 SimpleTypename:
-                       SimpleTypenameWithoutCollation opt_collate
-                       {
-                               $$ = $1;
-                               $$->collnames = $2;
-                       }
-
-SimpleTypenameWithoutCollation:
                        GenericType                                                             { $$ = $1; }
                        | Numeric                                                               { $$ = $1; }
                        | Bit                                                                   { $$ = $1; }
@@ -9625,6 +9687,14 @@ interval_second:
 a_expr:                c_expr                                                                  { $$ = $1; }
                        | a_expr TYPECAST Typename
                                        { $$ = makeTypeCast($1, $3, @2); }
+                       | a_expr COLLATE any_name
+                               {
+                                       CollateClause *n = makeNode(CollateClause);
+                                       n->arg = (Expr *) $1;
+                                       n->collnames = $3;
+                                       n->location = @2;
+                                       $$ = (Node *) n;
+                               }
                        | a_expr AT TIME ZONE a_expr
                                {
                                        FuncCall *n = makeNode(FuncCall);
@@ -10193,14 +10263,6 @@ c_expr:                columnref                                                               { $$ = $1; }
                                        r->location = @1;
                                        $$ = (Node *)r;
                                }
-                       | c_expr COLLATE any_name
-                               {
-                                       CollateClause *n = makeNode(CollateClause);
-                                       n->arg = (Expr *) $1;
-                                       n->collnames = $3;
-                                       n->location = @2;
-                                       $$ = (Node *)n;
-                               }
                ;
 
 /*
@@ -12678,15 +12740,6 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
        return (Node *) x;
 }
 
-/* parser_init()
- * Initialize to parse one query string
- */
-void
-parser_init(base_yy_extra_type *yyext)
-{
-       yyext->parsetree = NIL;         /* in case grammar forgets to set it */
-}
-
 /*
  * Merge the input and output parameters of a table function.
  */
@@ -12774,6 +12827,57 @@ makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
        return r;
 }
 
+/* Separate Constraint nodes from COLLATE clauses in a ColQualList */
+static void
+SplitColQualList(List *qualList,
+                                List **constraintList, CollateClause **collClause,
+                                core_yyscan_t yyscanner)
+{
+       ListCell   *cell;
+       ListCell   *prev;
+       ListCell   *next;
+
+       *collClause = NULL;
+       prev = NULL;
+       for (cell = list_head(qualList); cell; cell = next)
+       {
+               Node   *n = (Node *) lfirst(cell);
+
+               next = lnext(cell);
+               if (IsA(n, Constraint))
+               {
+                       /* keep it in list */
+                       prev = cell;
+                       continue;
+               }
+               if (IsA(n, CollateClause))
+               {
+                       CollateClause *c = (CollateClause *) n;
+
+                       if (*collClause)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("multiple COLLATE clauses not allowed"),
+                                                parser_errposition(c->location)));
+                       *collClause = c;
+               }
+               else
+                       elog(ERROR, "unexpected node type %d", (int) n->type);
+               /* remove non-Constraint nodes from qualList */
+               qualList = list_delete_cell(qualList, cell, prev);
+       }
+       *constraintList = qualList;
+}
+
+/* parser_init()
+ * Initialize to parse one query string
+ */
+void
+parser_init(base_yy_extra_type *yyext)
+{
+       yyext->parsetree = NIL;         /* in case grammar forgets to set it */
+}
+
 /*
  * Must undefine this stuff before including scan.c, since it has different
  * definitions for these macros.
index ae5653259280b140aef1cc03bdbf107d6d91457c..7a4f8cc2497aaf9f669dc3b1d65016a383f425da 100644 (file)
@@ -147,12 +147,6 @@ transformExpr(ParseState *pstate, Node *expr)
                        {
                                TypeCast   *tc = (TypeCast *) expr;
 
-                               if (tc->typeName->collnames)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                                        errmsg("COLLATE clause not allowed in cast target"),
-                                                        parser_errposition(pstate, tc->typeName->location)));
-
                                /*
                                 * If the subject of the typecast is an ARRAY[] construct and
                                 * the target type is an array type, we invoke
@@ -2116,13 +2110,16 @@ transformCollateClause(ParseState *pstate, CollateClause *c)
        newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg);
 
        argtype = exprType((Node *) newc->arg);
-       /* The unknown type is not collatable, but coerce_type() takes
-        * care of it separately, so we'll let it go here. */
+       /*
+        * The unknown type is not collatable, but coerce_type() takes
+        * care of it separately, so we'll let it go here.
+        */
        if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
                ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
                                 errmsg("collations are not supported by type %s",
-                                               format_type_be(argtype))));
+                                               format_type_be(argtype)),
+                                parser_errposition(pstate, c->location)));
 
        newc->collOid = LookupCollation(pstate, c->collnames, c->location);
        newc->collnames = c->collnames;
index 0af9cbd92b3dc1364cdefd656cd4e13937ff4b40..a2d6c598104cac0a8e1579984b5abe95a156f2db 100644 (file)
@@ -1313,7 +1313,7 @@ FuncNameAsType(List *funcname)
        Oid                     result;
        Type            typtup;
 
-       typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
+       typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
        if (typtup == NULL)
                return InvalidOid;
 
@@ -1500,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename)
        Oid                     result;
        Type            typtup;
 
-       typtup = LookupTypeName(NULL, typename, NULL, NULL);
+       typtup = LookupTypeName(NULL, typename, NULL);
        if (typtup == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
index c7000b991534d26f9c9f124059ae3b8243f53c89..488b1425a35db1d002fa0f87a7a0be4988a0bd17 100644 (file)
@@ -1169,7 +1169,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
                                                 errmsg("column \"%s\" cannot be declared SETOF",
                                                                attrname),
                                                 parser_errposition(pstate, n->typeName->location)));
-                       typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation);
+                       typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod);
+                       attrcollation = GetColumnDefCollation(pstate, n, attrtype);
                        eref->colnames = lappend(eref->colnames, makeString(attrname));
                        rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
                        rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
index 20cb47e712f831db014dc08d17427fe0278cb96b..2ba9bf5181642d3f4896c25e14f58ab72800dc08 100644 (file)
@@ -29,8 +29,6 @@
 
 static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
                                Type typ);
-static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
-                               Type typ);
 
 
 /*
@@ -38,8 +36,7 @@ static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
  *             Given a TypeName object, lookup the pg_type syscache entry of the type.
  *             Returns NULL if no such type can be found.      If the type is found,
  *             the typmod value represented in the TypeName struct is computed and
- *             stored into *typmod_p, and the collation is looked up and stored into
- *      *colloid_p.
+ *             stored into *typmod_p.
  *
  * NB: on success, the caller must ReleaseSysCache the type tuple when done
  * with it.
@@ -54,18 +51,15 @@ static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
  * found but is a shell, and there is typmod decoration, an error will be
  * thrown --- this is intentional.
  *
- * colloid_p can also be null.
- *
  * pstate is only used for error location info, and may be NULL.
  */
 Type
 LookupTypeName(ParseState *pstate, const TypeName *typeName,
-                          int32 *typmod_p, Oid *collid_p)
+                          int32 *typmod_p)
 {
        Oid                     typoid;
        HeapTuple       tup;
        int32           typmod;
-       Oid                     collid;
 
        if (typeName->names == NIL)
        {
@@ -180,28 +174,22 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
        if (typmod_p)
                *typmod_p = typmod;
 
-       collid = typenameCollation(pstate, typeName, (Type) tup);
-
-       if (collid_p)
-               *collid_p = collid;
-
        return (Type) tup;
 }
 
 /*
- * typenameType - given a TypeName, return a Type structure, typmod, and
- * collation
+ * typenameType - given a TypeName, return a Type structure and typmod
  *
  * This is equivalent to LookupTypeName, except that this will report
  * a suitable error message if the type cannot be found or is not defined.
  * Callers of this can therefore assume the result is a fully valid type.
  */
 Type
-typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p)
+typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
 {
        Type            tup;
 
-       tup = LookupTypeName(pstate, typeName, typmod_p, collid_p);
+       tup = LookupTypeName(pstate, typeName, typmod_p);
        if (tup == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -229,7 +217,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName)
        Oid                     typoid;
        Type            tup;
 
-       tup = typenameType(pstate, typeName, NULL, NULL);
+       tup = typenameType(pstate, typeName, NULL);
        typoid = HeapTupleGetOid(tup);
        ReleaseSysCache(tup);
 
@@ -248,25 +236,7 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
 {
        Type            tup;
 
-       tup = typenameType(pstate, typeName, typmod_p, NULL);
-       *typeid_p = HeapTupleGetOid(tup);
-       ReleaseSysCache(tup);
-}
-
-/*
- * typenameTypeIdModColl - given a TypeName, return the type's OID,
- * typmod, and collation
- *
- * This is equivalent to typenameType, but we only hand back the type OID,
- * typmod, and collation, not the syscache entry.
- */
-void
-typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
-                                         Oid *typeid_p, int32 *typmod_p, Oid *collid_p)
-{
-       Type            tup;
-
-       tup = typenameType(pstate, typeName, typmod_p, collid_p);
+       tup = typenameType(pstate, typeName, typmod_p);
        *typeid_p = HeapTupleGetOid(tup);
        ReleaseSysCache(tup);
 }
@@ -380,62 +350,6 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
        return result;
 }
 
-/*
- * typenameCollation - given a TypeName, return the collation OID
- *
- * This will throw an error if the TypeName includes a collation but
- * the data type does not support collations.
- *
- * The actual type OID represented by the TypeName must already have been
- * looked up, and is passed as "typ".
- *
- * pstate is only used for error location info, and may be NULL.
- */
-static Oid
-typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ)
-{
-       Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation;
-
-       /* return prespecified collation OID if no collation name specified */
-       if (typeName->collnames == NIL)
-       {
-               if (typeName->collOid == InvalidOid)
-                       return typcollation;
-               else
-                       return typeName->collOid;
-       }
-
-       if (!OidIsValid(typcollation))
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("collations are not supported by type %s",
-                                               format_type_be(HeapTupleGetOid(typ))),
-                                parser_errposition(pstate, typeName->location)));
-
-       return LookupCollation(pstate, typeName->collnames, typeName->location);
-}
-
-/*
- * LookupCollation
- *
- * Look up collation by name, return OID, with support for error
- * location.
- */
-Oid
-LookupCollation(ParseState *pstate, List *collnames, int location)
-{
-       Oid                     colloid;
-       ParseCallbackState pcbstate;
-
-       setup_parser_errposition_callback(&pcbstate, pstate, location);
-
-       colloid = get_collation_oid(collnames, false);
-
-       cancel_parser_errposition_callback(&pcbstate);
-
-       return colloid;
-}
-
 /*
  * appendTypeNameToBuffer
  *             Append a string representing the name of a TypeName to a StringInfo.
@@ -516,6 +430,72 @@ TypeNameListToString(List *typenames)
        return string.data;
 }
 
+/*
+ * LookupCollation
+ *
+ * Look up collation by name, return OID, with support for error location.
+ */
+Oid
+LookupCollation(ParseState *pstate, List *collnames, int location)
+{
+       Oid                     colloid;
+       ParseCallbackState pcbstate;
+
+       if (pstate)
+               setup_parser_errposition_callback(&pcbstate, pstate, location);
+
+       colloid = get_collation_oid(collnames, false);
+
+       if (pstate)
+               cancel_parser_errposition_callback(&pcbstate);
+
+       return colloid;
+}
+
+/*
+ * GetColumnDefCollation
+ *
+ * Get the collation to be used for a column being defined, given the
+ * ColumnDef node and the previously-determined column type OID.
+ *
+ * pstate is only used for error location purposes, and can be NULL.
+ */
+Oid
+GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid)
+{
+       Oid                     result;
+       Oid                     typcollation = get_typcollation(typeOid);
+       int                     location = -1;
+
+       if (coldef->collClause)
+       {
+               /* We have a raw COLLATE clause, so look up the collation */
+               location = coldef->collClause->location;
+               result = LookupCollation(pstate, coldef->collClause->collnames,
+                                                                location);
+       }
+       else if (OidIsValid(coldef->collOid))
+       {
+               /* Precooked collation spec, use that */
+               result = coldef->collOid;
+       }
+       else
+       {
+               /* Use the type's default collation if any */
+               result = typcollation;
+       }
+
+       /* Complain if COLLATE is applied to an uncollatable type */
+       if (OidIsValid(result) && !OidIsValid(typcollation))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("collations are not supported by type %s",
+                                               format_type_be(typeOid)),
+                                parser_errposition(pstate, location)));
+
+       return result;
+}
+
 /* return a Type structure, given a type id */
 /* NB: caller must ReleaseSysCache the type tuple when done with it */
 Type
index 61ce840a5e59395cbfe1e72d4b04235cd6f230e5..e876853af02c9a37b74e4031ea46a753898b4876 100644 (file)
@@ -627,13 +627,16 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
                def = makeNode(ColumnDef);
                def->colname = pstrdup(attributeName);
                def->typeName = makeTypeNameFromOid(attribute->atttypid,
-                                                                                       attribute->atttypmod,
-                                                                                       attribute->attcollation);
+                                                                                       attribute->atttypmod);
                def->inhcount = 0;
                def->is_local = true;
                def->is_not_null = attribute->attnotnull;
+               def->is_from_type = false;
+               def->storage = 0;
                def->raw_default = NULL;
                def->cooked_default = NULL;
+               def->collClause = NULL;
+               def->collOid = attribute->attcollation;
                def->constraints = NIL;
 
                /*
@@ -822,7 +825,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
        AssertArg(ofTypename);
 
-       tuple = typenameType(NULL, ofTypename, NULL, NULL);
+       tuple = typenameType(NULL, ofTypename, NULL);
        typ = (Form_pg_type) GETSTRUCT(tuple);
        ofTypeId = HeapTupleGetOid(tuple);
        ofTypename->typeOid = ofTypeId;         /* cached for later */
@@ -837,16 +840,24 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
        for (i = 0; i < tupdesc->natts; i++)
        {
                Form_pg_attribute attr = tupdesc->attrs[i];
-               ColumnDef  *n = makeNode(ColumnDef);
+               ColumnDef  *n;
 
                if (attr->attisdropped)
                        continue;
 
+               n = makeNode(ColumnDef);
                n->colname = pstrdup(NameStr(attr->attname));
-               n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation);
-               n->constraints = NULL;
+               n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
+               n->inhcount = 0;
                n->is_local = true;
+               n->is_not_null = false;
                n->is_from_type = true;
+               n->storage = 0;
+               n->raw_default = NULL;
+               n->cooked_default = NULL;
+               n->collClause = NULL;
+               n->collOid = attr->attcollation;
+               n->constraints = NIL;
                cxt->columns = lappend(cxt->columns, n);
        }
        DecrTupleDescRefCount(tupdesc);
@@ -2445,9 +2456,28 @@ static void
 transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
 {
        /*
-        * All we really need to do here is verify that the type is valid.
+        * All we really need to do here is verify that the type is valid,
+        * including any collation spec that might be present.
         */
-       Type            ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL);
+       Type            ctype = typenameType(cxt->pstate, column->typeName, NULL);
+
+       if (column->collClause)
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype);
+               Oid             collOid;
+
+               collOid = LookupCollation(cxt->pstate,
+                                                                 column->collClause->collnames,
+                                                                 column->collClause->location);
+               /* Complain if COLLATE is applied to an uncollatable type */
+               if (!OidIsValid(typtup->typcollation))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("collations are not supported by type %s",
+                                                       format_type_be(HeapTupleGetOid(ctype))),
+                                        parser_errposition(cxt->pstate,
+                                                                               column->collClause->location)));
+       }
 
        ReleaseSysCache(ctype);
 }
index 025edf0838657d848524d65ff0b285fca42b2a6a..7cbd0222cb2e77b59ec3264c8ffabb68addeb1a5 100644 (file)
@@ -5082,8 +5082,9 @@ get_rule_expr(Node *node, deparse_context *context,
 
                                if (!PRETTY_PAREN(context))
                                        appendStringInfoChar(buf, '(');
-                               get_rule_expr_paren(arg, context, false, node);
-                               appendStringInfo(buf, " COLLATE %s", generate_collation_name(collate->collOid));
+                               get_rule_expr_paren(arg, context, showimplicit, node);
+                               appendStringInfo(buf, " COLLATE %s",
+                                                                generate_collation_name(collate->collOid));
                                if (!PRETTY_PAREN(context))
                                        appendStringInfoChar(buf, ')');
                        }
index 8b7db798b9cd0d087d4960c9557891f5033990c7..6691b0dc77eeb3567f66119af04fc46fd13ffb4f 100644 (file)
@@ -68,7 +68,7 @@ extern RangeVar *makeRangeVar(char *schemaname, char *relname, int location);
 
 extern TypeName *makeTypeName(char *typnam);
 extern TypeName *makeTypeNameFromNameList(List *names);
-extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid);
+extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod);
 
 extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
                         List *args, Oid collid, CoercionForm fformat);
index 287e9f523ff4ba6a520b9df47d23090defe086de..9d4515cb27f39f5cae6165eb7385982a67aeb397 100644 (file)
@@ -168,8 +168,7 @@ typedef struct Query
  * specify the type by OID than by name.  If "names" is NIL then the
  * actual type OID is given by typeOid, otherwise typeOid is unused.
  * Similarly, if "typmods" is NIL then the actual typmod is expected to
- * be prespecified in typemod, otherwise typemod is unused.  Similarly
- * for collnames/collOid.
+ * be prespecified in typemod, otherwise typemod is unused.
  *
  * If pct_type is TRUE, then names is actually a field name and we look up
  * the type of that field.     Otherwise (the normal case), names is a type
@@ -185,8 +184,6 @@ typedef struct TypeName
        List       *typmods;            /* type modifier expression(s) */
        int32           typemod;                /* prespecified type modifier */
        List       *arrayBounds;        /* array bounds */
-       List       *collnames;          /* collation name */
-       Oid                     collOid;                /* collation by OID */
        int                     location;               /* token location, or -1 if unknown */
 } TypeName;
 
@@ -468,6 +465,10 @@ typedef struct RangeFunction
  * how this ColumnDef node was created (by parsing, or by inheritance
  * from an existing relation). We should never have both in the same node!
  *
+ * Similarly, we may have a COLLATE specification in either raw form
+ * (represented as a CollateClause with arg==NULL) or cooked form
+ * (the collation's OID).
+ *
  * The constraints list may contain a CONSTR_DEFAULT item in a raw
  * parsetree produced by gram.y, but transformCreateStmt will remove
  * the item and set raw_default instead.  CONSTR_DEFAULT items
@@ -485,6 +486,8 @@ typedef struct ColumnDef
        char            storage;                /* attstorage setting, or 0 for default */
        Node       *raw_default;        /* default value (untransformed parse tree) */
        Node       *cooked_default; /* default value (transformed expr tree) */
+       CollateClause *collClause;      /* untransformed COLLATE spec, if any */
+       Oid                     collOid;                /* collation OID (InvalidOid if not set) */
        List       *constraints;        /* other constraints on column */
 } ColumnDef;
 
@@ -1202,9 +1205,8 @@ typedef struct AlterTableCmd      /* one subcommand of an ALTER TABLE */
        AlterTableType subtype;         /* Type of table alteration to apply */
        char       *name;                       /* column, constraint, or trigger to act on,
                                                                 * or new owner or tablespace */
-       Node       *def;                        /* definition of new column, column type,
-                                                                * index, constraint, or parent table */
-       Node       *transform;          /* transformation expr for ALTER TYPE */
+       Node       *def;                        /* definition of new column, index,
+                                                                * constraint, or parent table */
        DropBehavior behavior;          /* RESTRICT or CASCADE for DROP cases */
        bool            missing_ok;             /* skip error if missing? */
        bool            validated;
@@ -1819,6 +1821,7 @@ typedef struct CreateDomainStmt
        NodeTag         type;
        List       *domainname;         /* qualified name (list of Value strings) */
        TypeName   *typeName;           /* the base type */
+       CollateClause *collClause;      /* untransformed COLLATE spec, if any */
        List       *constraints;        /* constraints (list of Constraint nodes) */
 } CreateDomainStmt;
 
index 8621ab678d0d7ef46102efc28953c592a74914c9..92c9ecba4acc7303a3a3c0ba4bf1f402d26dbb81 100644 (file)
 typedef HeapTuple Type;
 
 extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName,
-                                                  int32 *typmod_p, Oid *collid_p);
+                          int32 *typmod_p);
 extern Type typenameType(ParseState *pstate, const TypeName *typeName,
-                                                int32 *typmod_p, Oid *collid_p);
-
-extern Oid LookupCollation(ParseState *pstate, List *collnames, int location);
-
+                        int32 *typmod_p);
 extern Oid typenameTypeId(ParseState *pstate, const TypeName *typeName);
 extern void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
-                                                                 Oid *typeid_p, int32 *typmod_p);
-extern void typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
-                                                                 Oid *typeid_p, int32 *typmod_p, Oid *collid_p);
+                                                                Oid *typeid_p, int32 *typmod_p);
 
 extern char *TypeNameToString(const TypeName *typeName);
 extern char *TypeNameListToString(List *typenames);
 
+extern Oid LookupCollation(ParseState *pstate, List *collnames, int location);
+extern Oid GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid);
+
 extern Type typeidType(Oid id);
 
 extern Oid     typeTypeId(Type tp);
index d2b3862ec722a6c30ad24096af79f04ba7f316a7..41188a2369fd3632adbb7496c02d80e9ef964075 100644 (file)
@@ -1565,7 +1565,7 @@ plpgsql_parse_wordtype(char *ident)
         * Word wasn't found in the namespace stack. Try to find a data type with
         * that name, but ignore shell types and complex types.
         */
-       typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, NULL);
+       typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
        if (typeTup)
        {
                Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
index caa65b2f37f881cdfa817f6d8c9008cd56c1ab79..5ad5de2f00b36f8f65b68fadb8f19b12b3add2b9 100644 (file)
@@ -20,21 +20,21 @@ CREATE TABLE collate_test_fail (
 );
 ERROR:  collation "ja_JP.eucjp" for current database encoding "UTF8" does not exist
 LINE 3:     b text COLLATE "ja_JP.eucjp"
-              ^
+                   ^
 CREATE TABLE collate_test_fail (
     a int,
     b text COLLATE "foo"
 );
 ERROR:  collation "foo" for current database encoding "UTF8" does not exist
 LINE 3:     b text COLLATE "foo"
-              ^
+                   ^
 CREATE TABLE collate_test_fail (
     a int COLLATE "en_US.utf8",
     b text
 );
 ERROR:  collations are not supported by type integer
 LINE 2:     a int COLLATE "en_US.utf8",
-              ^
+                  ^
 CREATE TABLE collate_test_like (
     LIKE collate_test1
 );
@@ -632,9 +632,9 @@ ERROR:  no collation was derived for column "b" with collatable type text
 HINT:  Use the COLLATE clause to set the collation explicitly.
 -- casting
 SELECT CAST('42' AS text COLLATE "C");
-ERROR:  COLLATE clause not allowed in cast target
+ERROR:  syntax error at or near "COLLATE"
 LINE 1: SELECT CAST('42' AS text COLLATE "C");
-                            ^
+                                 ^
 SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2;
  a |  b  
 ---+-----
@@ -727,6 +727,8 @@ CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail
 ERROR:  collations are not supported by type integer
 CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: ...ATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C...
+                                                             ^
 SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%';
       relname       |                                       pg_get_indexdef                                        
 --------------------+----------------------------------------------------------------------------------------------
index 00730f25cb83847e71878e9d517766bde1fc537b..a7473349fdf0cad58f0a3ff8b015fee73eaf2550 100644 (file)
@@ -693,7 +693,7 @@ ERROR:  "ft1" is not a table or view
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
-ERROR:  ALTER TYPE USING is not supported on foreign tables
+ERROR:  ALTER TYPE USING is only supported on plain tables
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
 -- can't change the column type if it's used elsewhere