aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/tupdesc.c3
-rw-r--r--src/backend/commands/collationcmds.c2
-rw-r--r--src/backend/commands/functioncmds.c4
-rw-r--r--src/backend/commands/sequence.c23
-rw-r--r--src/backend/commands/tablecmds.c65
-rw-r--r--src/backend/commands/typecmds.c27
-rw-r--r--src/backend/commands/view.c13
-rw-r--r--src/backend/nodes/copyfuncs.c7
-rw-r--r--src/backend/nodes/equalfuncs.c7
-rw-r--r--src/backend/nodes/makefuncs.c5
-rw-r--r--src/backend/nodes/outfuncs.c5
-rw-r--r--src/backend/parser/gram.y182
-rw-r--r--src/backend/parser/parse_expr.c17
-rw-r--r--src/backend/parser/parse_func.c4
-rw-r--r--src/backend/parser/parse_relation.c3
-rw-r--r--src/backend/parser/parse_type.c166
-rw-r--r--src/backend/parser/parse_utilcmd.c46
-rw-r--r--src/backend/utils/adt/ruleutils.c5
-rw-r--r--src/include/nodes/makefuncs.h2
-rw-r--r--src/include/nodes/parsenodes.h17
-rw-r--r--src/include/parser/parse_type.h14
-rw-r--r--src/pl/plpgsql/src/pl_comp.c2
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out12
-rw-r--r--src/test/regress/expected/foreign_data.out2
24 files changed, 397 insertions, 236 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 0cd1f941e3f..d78b08381e0 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -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)
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 18e88d2653f..a52cb351ac8 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -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);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 3f25b3bf02a..a8ef947240e 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -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)
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index e71c311faf7..5c6212c64cd 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -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;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3be9a6f3481..f1264bfb66d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index be1f1d791fd..3513256b9a5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -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),
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 5576ea259f4..794a56e84de 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 86a16783f71..b948af604d8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c234416cf53..c8ee4744364 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 0225f19382a..d9f16452383 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -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;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 4aae2b33a6d..06fd7ff818e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5b96b5b0df5..373d2adc71c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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.
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index ae565325928..7a4f8cc2497 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 0af9cbd92b3..a2d6c598104 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -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),
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index c7000b99153..488b1425a35 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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);
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 20cb47e712f..2ba9bf51816 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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);
}
@@ -381,62 +351,6 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
}
/*
- * 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.
* This is the shared guts of TypeNameToString and TypeNameListToString.
@@ -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
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 61ce840a5e5..e876853af02 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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);
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 025edf08386..7cbd0222cb2 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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, ')');
}
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 8b7db798b9c..6691b0dc77e 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -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);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 287e9f523ff..9d4515cb27f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 8621ab678d0..92c9ecba4ac 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -20,21 +20,19 @@
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);
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d2b3862ec72..41188a2369f 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -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);
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index caa65b2f37f..5ad5de2f00b 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -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
--------------------+----------------------------------------------------------------------------------------------
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 00730f25cb8..a7473349fdf 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -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