diff options
Diffstat (limited to 'src')
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 |