diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/common/tupdesc.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/dependency.c | 4 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 10 | ||||
-rw-r--r-- | src/backend/catalog/index.c | 14 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 5 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 6 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 38 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 10 | ||||
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 57 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 11 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 10 | ||||
-rw-r--r-- | src/backend/parser/parse_cte.c | 18 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 8 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 13 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 9 | ||||
-rw-r--r-- | src/backend/utils/cache/syscache.c | 4 |
17 files changed, 144 insertions, 87 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index c06a0271ca5..16979c4ea72 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -360,6 +360,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (attr1->attinhcount != attr2->attinhcount) return false; + if (attr1->attcollation != attr2->attcollation) + return false; /* attacl and attoptions are not even present... */ } @@ -611,7 +613,9 @@ BuildDescForRelation(List *schema) * BuildDescFromLists * * Build a TupleDesc given lists of column names (as String nodes), - * column type OIDs, and column typmods. No constraints are generated. + * column type OIDs, typmods, and collation OIDs. + * + * No constraints are generated. * * This is essentially a cut-down version of BuildDescForRelation for use * with functions returning RECORD. diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index b3ed946530e..c459c1e2213 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -2227,14 +2227,16 @@ getObjectDescription(const ObjectAddress *object) case OCLASS_COLLATION: { HeapTuple collTup; + Form_pg_collation coll; collTup = SearchSysCache1(COLLOID, ObjectIdGetDatum(object->objectId)); if (!HeapTupleIsValid(collTup)) elog(ERROR, "cache lookup failed for collation %u", object->objectId); + coll = (Form_pg_collation) GETSTRUCT(collTup); appendStringInfo(&buffer, _("collation %s"), - NameStr(((Form_pg_collation) GETSTRUCT(collTup))->collname)); + NameStr(coll->collname)); ReleaseSysCache(collTup); break; } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 8c5670fdb8c..28d5c549a32 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -644,7 +644,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* * First we add the user attributes. This is also a convenient place to - * add dependencies on their datatypes. + * add dependencies on their datatypes and collations. */ for (i = 0; i < natts; i++) { @@ -666,7 +666,9 @@ AddNewAttributeTuples(Oid new_rel_oid, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - if (OidIsValid(attr->attcollation)) + /* The default collation is pinned, so don't bother recording it */ + if (OidIsValid(attr->attcollation) && + attr->attcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = attr->attcollation; @@ -921,7 +923,7 @@ AddNewRelationType(const char *typeName, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid); /* rowtypes never have a collation */ } /* -------------------------------- @@ -1183,7 +1185,7 @@ heap_create_with_catalog(const char *relname, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid); /* rowtypes never have a collation */ pfree(relarrayname); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index c79402c72a9..bc630a6f3ac 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -351,7 +351,6 @@ ConstructTupleDescriptor(Relation heapRelation, to->atthasdef = false; to->attislocal = true; to->attinhcount = 0; - to->attcollation = collationObjectId[i]; } else @@ -388,7 +387,6 @@ ConstructTupleDescriptor(Relation heapRelation, to->attcacheoff = -1; to->atttypmod = -1; to->attislocal = true; - to->attcollation = collationObjectId[i]; ReleaseSysCache(tuple); @@ -653,6 +651,7 @@ UpdateIndexRelation(Oid indexoid, * indexColNames: column names to use for index (List of char *) * accessMethodObjectId: OID of index AM to use * tableSpaceId: OID of tablespace to use + * collationObjectId: array of collation OIDs, one per index column * classObjectId: array of index opclass OIDs, one per index column * coloptions: array of per-index-column indoption settings * reloptions: AM-specific options @@ -871,7 +870,8 @@ index_create(Relation heapRelation, * ---------------- */ UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo, - collationObjectId, classObjectId, coloptions, isprimary, is_exclusion, + collationObjectId, classObjectId, coloptions, + isprimary, is_exclusion, !deferrable, !concurrent); @@ -965,9 +965,11 @@ index_create(Relation heapRelation, } /* Store dependency on collations */ + /* The default collation is pinned, so don't bother recording it */ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { - if (OidIsValid(collationObjectId[i])) + if (OidIsValid(collationObjectId[i]) && + collationObjectId[i] != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = collationObjectId[i]; @@ -2445,8 +2447,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples; ivinfo.strategy = NULL; - state.tuplesort = tuplesort_begin_datum(TIDOID, - TIDLessOperator, InvalidOid, false, + state.tuplesort = tuplesort_begin_datum(TIDOID, TIDLessOperator, + InvalidOid, false, maintenance_work_mem, false); state.htups = state.itups = state.tups_inserted = 0; diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 9e35e73f9cf..b6912578786 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -643,8 +643,9 @@ GenerateTypeDependencies(Oid typeNamespace, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - /* Normal dependency from a domain to its base type's collation. */ - if (OidIsValid(typeCollation)) + /* Normal dependency from a domain to its collation. */ + /* We know the default collation is pinned, so don't bother recording it */ + if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = typeCollation; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 53a6aafbbfb..ff84045d4fc 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -350,7 +350,8 @@ DefineIndex(RangeVar *heapRelation, collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16)); - ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, coloptions, attributeList, + ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, + coloptions, attributeList, exclusionOpNames, relationId, accessMethodName, accessMethodId, amcanorder, isconstraint); @@ -395,7 +396,8 @@ DefineIndex(RangeVar *heapRelation, indexRelationId = index_create(rel, indexRelationName, indexRelationId, indexInfo, indexColNames, - accessMethodId, tablespaceId, collationObjectId, classObjectId, + accessMethodId, tablespaceId, + collationObjectId, classObjectId, coloptions, reloptions, primary, isconstraint, deferrable, initdeferred, allowSystemTableMods, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index bcf660b6de0..7660114ec2c 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -295,7 +295,8 @@ static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recu static void ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, ColumnDef *colDef, bool isOid, bool recurse, bool recursing, LOCKMODE lockmode); -static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid); +static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); +static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid); static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode); static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode); @@ -4423,7 +4424,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* * Add needed dependency entries for the new column. */ - add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid, attribute.attcollation); + add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid); + add_column_collation_dependency(myrelid, newattnum, attribute.attcollation); /* * Propagate to children as appropriate. Unlike most other ALTER @@ -4474,7 +4476,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, * Install a column's dependency on its datatype. */ static void -add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid) +add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) { ObjectAddress myself, referenced; @@ -4486,9 +4488,23 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid) referenced.objectId = typid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); +} + +/* + * Install a column's dependency on its collation. + */ +static void +add_column_collation_dependency(Oid relid, int32 attnum, Oid collid) +{ + ObjectAddress myself, + referenced; - if (collid) + /* We know the default collation is pinned, so don't bother recording it */ + if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID) { + myself.classId = RelationRelationId; + myself.objectId = relid; + myself.objectSubId = attnum; referenced.classId = CollationRelationId; referenced.objectId = collid; referenced.objectSubId = 0; @@ -6671,7 +6687,8 @@ ATPrepAlterColumnType(List **wqueue, else { transform = (Node *) makeVar(1, attnum, - attTup->atttypid, attTup->atttypmod, attTup->attcollation, + attTup->atttypid, attTup->atttypmod, + attTup->attcollation, 0); } @@ -7052,7 +7069,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, /* * Now scan for dependencies of this column on other things. The only * thing we should find is the dependency on the column datatype, which we - * want to remove, and possibly an associated collation. + * want to remove, and possibly a collation dependency. */ ScanKeyInit(&key[0], Anum_pg_depend_classid, @@ -7091,8 +7108,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, heap_close(depRel, RowExclusiveLock); /* - * Here we go --- change the recorded column type. (Note heapTup is a - * copy of the syscache entry, so okay to scribble on.) + * Here we go --- change the recorded column type and collation. (Note + * heapTup is a copy of the syscache entry, so okay to scribble on.) */ attTup->atttypid = targettype; attTup->atttypmod = targettypmod; @@ -7112,8 +7129,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, heap_close(attrelation, RowExclusiveLock); - /* Install dependency on new datatype */ - add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype, targetcollid); + /* Install dependencies on new datatype and collation */ + add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype); + add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid); /* * Drop any pg_statistic entry for the column, since it's now wrong type diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 1a20b0d91be..7388e5a3db9 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -571,7 +571,7 @@ DefineType(List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); + collation); /* type's collation */ /* * Create the array type that goes with it. @@ -611,7 +611,7 @@ DefineType(List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); + collation); /* type's collation */ pfree(array_type); } @@ -1069,7 +1069,7 @@ DefineDomain(CreateDomainStmt *stmt) basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ typNotNull, /* Type NOT NULL */ - domaincoll); + domaincoll); /* type's collation */ /* * Process constraints which refer to the domain ID returned by TypeCreate @@ -1179,7 +1179,7 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid); /* type's collation */ /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeOid, stmt->vals); @@ -1219,7 +1219,7 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid); /* type's collation */ pfree(enumArrayName); } diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 208c0fb76ac..7d27123cf05 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -170,13 +170,13 @@ typedef enum * the two expressions from the original clause. * * In addition to the expressions themselves, the planner passes the btree - * opfamily OID, btree strategy number (BTLessStrategyNumber or + * opfamily OID, collation OID, btree strategy number (BTLessStrategyNumber or * BTGreaterStrategyNumber), and nulls-first flag that identify the intended * sort ordering for each merge key. The mergejoinable operator is an - * equality operator in this opfamily, and the two inputs are guaranteed to be + * equality operator in the opfamily, and the two inputs are guaranteed to be * ordered in either increasing or decreasing (respectively) order according - * to this opfamily, with nulls at the indicated end of the range. This - * allows us to obtain the needed comparison function from the opfamily. + * to the opfamily and collation, with nulls at the indicated end of the range. + * This allows us to obtain the needed comparison function from the opfamily. */ static MergeJoinClause MJExamineQuals(List *mergeclauses, diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 1a9540ce068..bbff4f26f8d 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -108,7 +108,8 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, List *tidquals); static FunctionScan *make_functionscan(List *qptlist, List *qpqual, Index scanrelid, Node *funcexpr, List *funccolnames, - List *funccoltypes, List *funccoltypmods, List *funccolcollations); + List *funccoltypes, List *funccoltypmods, + List *funccolcollations); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, Index scanrelid, List *values_lists); static CteScan *make_ctescan(List *qptlist, List *qpqual, @@ -143,9 +144,9 @@ static MergeJoin *make_mergejoin(List *tlist, bool *mergenullsfirst, Plan *lefttree, Plan *righttree, JoinType jointype); -static Sort * -make_sort(PlannerInfo *root, Plan *lefttree, int numCols, -AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, +static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, + AttrNumber *sortColIdx, Oid *sortOperators, + Oid *collations, bool *nullsFirst, double limit_tuples); static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, @@ -738,7 +739,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) /* Now, insert a Sort node if subplan isn't sufficiently ordered */ if (!pathkeys_contained_in(pathkeys, subpath->pathkeys)) subplan = (Plan *) make_sort(root, subplan, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst, + sortColIdx, sortOperators, + collations, nullsFirst, best_path->limit_tuples); subplans = lappend(subplans, subplan); @@ -2013,10 +2015,10 @@ create_mergejoin_plan(PlannerInfo *root, } /* - * Compute the opfamily/strategy/nullsfirst arrays needed by the executor. - * The information is in the pathkeys for the two inputs, but we need to - * be careful about the possibility of mergeclauses sharing a pathkey - * (compare find_mergeclauses_for_pathkeys()). + * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the + * executor. The information is in the pathkeys for the two inputs, but + * we need to be careful about the possibility of mergeclauses sharing a + * pathkey (compare find_mergeclauses_for_pathkeys()). */ nClauses = list_length(mergeclauses); Assert(nClauses == list_length(best_path->path_mergeclauses)); @@ -3316,13 +3318,14 @@ make_mergejoin(List *tlist, /* * make_sort --- basic routine to build a Sort plan node * - * Caller must have built the sortColIdx, sortOperators, and nullsFirst - * arrays already. limit_tuples is as for cost_sort (in particular, pass - * -1 if no limit) + * Caller must have built the sortColIdx, sortOperators, collations, and + * nullsFirst arrays already. + * limit_tuples is as for cost_sort (in particular, pass -1 if no limit) */ static Sort * make_sort(PlannerInfo *root, Plan *lefttree, int numCols, -AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, + AttrNumber *sortColIdx, Oid *sortOperators, + Oid *collations, bool *nullsFirst, double limit_tuples) { Sort *node = makeNode(Sort); @@ -3378,6 +3381,11 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, * values that < considers equal. We need not check nulls_first * however because a lower-order column with the same sortop but * opposite nulls direction is redundant. + * + * We could probably consider sort keys with the same sortop and + * different collations to be redundant too, but for the moment + * treat them as not redundant. This will be needed if we ever + * support collations with different notions of equality. */ if (sortColIdx[i] == colIdx && sortOperators[numCols] == sortOp && @@ -3410,8 +3418,9 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, * 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place * * We must convert the pathkey information into arrays of sort key column - * numbers and sort operator OIDs, which is the representation the executor - * wants. These are returned into the output parameters *p_numsortkeys etc. + * numbers, sort operator OIDs, collation OIDs, and nulls-first flags, + * which is the representation the executor wants. These are returned into + * the output parameters *p_numsortkeys etc. * * If the pathkeys include expressions that aren't simple Vars, we will * usually need to add resjunk items to the input plan's targetlist to @@ -3610,7 +3619,8 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, pathkey->pk_eclass->ec_collation, pathkey->pk_nulls_first, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst); + sortColIdx, sortOperators, + collations, nullsFirst); } Assert(numsortkeys > 0); @@ -3655,7 +3665,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, /* Now build the Sort node */ return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst, limit_tuples); + sortColIdx, sortOperators, collations, + nullsFirst, limit_tuples); } /* @@ -3701,13 +3712,15 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) exprCollation((Node *) tle->expr), sortcl->nulls_first, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst); + sortColIdx, sortOperators, + collations, nullsFirst); } Assert(numsortkeys > 0); return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst, -1.0); + sortColIdx, sortOperators, collations, + nullsFirst, -1.0); } /* @@ -3763,14 +3776,16 @@ make_sort_from_groupcols(PlannerInfo *root, exprCollation((Node *) tle->expr), grpcl->nulls_first, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst); + sortColIdx, sortOperators, + collations, nullsFirst); grpno++; } Assert(numsortkeys > 0); return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, collations, nullsFirst, -1.0); + sortColIdx, sortOperators, collations, + nullsFirst, -1.0); } static Material * diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index f2b586d19cc..493bc86299f 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -281,7 +281,7 @@ SS_assign_special_param(PlannerInfo *root) } /* - * Get the datatype of the first column of the plan's output. + * Get the datatype/typmod/collation of the first column of the plan's output. * * This information is stored for ARRAY_SUBLINK execution and for * exprType()/exprTypmod()/exprCollation(), which have no way to get at the @@ -290,7 +290,8 @@ SS_assign_special_param(PlannerInfo *root) * always. */ static void -get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation) +get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, + Oid *colcollation) { /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */ if (plan->targetlist) @@ -478,7 +479,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks, splan->subLinkType = subLinkType; splan->testexpr = NULL; splan->paramIds = NIL; - get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); + get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, + &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = unknownEqFalse; splan->setParam = NIL; @@ -976,7 +978,8 @@ SS_process_ctes(PlannerInfo *root) splan->subLinkType = CTE_SUBLINK; splan->testexpr = NULL; splan->paramIds = NIL; - get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); + get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, + &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = false; splan->setParam = NIL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 4947a7d837e..b2032870477 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1357,9 +1357,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * Generate dummy targetlist for outer query using column names of - * leftmost select and common datatypes of topmost set operation. Also - * make lists of the dummy vars and their names for use in parsing ORDER - * BY. + * leftmost select and common datatypes/collations of topmost set + * operation. Also make lists of the dummy vars and their names for use + * in parsing ORDER BY. * * Note: we use leftmostRTI as the varno of the dummy variables. It * shouldn't matter too much which RT index they have, as long as they @@ -1371,7 +1371,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) targetnames = NIL; left_tlist = list_head(leftmostQuery->targetList); - forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations) + forthree(lct, sostmt->colTypes, + lcm, sostmt->colTypmods, + lcc, sostmt->colCollations) { Oid colType = lfirst_oid(lct); int32 colTypmod = lfirst_int(lcm); diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 41097263b41..ec6afd83b6c 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -286,10 +286,10 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) else { /* - * Verify that the previously determined output column types match - * what the query really produced. We have to check this because the - * recursive term could have overridden the non-recursive term, and we - * don't have any easy way to fix that. + * Verify that the previously determined output column types and + * collations match what the query really produced. We have to check + * this because the recursive term could have overridden the + * non-recursive term, and we don't have any easy way to fix that. */ ListCell *lctlist, *lctyp, @@ -366,11 +366,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) Assert(cte->ctecolnames == NIL); /* - * We need to determine column names and types. The alias column names - * override anything coming from the query itself. (Note: the SQL spec - * says that the alias list must be empty or exactly as long as the output - * column set; but we allow it to be shorter for consistency with Alias - * handling.) + * We need to determine column names, types, and collations. The alias + * column names override anything coming from the query itself. (Note: + * the SQL spec says that the alias list must be empty or exactly as long + * as the output column set; but we allow it to be shorter for consistency + * with Alias handling.) */ cte->ctecolnames = copyObject(cte->aliascolnames); cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 2a94f73a9ab..5359e691dd1 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1174,7 +1174,8 @@ addRangeTableEntryForFunction(ParseState *pstate, eref->colnames = lappend(eref->colnames, makeString(attrname)); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); - rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation); + rte->funccolcollations = lappend_oid(rte->funccolcollations, + attrcollation); } } else @@ -1902,7 +1903,8 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, Var *varnode; varnode = makeVar(rtindex, attr->attnum, - attr->atttypid, attr->atttypmod, attr->attcollation, + attr->atttypid, attr->atttypmod, + attr->attcollation, sublevels_up); varnode->location = location; @@ -2009,7 +2011,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) /* * get_rte_attribute_type - * Get attribute type information from a RangeTblEntry + * Get attribute type/typmod/collation information from a RangeTblEntry */ void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 3f630147b0f..e6f9e36bbca 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -372,7 +372,7 @@ transformAssignedExpr(ParseState *pstate, Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; - Oid attrcollation; + Oid attrcollation; /* collation of target column */ Relation rd = pstate->p_target_relation; Assert(rd != NULL); @@ -388,11 +388,12 @@ transformAssignedExpr(ParseState *pstate, /* * If the expression is a DEFAULT placeholder, insert the attribute's - * type/typmod into it so that exprType will report the right things. (We - * expect that the eventually substituted default expression will in fact - * have this type and typmod.) Also, reject trying to update a subfield - * or array element with DEFAULT, since there can't be any default for - * portions of a column. + * type/typmod/collation into it so that exprType etc will report the + * right things. (We expect that the eventually substituted default + * expression will in fact have this type and typmod. The collation + * likely doesn't matter, but let's set it correctly anyway.) Also, + * reject trying to update a subfield or array element with DEFAULT, since + * there can't be any default for portions of a column. */ if (expr && IsA(expr, SetToDefault)) { diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index e436a1ee59f..3ab90cb7d88 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -235,7 +235,8 @@ static void get_from_clause_item(Node *jtnode, Query *query, deparse_context *context); static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte, deparse_context *context); -static void get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations, +static void get_from_clause_coldeflist(List *names, + List *types, List *typmods, List *collations, deparse_context *context); static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); @@ -6617,7 +6618,8 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte, * responsible for ensuring that an alias or AS is present before it. */ static void -get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations, +get_from_clause_coldeflist(List *names, + List *types, List *typmods, List *collations, deparse_context *context) { StringInfo buf = context->buf; @@ -6651,7 +6653,8 @@ get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collat appendStringInfo(buf, "%s %s", quote_identifier(attname), format_type_with_typemod(atttypid, atttypmod)); - if (attcollation && attcollation != DEFAULT_COLLATION_OID) + if (OidIsValid(attcollation) && + attcollation != get_typcollation(atttypid)) appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation)); i++; diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 2b5e37e2f05..75f510c1643 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -277,7 +277,7 @@ static const struct cachedesc cacheinfo[] = { Anum_pg_collation_collnamespace, 0 }, - 256 + 64 }, {CollationRelationId, /* COLLOID */ CollationOidIndexId, @@ -288,7 +288,7 @@ static const struct cachedesc cacheinfo[] = { 0, 0 }, - 256 + 64 }, {ConversionRelationId, /* CONDEFAULT */ ConversionDefaultIndexId, |