From: Tom Lane Date: Sat, 26 Mar 2011 22:28:40 +0000 (-0400) Subject: Clean up cruft around collation initialization for tupdescs and scankeys. X-Git-Tag: REL9_1_ALPHA5~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7208fae18f1fdb242b4fcced77a3b836e15ac3ec;p=postgresql Clean up cruft around collation initialization for tupdescs and scankeys. I found actual bugs in GiST and plpgsql; the rest of this is cosmetic but meant to decrease the odds of future bugs of omission. --- diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c index 41cd36fce9..b632408da4 100644 --- a/src/backend/access/common/scankey.c +++ b/src/backend/access/common/scankey.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "access/skey.h" +#include "catalog/pg_collation.h" /* @@ -33,6 +34,7 @@ ScanKeyEntryInitialize(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, + Oid collation, RegProcedure procedure, Datum argument) { @@ -42,7 +44,10 @@ ScanKeyEntryInitialize(ScanKey entry, entry->sk_subtype = subtype; entry->sk_argument = argument; if (RegProcedureIsValid(procedure)) + { fmgr_info(procedure, &entry->sk_func); + entry->sk_func.fn_collation = collation; + } else { Assert(flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)); @@ -53,12 +58,16 @@ ScanKeyEntryInitialize(ScanKey entry, /* * ScanKeyInit * Shorthand version of ScanKeyEntryInitialize: flags and subtype - * are assumed to be zero (the usual value). + * are assumed to be zero (the usual value), and collation is defaulted. * * This is the recommended version for hardwired lookups in system catalogs. * It cannot handle NULL arguments, unary operators, or nondefault operators, * but we need none of those features for most hardwired lookups. * + * We set collation to DEFAULT_COLLATION_OID always. This is appropriate + * for textual columns in system catalogs, and it will be ignored for + * non-textual columns, so it's not worth trying to be more finicky. + * * Note: CurrentMemoryContext at call should be as long-lived as the ScanKey * itself, because that's what will be used for any subsidiary info attached * to the ScanKey's FmgrInfo record. @@ -76,6 +85,7 @@ ScanKeyInit(ScanKey entry, entry->sk_subtype = InvalidOid; entry->sk_argument = argument; fmgr_info(procedure, &entry->sk_func); + entry->sk_func.fn_collation = DEFAULT_COLLATION_OID; } /* @@ -93,6 +103,7 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, + Oid collation, FmgrInfo *finfo, Datum argument) { @@ -102,17 +113,5 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry, entry->sk_subtype = subtype; entry->sk_argument = argument; fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext); -} - -/* - * ScanKeyEntryInitializeCollation - * - * Initialize the collation of a scan key. This is just a notational - * convenience and small abstraction. - */ -void -ScanKeyEntryInitializeCollation(ScanKey entry, - Oid collation) -{ entry->sk_func.fn_collation = collation; } diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index d78b08381e..c06a0271ca 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -427,6 +427,10 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) * TupleDescInitEntry * This function initializes a single attribute structure in * a previously allocated tuple descriptor. + * + * Note that attcollation is set to the default for the specified datatype. + * If a nondefault collation is needed, insert it afterwards using + * TupleDescInitEntryCollation. */ void TupleDescInitEntry(TupleDesc desc, @@ -496,8 +500,8 @@ TupleDescInitEntry(TupleDesc desc, /* * TupleDescInitEntryCollation * - * Fill in the collation for an attribute in a previously initialized - * tuple descriptor. + * Assign a nondefault collation to a previously initialized tuple descriptor + * entry. */ void TupleDescInitEntryCollation(TupleDesc desc, @@ -571,9 +575,9 @@ BuildDescForRelation(List *schema) TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, attdim); - TupleDescInitEntryCollation(desc, attnum, attcollation); /* Override TupleDescInitEntry's settings as requested */ + TupleDescInitEntryCollation(desc, attnum, attcollation); if (entry->storage) desc->attrs[attnum - 1]->attstorage = entry->storage; diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 23965449df..9c4473c449 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -56,6 +56,8 @@ initGinState(GinState *state, Relation index) origTupdesc->attrs[i]->atttypid, origTupdesc->attrs[i]->atttypmod, origTupdesc->attrs[i]->attndims); + TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2, + origTupdesc->attrs[i]->attcollation); } fmgr_info_copy(&(state->compareFn[i]), diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index c5d32ef748..0a125e772d 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -168,7 +168,8 @@ gistrescan(PG_FUNCTION_ARGS) * all comparisons. The original operator is passed to the Consistent * function in the form of its strategy number, which is available * from the sk_strategy field, and its subtype from the sk_subtype - * field. + * field. Also, preserve sk_func.fn_collation which is the input + * collation for the operator. * * Next, if any of keys is a NULL and that key is not marked with * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we @@ -179,8 +180,10 @@ gistrescan(PG_FUNCTION_ARGS) for (i = 0; i < scan->numberOfKeys; i++) { ScanKey skey = scan->keyData + i; + Oid collation = skey->sk_func.fn_collation; skey->sk_func = so->giststate->consistentFn[skey->sk_attno - 1]; + skey->sk_func.fn_collation = collation; if (skey->sk_flags & SK_ISNULL) { @@ -201,13 +204,16 @@ gistrescan(PG_FUNCTION_ARGS) * all comparisons. The original operator is passed to the Distance * function in the form of its strategy number, which is available * from the sk_strategy field, and its subtype from the sk_subtype - * field. + * field. Also, preserve sk_func.fn_collation which is the input + * collation for the operator. */ for (i = 0; i < scan->numberOfOrderBys; i++) { ScanKey skey = scan->orderByData + i; + Oid collation = skey->sk_func.fn_collation; skey->sk_func = so->giststate->distanceFn[skey->sk_attno - 1]; + skey->sk_func.fn_collation = collation; /* Check we actually have a distance function ... */ if (!OidIsValid(skey->sk_func.fn_oid)) diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index be8d958352..cb78a1bae1 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -721,10 +721,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) cur->sk_attno, InvalidStrategy, cur->sk_subtype, + cur->sk_func.fn_collation, procinfo, cur->sk_argument); - ScanKeyEntryInitializeCollation(scankeys + i, - cur->sk_func.fn_collation); } else { @@ -743,10 +742,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) cur->sk_attno, InvalidStrategy, cur->sk_subtype, + cur->sk_func.fn_collation, cmp_proc, cur->sk_argument); - ScanKeyEntryInitializeCollation(scankeys + i, - cur->sk_func.fn_collation); } } } diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 7ee7ebeb3c..add932d942 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -70,7 +70,8 @@ _bt_mkscankey(Relation rel, IndexTuple itup) /* * We can use the cached (default) support procs since no cross-type - * comparison can be needed. + * comparison can be needed. The cached support proc entries have + * the right collation for the index, too. */ procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC); arg = index_getattr(itup, i + 1, itupdesc, &null); @@ -80,6 +81,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup) (AttrNumber) (i + 1), InvalidStrategy, InvalidOid, + procinfo->fn_collation, procinfo, arg); } @@ -118,7 +120,8 @@ _bt_mkscankey_nodata(Relation rel) /* * We can use the cached (default) support procs since no cross-type - * comparison can be needed. + * comparison can be needed. The cached support proc entries have + * the right collation for the index, too. */ procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC); flags = SK_ISNULL | (indoption[i] << SK_BT_INDOPTION_SHIFT); @@ -127,6 +130,7 @@ _bt_mkscankey_nodata(Relation rel) (AttrNumber) (i + 1), InvalidStrategy, InvalidOid, + procinfo->fn_collation, procinfo, (Datum) 0); } diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 050792f4a0..1c96b005d7 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -15,7 +15,6 @@ #include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" -#include "catalog/pg_collation.h" #include "catalog/pg_seclabel.h" #include "commands/seclabel.h" #include "miscadmin.h" @@ -166,7 +165,6 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider) Anum_pg_seclabel_provider, BTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(provider)); - ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID); pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock); @@ -236,7 +234,6 @@ SetSecurityLabel(const ObjectAddress *object, Anum_pg_seclabel_provider, BTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(provider)); - ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID); pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 5c6212c64c..bfa94a0c11 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1455,11 +1455,16 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) RelationGetRelationName(seqrel)))); tupdesc = CreateTemplateTupleDesc(5, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "maximum_value", INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "increment", INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "maximum_value", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "increment", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", + BOOLOID, -1, 0); BlessTupleDesc(tupdesc); diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index e60db7813a..3b8741fc21 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -829,10 +829,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ + ((OpExpr *) clause)->inputcollid, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ - ScanKeyEntryInitializeCollation(this_scan_key, - ((OpExpr *) clause)->inputcollid); } else if (IsA(clause, RowCompareExpr)) { @@ -957,10 +956,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, varattno, /* attribute number */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ + inputcollation, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ - ScanKeyEntryInitializeCollation(this_sub_key, - inputcollation); n_sub_key++; } @@ -1042,10 +1040,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ + saop->inputcollid, /* collation */ opfuncid, /* reg proc to use */ (Datum) 0); /* constant */ - ScanKeyEntryInitializeCollation(this_scan_key, - saop->inputcollid); } else if (IsA(clause, NullTest)) { @@ -1094,6 +1091,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, varattno, /* attribute number to scan */ InvalidStrategy, /* no strategy */ InvalidOid, /* no strategy subtype */ + InvalidOid, /* no collation */ InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ } diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index e46af8cff9..73920f21c8 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -134,30 +134,32 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) { Oid sortFunction; bool reverse; + int flags; if (!get_compare_function_for_ordering_op(node->sortOperators[i], &sortFunction, &reverse)) elog(ERROR, "operator %u is not a valid ordering operator", node->sortOperators[i]); + /* We use btree's conventions for encoding directionality */ + flags = 0; + if (reverse) + flags |= SK_BT_DESC; + if (node->nullsFirst[i]) + flags |= SK_BT_NULLS_FIRST; + /* * We needn't fill in sk_strategy or sk_subtype since these scankeys * will never be passed to an index. */ - ScanKeyInit(&mergestate->ms_scankeys[i], - node->sortColIdx[i], - InvalidStrategy, - sortFunction, - (Datum) 0); - - ScanKeyEntryInitializeCollation(&mergestate->ms_scankeys[i], - node->collations[i]); - - /* However, we use btree's conventions for encoding directionality */ - if (reverse) - mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC; - if (node->nullsFirst[i]) - mergestate->ms_scankeys[i].sk_flags |= SK_BT_NULLS_FIRST; + ScanKeyEntryInitialize(&mergestate->ms_scankeys[i], + flags, + node->sortColIdx[i], + InvalidStrategy, + InvalidOid, + node->collations[i], + sortFunction, + (Datum) 0); } /* diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index eb9bca1987..137c811bc3 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -507,18 +507,30 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(12, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", INETOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_hostname", TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_port", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", + BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", + INETOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_hostname", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_port", + INT4OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index c757fcb424..f7358d198c 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -4649,6 +4649,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, 1, /* index col to scan */ InvalidStrategy, /* no strategy */ InvalidOid, /* no strategy subtype */ + InvalidOid, /* no collation */ InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 2241cb91f2..5caa53d4d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -934,6 +934,8 @@ CatalogCacheInitializeCache(CatCache *cache) /* Fill in sk_strategy as well --- always standard equality */ cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber; cache->cc_skey[i].sk_subtype = InvalidOid; + /* Currently, there are no catcaches on collation-aware data types */ + cache->cc_skey[i].sk_func.fn_collation = InvalidOid; CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p", cache->cc_relname, diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 56185fcabc..a1850b83c5 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -621,6 +621,7 @@ tuplesort_begin_heap(TupleDesc tupDesc, { Oid sortFunction; bool reverse; + int flags; AssertArg(attNums[i] != 0); AssertArg(sortOperators[i] != 0); @@ -630,25 +631,25 @@ tuplesort_begin_heap(TupleDesc tupDesc, elog(ERROR, "operator %u is not a valid ordering operator", sortOperators[i]); + /* We use btree's conventions for encoding directionality */ + flags = 0; + if (reverse) + flags |= SK_BT_DESC; + if (nullsFirstFlags[i]) + flags |= SK_BT_NULLS_FIRST; + /* * We needn't fill in sk_strategy or sk_subtype since these scankeys * will never be passed to an index. */ - ScanKeyInit(&state->scanKeys[i], - attNums[i], - InvalidStrategy, - sortFunction, - (Datum) 0); - - if (collations) - ScanKeyEntryInitializeCollation(&state->scanKeys[i], - collations[i]); - - /* However, we use btree's conventions for encoding directionality */ - if (reverse) - state->scanKeys[i].sk_flags |= SK_BT_DESC; - if (nullsFirstFlags[i]) - state->scanKeys[i].sk_flags |= SK_BT_NULLS_FIRST; + ScanKeyEntryInitialize(&state->scanKeys[i], + flags, + attNums[i], + InvalidStrategy, + InvalidOid, + collations ? collations[i] : InvalidOid, + sortFunction, + (Datum) 0); } MemoryContextSwitchTo(oldcontext); diff --git a/src/include/access/skey.h b/src/include/access/skey.h index 3d2956c935..1d0071ac2d 100644 --- a/src/include/access/skey.h +++ b/src/include/access/skey.h @@ -52,6 +52,9 @@ typedef uint16 StrategyNumber; * the operator. When using a ScanKey in a heap scan, these fields are not * used and may be set to InvalidStrategy/InvalidOid. * + * If the operator is collation-sensitive, sk_func.fn_collation must be set + * correctly as well. + * * A ScanKey can also represent a condition "column IS NULL" or "column * IS NOT NULL"; these cases are signaled by the SK_SEARCHNULL and * SK_SEARCHNOTNULL flag bits respectively. The argument is always NULL, @@ -143,6 +146,7 @@ extern void ScanKeyEntryInitialize(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, + Oid collation, RegProcedure procedure, Datum argument); extern void ScanKeyEntryInitializeWithInfo(ScanKey entry, @@ -150,9 +154,8 @@ extern void ScanKeyEntryInitializeWithInfo(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, + Oid collation, FmgrInfo *finfo, Datum argument); -extern void ScanKeyEntryInitializeCollation(ScanKey entry, - Oid collation); #endif /* SKEY_H */ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 675b91d530..c7ba4248c7 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -2011,12 +2011,14 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) PLpgSQL_variable *var = vars[i]; Oid typoid = RECORDOID; int32 typmod = -1; + Oid typcoll = InvalidOid; switch (var->dtype) { case PLPGSQL_DTYPE_VAR: typoid = ((PLpgSQL_var *) var)->datatype->typoid; typmod = ((PLpgSQL_var *) var)->datatype->atttypmod; + typcoll = ((PLpgSQL_var *) var)->datatype->collation; break; case PLPGSQL_DTYPE_REC: @@ -2027,6 +2029,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) { typoid = ((PLpgSQL_row *) var)->rowtupdesc->tdtypeid; typmod = ((PLpgSQL_row *) var)->rowtupdesc->tdtypmod; + /* composite types have no collation */ } break; @@ -2041,6 +2044,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) var->refname, typoid, typmod, 0); + TupleDescInitEntryCollation(row->rowtupdesc, i + 1, typcoll); } return row;