diff options
author | Peter Eisentraut <peter@eisentraut.org> | 2019-03-18 17:01:40 +0100 |
---|---|---|
committer | Peter Eisentraut <peter@eisentraut.org> | 2019-03-18 17:19:21 +0100 |
commit | 1ffa59a85cb40a61f4523fb03c8960db97eea124 (patch) | |
tree | 98a8ed4bcff8600e2a548e72ca502243ceea7c04 /src/backend/utils/adt/datum.c | |
parent | fb5806533f9fe0433290d84c9b019399cd69e9c2 (diff) | |
download | postgresql-1ffa59a85cb40a61f4523fb03c8960db97eea124.tar.gz postgresql-1ffa59a85cb40a61f4523fb03c8960db97eea124.zip |
Fix optimization of foreign-key on update actions
In RI_FKey_pk_upd_check_required(), we check among other things
whether the old and new key are equal, so that we don't need to run
cascade actions when nothing has actually changed. This was using the
equality operator. But the effect of this is that if a value in the
primary key is changed to one that "looks" different but compares as
equal, the update is not propagated. (Examples are float -0 and 0 and
case-insensitive text.) This appears to violate the SQL standard, and
it also behaves inconsistently if in a multicolumn key another key is
also updated that would cause the row to compare as not equal.
To fix, if we are looking at the PK table in ri_KeysEqual(), then do a
bytewise comparison similar to record_image_eq() instead of using the
equality operators. This only makes a difference for ON UPDATE
CASCADE, but for consistency we treat all changes to the PK the same. For
the FK table, we continue to use the equality operators.
Discussion: https://www.postgresql.org/message-id/flat/3326fc2e-bc02-d4c5-e3e5-e54da466e89a@2ndquadrant.com
Diffstat (limited to 'src/backend/utils/adt/datum.c')
-rw-r--r-- | src/backend/utils/adt/datum.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c index af5944d3954..81ea5a48e59 100644 --- a/src/backend/utils/adt/datum.c +++ b/src/backend/utils/adt/datum.c @@ -42,6 +42,8 @@ #include "postgres.h" +#include "access/tuptoaster.h" +#include "fmgr.h" #include "utils/datum.h" #include "utils/expandeddatum.h" @@ -252,6 +254,61 @@ datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen) } /*------------------------------------------------------------------------- + * datum_image_eq + * + * Compares two datums for identical contents, based on byte images. Return + * true if the two datums are equal, false otherwise. + *------------------------------------------------------------------------- + */ +bool +datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen) +{ + bool result = true; + + if (typLen == -1) + { + Size len1, + len2; + + len1 = toast_raw_datum_size(value1); + len2 = toast_raw_datum_size(value2); + /* No need to de-toast if lengths don't match. */ + if (len1 != len2) + result = false; + else + { + struct varlena *arg1val; + struct varlena *arg2val; + + arg1val = PG_DETOAST_DATUM_PACKED(value1); + arg2val = PG_DETOAST_DATUM_PACKED(value2); + + result = (memcmp(VARDATA_ANY(arg1val), + VARDATA_ANY(arg2val), + len1 - VARHDRSZ) == 0); + + /* Only free memory if it's a copy made here. */ + if ((Pointer) arg1val != (Pointer) value1) + pfree(arg1val); + if ((Pointer) arg2val != (Pointer) value2) + pfree(arg2val); + } + } + else if (typByVal) + { + result = (value1 == value2); + } + else + { + result = (memcmp(DatumGetPointer(value1), + DatumGetPointer(value2), + typLen) == 0); + } + + return result; +} + +/*------------------------------------------------------------------------- * datumEstimateSpace * * Compute the amount of space that datumSerialize will require for a |