(<structfield>collname</>, <structfield>collnamespace</>).
<productname>PostgreSQL</productname> generally ignores all
collations that do not have <structfield>collencoding</> equal to
- either the current database's encoding or -1, and creation of new
- entries matching an entry with <structfield>collencoding</> = -1
+ either the current database's encoding or -1, and creation of new entries
+ with the same name as an entry with <structfield>collencoding</> = -1
is forbidden. Therefore it is sufficient to use a qualified SQL name
(<replaceable>schema</>.<replaceable>name</>) to identify a collation,
even though this is not unique according to the catalog definition.
of the type. If the type does not support collations, this will
be zero. A base type that supports collations will have
<symbol>DEFAULT_COLLATION_OID</symbol> here. A domain over a
- collatable type can have some other collation OID, if one was defined
- for the domain.
+ collatable type can have some other collation OID, if one was
+ specified for the domain.
</para></entry>
</row>
<sect1 id="indexes-collations">
- <title>Collations and Indexes</title>
+ <title>Indexes and Collations</title>
<para>
- An index can only support one collation for one column or
- expression. If multiple collations are of interest, multiple
- indexes may be created.
+ An index can support only one collation per index column.
+ If multiple collations are of interest, multiple indexes may be needed.
</para>
<para>
CREATE INDEX test1c_content_index ON test1c (content);
</programlisting>
- The created index automatically follows the collation of the
- underlying column, and so a query of the form
+ The index automatically uses the collation of the
+ underlying column. So a query of the form
<programlisting>
-SELECT * FROM test1c WHERE content = <replaceable>constant</replaceable>;
+SELECT * FROM test1c WHERE content > <replaceable>constant</replaceable>;
</programlisting>
- could use the index.
- </para>
-
- <para>
- If in addition, a query of the form, say,
+ could use the index, because the comparison will by default use the
+ collation of the column. However, this index cannot accelerate queries
+ that involve some other collation. So if queries of the form, say,
<programlisting>
SELECT * FROM test1c WHERE content > <replaceable>constant</replaceable> COLLATE "y";
</programlisting>
- is of interest, an additional index could be created that supports
- the <literal>"y"</literal> collation, like so:
+ are also of interest, an additional index could be created that supports
+ the <literal>"y"</literal> collation, like this:
<programlisting>
-CREATE INDEX test1c_content_index ON test1c (content COLLATE "y");
+CREATE INDEX test1c_content_y_index ON test1c (content COLLATE "y");
</programlisting>
</para>
</sect1>
<listitem>
<para>
The name of an existing collation to copy. The new collation
- will have the same properties as the existing one, but they
- will become independent objects.
+ will have the same properties as the existing one, but it
+ will be an independent object.
</para>
</listitem>
</varlistentry>
<title>Examples</title>
<para>
- To create a collation from the locale <literal>fr_FR.utf8</literal>
+ To create a collation from the operating system locale
+ <literal>fr_FR.utf8</literal>
(assuming the current database encoding is <literal>UTF8</literal>):
<programlisting>
CREATE COLLATION french (LOCALE = 'fr_FR.utf8');
<para>
An optional collation for the domain. If no collation is
specified, the underlying data type's default collation is used.
- The underlying type must be collatable when <literal>COLLATE</>
+ The underlying type must be collatable if <literal>COLLATE</>
is specified.
</para>
</listitem>
The name of the collation to use for the index. By default,
the index uses the collation declared for the column to be
indexed or the result collation of the expression to be
- indexed. Indexes with nondefault collations are
- available for use by queries that involve expressions using
- nondefault collations.
+ indexed. Indexes with non-default collations can be useful for
+ queries that involve expressions using non-default collations.
</para>
</listitem>
</varlistentry>
will choose a name, typically <literal>films_lower_idx</>.)
</para>
+ <para>
+ To create an index with non-default collation:
+<programlisting>
+CREATE INDEX title_idx_german ON films (title COLLATE "de_DE");
+</programlisting>
+ </para>
+
<para>
To create an index with non-default sort ordering of nulls:
<programlisting>
</para>
<para>
- If the optional
+ If the optional boolean
parameter <replaceable class="parameter">collatable</replaceable>
is true, column definitions and expressions of the type may carry
- collation information and allow the use of
+ collation information through use of
the <literal>COLLATE</literal> clause. It is up to the
implementations of the functions operating on the type to actually
make use of the collation information; this does not happen
</sect2>
<sect2>
- <title>Extra tests</title>
+ <title>Extra Tests</title>
<para>
The regression test suite contains a few test files that are not
<screen>
gmake check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8
</screen>
- This test works only on Linux/glibc platforms and when run in a
- UTF-8 locale.
+ The <literal>collate.linux.utf8</> test works only on Linux/glibc
+ platforms, and only when run in a locale that uses UTF-8 encoding.
</para>
</sect2>
</sect1>
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
+ if (attr1->attcollation != attr2->attcollation)
+ return false;
/* attacl and attoptions are not even present... */
}
* 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.
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;
}
/*
* 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++)
{
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;
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* rowtypes never have a collation */
}
/* --------------------------------
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* rowtypes never have a collation */
pfree(relarrayname);
}
to->atthasdef = false;
to->attislocal = true;
to->attinhcount = 0;
-
to->attcollation = collationObjectId[i];
}
else
to->attcacheoff = -1;
to->atttypmod = -1;
to->attislocal = true;
-
to->attcollation = collationObjectId[i];
ReleaseSysCache(tuple);
* 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
* ----------------
*/
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
- collationObjectId, classObjectId, coloptions, isprimary, is_exclusion,
+ collationObjectId, classObjectId, coloptions,
+ isprimary, is_exclusion,
!deferrable,
!concurrent);
}
/* 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];
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;
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;
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);
indexRelationId =
index_create(rel, indexRelationName, indexRelationId,
indexInfo, indexColNames,
- accessMethodId, tablespaceId, collationObjectId, classObjectId,
+ accessMethodId, tablespaceId,
+ collationObjectId, classObjectId,
coloptions, reloptions, primary,
isconstraint, deferrable, initdeferred,
allowSystemTableMods,
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);
/*
* 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
* 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;
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;
else
{
transform = (Node *) makeVar(1, attnum,
- attTup->atttypid, attTup->atttypmod, attTup->attcollation,
+ attTup->atttypid, attTup->atttypmod,
+ attTup->attcollation,
0);
}
/*
* 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,
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;
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
-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.
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation);
+ collation); /* type's collation */
pfree(array_type);
}
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
-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);
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* type's collation */
pfree(enumArrayName);
}
* 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,
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,
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,
/* 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);
}
/*
- * 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));
/*
* 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);
* 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 &&
* '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
pathkey->pk_eclass->ec_collation,
pathkey->pk_nulls_first,
numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst);
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
Assert(numsortkeys > 0);
/* Now build the Sort node */
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst, limit_tuples);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, limit_tuples);
}
/*
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);
}
/*
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 *
}
/*
- * 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
* 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)
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;
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;
/*
* 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
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);
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,
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;
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
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod, attr->attcollation,
+ attr->atttypid, attr->atttypmod,
+ attr->attcollation,
sublevels_up);
varnode->location = location;
/*
* 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,
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);
/*
* 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))
{
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);
* 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;
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++;
Anum_pg_collation_collnamespace,
0
},
- 256
+ 64
},
{CollationRelationId, /* COLLOID */
CollationOidIndexId,
0,
0
},
- 256
+ 64
},
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,