Skip to content

Commit 9321d2f

Browse files
committed
Fix collation handling for foreign keys
Allowing foreign keys where the referenced and the referencing columns have collations with different notions of equality is problematic. This can only happen when using nondeterministic collations, for example, if the referencing column is case-insensitive and the referenced column is not, or vice versa. It does not happen if both collations are deterministic. To show one example: CREATE COLLATION case_insensitive (provider = icu, deterministic = false, locale = 'und-u-ks-level2'); CREATE TABLE pktable (x text COLLATE "C" PRIMARY KEY); CREATE TABLE fktable (x text COLLATE case_insensitive REFERENCES pktable ON UPDATE CASCADE ON DELETE CASCADE); INSERT INTO pktable VALUES ('A'), ('a'); INSERT INTO fktable VALUES ('A'); BEGIN; DELETE FROM pktable WHERE x = 'a'; TABLE fktable; ROLLBACK; BEGIN; DELETE FROM pktable WHERE x = 'A'; TABLE fktable; ROLLBACK; Both of these DELETE statements delete the one row from fktable. So this means that one row from fktable references two rows in pktable, which should not happen. (That's why a primary key or unique constraint is required on pktable.) When nondeterministic collations were implemented, the SQL standard available to yours truly said that referential integrity checks should be performed with the collation of the referenced column, and so that's how we implemented it. But this turned out to be a mistake in the SQL standard, for the same reasons as above, that was later (SQL:2016) fixed to require both collations to be the same. So that's what we are aiming for here. We don't have to be quite so strict. We can allow different collations if they are both deterministic. This is also good for backward compatibility. So the new rule is that the collations either have to be the same or both deterministic. Or in other words, if one of them is nondeterministic, then both have to be the same. Users upgrading from before that have affected setups will need to make changes to their schemas (i.e., change one or both collations in affected foreign-key relationships) before the upgrade will succeed. Some of the nice test cases for the previous situation in collate.icu.utf8.sql are now obsolete. They are changed to just check the error checking of the new rule. Note that collate.sql already contained a test for foreign keys with different deterministic collations. A bunch of code in ri_triggers.c that added a COLLATE clause to enforce the referenced column's collation can be removed, because both columns now have to have the same notion of equality, so it doesn't matter which one to use. Reported-by: Paul Jungwirth <[email protected]> Reviewed-by: Jian He <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/[email protected]
1 parent 90bcc7c commit 9321d2f

File tree

5 files changed

+105
-178
lines changed

5 files changed

+105
-178
lines changed

doc/src/sgml/ref/create_table.sgml

+7
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,13 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
12051205
must have its final column marked <literal>WITHOUT OVERLAPS</literal>.
12061206
</para>
12071207

1208+
<para>
1209+
For each pair of referencing and referenced column, if they are of a
1210+
collatable data type, then the collations must either be both
1211+
deterministic or else both the same. This ensures that both columns
1212+
have a consistent notion of equality.
1213+
</para>
1214+
12081215
<para>
12091216
The user
12101217
must have <literal>REFERENCES</literal> permission on the referenced

src/backend/commands/tablecmds.c

+68-16
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,10 @@ static ObjectAddress ATExecValidateConstraint(List **wqueue,
398398
Relation rel, char *constrName,
399399
bool recurse, bool recursing, LOCKMODE lockmode);
400400
static int transformColumnNameList(Oid relId, List *colList,
401-
int16 *attnums, Oid *atttypids);
401+
int16 *attnums, Oid *atttypids, Oid *attcollids);
402402
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
403403
List **attnamelist,
404-
int16 *attnums, Oid *atttypids,
404+
int16 *attnums, Oid *atttypids, Oid *attcollids,
405405
Oid *opclasses, bool *pk_has_without_overlaps);
406406
static Oid transformFkeyCheckAttrs(Relation pkrel,
407407
int numattrs, int16 *attnums,
@@ -9705,6 +9705,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
97059705
int16 fkattnum[INDEX_MAX_KEYS] = {0};
97069706
Oid pktypoid[INDEX_MAX_KEYS] = {0};
97079707
Oid fktypoid[INDEX_MAX_KEYS] = {0};
9708+
Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9709+
Oid fkcolloid[INDEX_MAX_KEYS] = {0};
97089710
Oid opclasses[INDEX_MAX_KEYS] = {0};
97099711
Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
97109712
Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
@@ -9801,11 +9803,11 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
98019803

98029804
/*
98039805
* Look up the referencing attributes to make sure they exist, and record
9804-
* their attnums and type OIDs.
9806+
* their attnums and type and collation OIDs.
98059807
*/
98069808
numfks = transformColumnNameList(RelationGetRelid(rel),
98079809
fkconstraint->fk_attrs,
9808-
fkattnum, fktypoid);
9810+
fkattnum, fktypoid, fkcolloid);
98099811
with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
98109812
if (with_period && !fkconstraint->fk_with_period)
98119813
ereport(ERROR,
@@ -9814,7 +9816,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
98149816

98159817
numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
98169818
fkconstraint->fk_del_set_cols,
9817-
fkdelsetcols, NULL);
9819+
fkdelsetcols, NULL, NULL);
98189820
validateFkOnDeleteSetColumns(numfks, fkattnum,
98199821
numfkdelsetcols, fkdelsetcols,
98209822
fkconstraint->fk_del_set_cols);
@@ -9823,13 +9825,14 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
98239825
* If the attribute list for the referenced table was omitted, lookup the
98249826
* definition of the primary key and use it. Otherwise, validate the
98259827
* supplied attribute list. In either case, discover the index OID and
9826-
* index opclasses, and the attnums and type OIDs of the attributes.
9828+
* index opclasses, and the attnums and type and collation OIDs of the
9829+
* attributes.
98279830
*/
98289831
if (fkconstraint->pk_attrs == NIL)
98299832
{
98309833
numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
98319834
&fkconstraint->pk_attrs,
9832-
pkattnum, pktypoid,
9835+
pkattnum, pktypoid, pkcolloid,
98339836
opclasses, &pk_has_without_overlaps);
98349837

98359838
/* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
@@ -9842,7 +9845,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
98429845
{
98439846
numpks = transformColumnNameList(RelationGetRelid(pkrel),
98449847
fkconstraint->pk_attrs,
9845-
pkattnum, pktypoid);
9848+
pkattnum, pktypoid, pkcolloid);
98469849

98479850
/* Since we got pk_attrs, one should be a period. */
98489851
if (with_period && !fkconstraint->pk_with_period)
@@ -9944,6 +9947,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
99449947
Oid pktype = pktypoid[i];
99459948
Oid fktype = fktypoid[i];
99469949
Oid fktyped;
9950+
Oid pkcoll = pkcolloid[i];
9951+
Oid fkcoll = fkcolloid[i];
99479952
HeapTuple cla_ht;
99489953
Form_pg_opclass cla_tup;
99499954
Oid amid;
@@ -10086,6 +10091,41 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1008610091
format_type_be(fktype),
1008710092
format_type_be(pktype))));
1008810093

10094+
/*
10095+
* This shouldn't be possible, but better check to make sure we have a
10096+
* consistent state for the check below.
10097+
*/
10098+
if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10099+
elog(ERROR, "key columns are not both collatable");
10100+
10101+
if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10102+
{
10103+
bool pkcolldet;
10104+
bool fkcolldet;
10105+
10106+
pkcolldet = get_collation_isdeterministic(pkcoll);
10107+
fkcolldet = get_collation_isdeterministic(fkcoll);
10108+
10109+
/*
10110+
* SQL requires that both collations are the same. This is
10111+
* because we need a consistent notion of equality on both
10112+
* columns. We relax this by allowing different collations if
10113+
* they are both deterministic. (This is also for backward
10114+
* compatibility, because PostgreSQL has always allowed this.)
10115+
*/
10116+
if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10117+
ereport(ERROR,
10118+
(errcode(ERRCODE_COLLATION_MISMATCH),
10119+
errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10120+
errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10121+
"have incompatible collations: \"%s\" and \"%s\". "
10122+
"If either collation is nondeterministic, then both collations have to be the same.",
10123+
strVal(list_nth(fkconstraint->fk_attrs, i)),
10124+
strVal(list_nth(fkconstraint->pk_attrs, i)),
10125+
get_collation_name(fkcoll),
10126+
get_collation_name(pkcoll))));
10127+
}
10128+
1008910129
if (old_check_ok)
1009010130
{
1009110131
/*
@@ -10106,6 +10146,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1010610146
CoercionPathType new_pathtype;
1010710147
Oid old_castfunc;
1010810148
Oid new_castfunc;
10149+
Oid old_fkcoll;
10150+
Oid new_fkcoll;
1010910151
Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
1011010152
fkattnum[i] - 1);
1011110153

@@ -10121,6 +10163,9 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1012110163
new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
1012210164
&new_castfunc);
1012310165

10166+
old_fkcoll = attr->attcollation;
10167+
new_fkcoll = fkcoll;
10168+
1012410169
/*
1012510170
* Upon a change to the cast from the FK column to its pfeqop
1012610171
* operand, revalidate the constraint. For this evaluation, a
@@ -10144,9 +10189,10 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1014410189
* turn conform to the domain. Consequently, we need not treat
1014510190
* domains specially here.
1014610191
*
10147-
* Since we require that all collations share the same notion of
10148-
* equality (which they do, because texteq reduces to bitwise
10149-
* equality), we don't compare collation here.
10192+
* If the collation changes, revalidation is required, unless both
10193+
* collations are deterministic, because those share the same
10194+
* notion of equality (because texteq reduces to bitwise
10195+
* equality).
1015010196
*
1015110197
* We need not directly consider the PK type. It's necessarily
1015210198
* binary coercible to the opcintype of the unique index column,
@@ -10156,7 +10202,9 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1015610202
old_check_ok = (new_pathtype == old_pathtype &&
1015710203
new_castfunc == old_castfunc &&
1015810204
(!IsPolymorphicType(pfeqop_right) ||
10159-
new_fktype == old_fktype));
10205+
new_fktype == old_fktype) &&
10206+
(new_fkcoll == old_fkcoll ||
10207+
(get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
1016010208
}
1016110209

1016210210
pfeqoperators[i] = pfeqop;
@@ -12092,7 +12140,8 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
1209212140
/*
1209312141
* transformColumnNameList - transform list of column names
1209412142
*
12095-
* Lookup each name and return its attnum and, optionally, type OID
12143+
* Lookup each name and return its attnum and, optionally, type and collation
12144+
* OIDs
1209612145
*
1209712146
* Note: the name of this function suggests that it's general-purpose,
1209812147
* but actually it's only used to look up names appearing in foreign-key
@@ -12101,7 +12150,7 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
1210112150
*/
1210212151
static int
1210312152
transformColumnNameList(Oid relId, List *colList,
12104-
int16 *attnums, Oid *atttypids)
12153+
int16 *attnums, Oid *atttypids, Oid *attcollids)
1210512154
{
1210612155
ListCell *l;
1210712156
int attnum;
@@ -12132,6 +12181,8 @@ transformColumnNameList(Oid relId, List *colList,
1213212181
attnums[attnum] = attform->attnum;
1213312182
if (atttypids != NULL)
1213412183
atttypids[attnum] = attform->atttypid;
12184+
if (attcollids != NULL)
12185+
attcollids[attnum] = attform->attcollation;
1213512186
ReleaseSysCache(atttuple);
1213612187
attnum++;
1213712188
}
@@ -12142,7 +12193,7 @@ transformColumnNameList(Oid relId, List *colList,
1214212193
/*
1214312194
* transformFkeyGetPrimaryKey -
1214412195
*
12145-
* Look up the names, attnums, and types of the primary key attributes
12196+
* Look up the names, attnums, types, and collations of the primary key attributes
1214612197
* for the pkrel. Also return the index OID and index opclasses of the
1214712198
* index supporting the primary key. Also return whether the index has
1214812199
* WITHOUT OVERLAPS.
@@ -12155,7 +12206,7 @@ transformColumnNameList(Oid relId, List *colList,
1215512206
static int
1215612207
transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
1215712208
List **attnamelist,
12158-
int16 *attnums, Oid *atttypids,
12209+
int16 *attnums, Oid *atttypids, Oid *attcollids,
1215912210
Oid *opclasses, bool *pk_has_without_overlaps)
1216012211
{
1216112212
List *indexoidlist;
@@ -12229,6 +12280,7 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
1222912280

1223012281
attnums[i] = pkattno;
1223112282
atttypids[i] = attnumTypeId(pkrel, pkattno);
12283+
attcollids[i] = attnumCollationId(pkrel, pkattno);
1223212284
opclasses[i] = indclass->values[i];
1223312285
*attnamelist = lappend(*attnamelist,
1223412286
makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));

src/backend/utils/adt/ri_triggers.c

+19-38
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ static void ri_BuildQueryKey(RI_QueryKey *key,
207207
int32 constr_queryno);
208208
static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
209209
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
210-
static bool ri_CompareWithCast(Oid eq_opr, Oid typeid,
210+
static bool ri_CompareWithCast(Oid eq_opr, Oid typeid, Oid collid,
211211
Datum lhs, Datum rhs);
212212

213213
static void ri_InitHashTables(void);
@@ -776,8 +776,6 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
776776
{
777777
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
778778
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
779-
Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
780-
Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
781779

782780
quoteOneName(attname,
783781
RIAttName(fk_rel, riinfo->fk_attnums[i]));
@@ -786,8 +784,6 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
786784
paramname, pk_type,
787785
riinfo->pf_eq_oprs[i],
788786
attname, fk_type);
789-
if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
790-
ri_GenerateQualCollation(&querybuf, pk_coll);
791787
querysep = "AND";
792788
queryoids[i] = pk_type;
793789
}
@@ -881,8 +877,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
881877
{
882878
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
883879
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
884-
Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
885-
Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
886880

887881
quoteOneName(attname,
888882
RIAttName(fk_rel, riinfo->fk_attnums[i]));
@@ -891,8 +885,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
891885
paramname, pk_type,
892886
riinfo->pf_eq_oprs[i],
893887
attname, fk_type);
894-
if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
895-
ri_GenerateQualCollation(&querybuf, pk_coll);
896888
querysep = "AND";
897889
queryoids[i] = pk_type;
898890
}
@@ -996,8 +988,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
996988
{
997989
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
998990
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
999-
Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1000-
Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1001991

1002992
quoteOneName(attname,
1003993
RIAttName(fk_rel, riinfo->fk_attnums[i]));
@@ -1009,8 +999,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
1009999
paramname, pk_type,
10101000
riinfo->pf_eq_oprs[i],
10111001
attname, fk_type);
1012-
if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
1013-
ri_GenerateQualCollation(&querybuf, pk_coll);
10141002
querysep = ",";
10151003
qualsep = "AND";
10161004
queryoids[i] = pk_type;
@@ -1232,8 +1220,6 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
12321220
{
12331221
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
12341222
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1235-
Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1236-
Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
12371223

12381224
quoteOneName(attname,
12391225
RIAttName(fk_rel, riinfo->fk_attnums[i]));
@@ -1243,8 +1229,6 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
12431229
paramname, pk_type,
12441230
riinfo->pf_eq_oprs[i],
12451231
attname, fk_type);
1246-
if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
1247-
ri_GenerateQualCollation(&querybuf, pk_coll);
12481232
qualsep = "AND";
12491233
queryoids[i] = pk_type;
12501234
}
@@ -1998,19 +1982,17 @@ ri_GenerateQual(StringInfo buf,
19981982
/*
19991983
* ri_GenerateQualCollation --- add a COLLATE spec to a WHERE clause
20001984
*
2001-
* At present, we intentionally do not use this function for RI queries that
2002-
* compare a variable to a $n parameter. Since parameter symbols always have
2003-
* default collation, the effect will be to use the variable's collation.
2004-
* Now that is only strictly correct when testing the referenced column, since
2005-
* the SQL standard specifies that RI comparisons should use the referenced
2006-
* column's collation. However, so long as all collations have the same
2007-
* notion of equality (which they do, because texteq reduces to bitwise
2008-
* equality), there's no visible semantic impact from using the referencing
2009-
* column's collation when testing it, and this is a good thing to do because
2010-
* it lets us use a normal index on the referencing column. However, we do
2011-
* have to use this function when directly comparing the referencing and
2012-
* referenced columns, if they are of different collations; else the parser
2013-
* will fail to resolve the collation to use.
1985+
* We only have to use this function when directly comparing the referencing
1986+
* and referenced columns, if they are of different collations; else the
1987+
* parser will fail to resolve the collation to use. We don't need to use
1988+
* this function for RI queries that compare a variable to a $n parameter.
1989+
* Since parameter symbols always have default collation, the effect will be
1990+
* to use the variable's collation.
1991+
*
1992+
* Note that we require that the collations of the referencing and the
1993+
* referenced column have the same notion of equality: Either they have to
1994+
* both be deterministic or else they both have to be the same. (See also
1995+
* ATAddForeignKeyConstraint().)
20141996
*/
20151997
static void
20161998
ri_GenerateQualCollation(StringInfo buf, Oid collation)
@@ -2952,7 +2934,7 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
29522934
* operator. Changes that compare equal will still satisfy the
29532935
* constraint after the update.
29542936
*/
2955-
if (!ri_CompareWithCast(eq_opr, RIAttType(rel, attnums[i]),
2937+
if (!ri_CompareWithCast(eq_opr, RIAttType(rel, attnums[i]), RIAttCollation(rel, attnums[i]),
29562938
newvalue, oldvalue))
29572939
return false;
29582940
}
@@ -2968,11 +2950,12 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
29682950
* Call the appropriate comparison operator for two values.
29692951
* Normally this is equality, but for the PERIOD part of foreign keys
29702952
* it is ContainedBy, so the order of lhs vs rhs is significant.
2953+
* See below for how the collation is applied.
29712954
*
29722955
* NB: we have already checked that neither value is null.
29732956
*/
29742957
static bool
2975-
ri_CompareWithCast(Oid eq_opr, Oid typeid,
2958+
ri_CompareWithCast(Oid eq_opr, Oid typeid, Oid collid,
29762959
Datum lhs, Datum rhs)
29772960
{
29782961
RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
@@ -2998,19 +2981,17 @@ ri_CompareWithCast(Oid eq_opr, Oid typeid,
29982981
* on the other side of a foreign-key constraint. Therefore, the
29992982
* comparison here would need to be done with the collation of the *other*
30002983
* table. For simplicity (e.g., we might not even have the other table
3001-
* open), we'll just use the default collation here, which could lead to
3002-
* some false negatives. All this would break if we ever allow
3003-
* database-wide collations to be nondeterministic.
2984+
* open), we'll use our own collation. This is fine because we require
2985+
* that both collations have the same notion of equality (either they are
2986+
* both deterministic or else they are both the same).
30042987
*
30052988
* With range/multirangetypes, the collation of the base type is stored as
30062989
* part of the rangetype (pg_range.rngcollation), and always used, so
30072990
* there is no danger of inconsistency even using a non-equals operator.
30082991
* But if we support arbitrary types with PERIOD, we should perhaps just
30092992
* always force a re-check.
30102993
*/
3011-
return DatumGetBool(FunctionCall2Coll(&entry->eq_opr_finfo,
3012-
DEFAULT_COLLATION_OID,
3013-
lhs, rhs));
2994+
return DatumGetBool(FunctionCall2Coll(&entry->eq_opr_finfo, collid, lhs, rhs));
30142995
}
30152996

30162997
/*

0 commit comments

Comments
 (0)