Commit a051ef69 authored by Tom Lane's avatar Tom Lane

Remove collation information from TypeName, where it does not belong.

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