Skip to content

Commit 9b43c24

Browse files
committed
Avoid misbehavior in foreign key checks when casting to a datatype for which
the parser supplies a default typmod that can result in data loss (ie, truncation). Currently that appears to be only CHARACTER and BIT. We can avoid the problem by specifying the type's internal name instead of using SQL-spec syntax. Since the queries generated here are only used internally, there's no need to worry about portability. This problem is new in 8.3; before we just let the parser do whatever it wanted to resolve the operator, but 8.3 is trying to be sure that the semantics of FK checks are consistent. Per report from Harald Fuchs.
1 parent d9c7f63 commit 9b43c24

File tree

1 file changed

+40
-5
lines changed

1 file changed

+40
-5
lines changed

src/backend/utils/adt/ri_triggers.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1717
*
18-
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.102 2008/01/25 04:46:07 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.103 2008/02/07 22:58:35 tgl Exp $
1919
*
2020
* ----------
2121
*/
@@ -185,6 +185,7 @@ static void ri_GenerateQual(StringInfo buf,
185185
const char *leftop, Oid leftoptype,
186186
Oid opoid,
187187
const char *rightop, Oid rightoptype);
188+
static void ri_add_cast_to(StringInfo buf, Oid typid);
188189
static int ri_NullCheck(Relation rel, HeapTuple tup,
189190
RI_QueryKey *key, int pairidx);
190191
static void ri_BuildQueryKeyFull(RI_QueryKey *key,
@@ -2893,8 +2894,10 @@ quoteRelationName(char *buffer, Relation rel)
28932894
* The idea is to append " sep leftop op rightop" to buf. The complexity
28942895
* comes from needing to be sure that the parser will select the desired
28952896
* operator. We always name the operator using OPERATOR(schema.op) syntax
2896-
* (readability isn't a big priority here). We have to emit casts too,
2897-
* if either input isn't already the input type of the operator.
2897+
* (readability isn't a big priority here), so as to avoid search-path
2898+
* uncertainties. We have to emit casts too, if either input isn't already
2899+
* the input type of the operator; else we are at the mercy of the parser's
2900+
* heuristics for ambiguous-operator resolution.
28982901
*/
28992902
static void
29002903
ri_GenerateQual(StringInfo buf,
@@ -2921,16 +2924,48 @@ ri_GenerateQual(StringInfo buf,
29212924

29222925
appendStringInfo(buf, " %s %s", sep, leftop);
29232926
if (leftoptype != operform->oprleft)
2924-
appendStringInfo(buf, "::%s", format_type_be(operform->oprleft));
2927+
ri_add_cast_to(buf, operform->oprleft);
29252928
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
29262929
appendStringInfoString(buf, oprname);
29272930
appendStringInfo(buf, ") %s", rightop);
29282931
if (rightoptype != operform->oprright)
2929-
appendStringInfo(buf, "::%s", format_type_be(operform->oprright));
2932+
ri_add_cast_to(buf, operform->oprright);
29302933

29312934
ReleaseSysCache(opertup);
29322935
}
29332936

2937+
/*
2938+
* Add a cast specification to buf. We spell out the type name the hard way,
2939+
* intentionally not using format_type_be(). This is to avoid corner cases
2940+
* for CHARACTER, BIT, and perhaps other types, where specifying the type
2941+
* using SQL-standard syntax results in undesirable data truncation. By
2942+
* doing it this way we can be certain that the cast will have default (-1)
2943+
* target typmod.
2944+
*/
2945+
static void
2946+
ri_add_cast_to(StringInfo buf, Oid typid)
2947+
{
2948+
HeapTuple typetup;
2949+
Form_pg_type typform;
2950+
char *typname;
2951+
char *nspname;
2952+
2953+
typetup = SearchSysCache(TYPEOID,
2954+
ObjectIdGetDatum(typid),
2955+
0, 0, 0);
2956+
if (!HeapTupleIsValid(typetup))
2957+
elog(ERROR, "cache lookup failed for type %u", typid);
2958+
typform = (Form_pg_type) GETSTRUCT(typetup);
2959+
2960+
typname = NameStr(typform->typname);
2961+
nspname = get_namespace_name(typform->typnamespace);
2962+
2963+
appendStringInfo(buf, "::%s.%s",
2964+
quote_identifier(nspname), quote_identifier(typname));
2965+
2966+
ReleaseSysCache(typetup);
2967+
}
2968+
29342969
/* ----------
29352970
* ri_BuildQueryKeyFull -
29362971
*

0 commit comments

Comments
 (0)