diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/datum.c | 57 | ||||
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 40 | ||||
-rw-r--r-- | src/backend/utils/adt/rowtypes.c | 41 |
3 files changed, 86 insertions, 52 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 diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index d715709b7cd..b4d0029877b 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -42,6 +42,7 @@ #include "storage/bufmgr.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/inval.h" @@ -2402,18 +2403,11 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, const RI_ConstraintInfo *riinfo, bool rel_is_pk) { const int16 *attnums; - const Oid *eq_oprs; if (rel_is_pk) - { attnums = riinfo->pk_attnums; - eq_oprs = riinfo->pp_eq_oprs; - } else - { attnums = riinfo->fk_attnums; - eq_oprs = riinfo->ff_eq_oprs; - } /* XXX: could be worthwhile to fetch all necessary attrs at once */ for (int i = 0; i < riinfo->nkeys; i++) @@ -2436,12 +2430,32 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, if (isnull) return false; - /* - * Compare them with the appropriate equality operator. - */ - if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]), - oldvalue, newvalue)) - return false; + if (rel_is_pk) + { + /* + * If we are looking at the PK table, then do a bytewise + * comparison. We must propagate PK changes if the value is + * changed to one that "looks" different but would compare as + * equal using the equality operator. This only makes a + * difference for ON UPDATE CASCADE, but for consistency we treat + * all changes to the PK the same. + */ + Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1); + + if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen)) + return false; + } + else + { + /* + * For the FK table, compare with the appropriate equality + * operator. Changes that compare equal will still satisfy the + * constraint after the update. + */ + if (!ri_AttributesEqual(riinfo->ff_eq_oprs[i], RIAttType(rel, attnums[i]), + oldvalue, newvalue)) + return false; + } } return true; diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c index 5bbf5686103..aa7ec8735c4 100644 --- a/src/backend/utils/adt/rowtypes.c +++ b/src/backend/utils/adt/rowtypes.c @@ -23,6 +23,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/lsyscache.h" #include "utils/typcache.h" @@ -1671,45 +1672,7 @@ record_image_eq(PG_FUNCTION_ARGS) } /* Compare the pair of elements */ - if (att1->attlen == -1) - { - Size len1, - len2; - - len1 = toast_raw_datum_size(values1[i1]); - len2 = toast_raw_datum_size(values2[i2]); - /* 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(values1[i1]); - arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]); - - 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) values1[i1]) - pfree(arg1val); - if ((Pointer) arg2val != (Pointer) values2[i2]) - pfree(arg2val); - } - } - else if (att1->attbyval) - { - result = (values1[i1] == values2[i2]); - } - else - { - result = (memcmp(DatumGetPointer(values1[i1]), - DatumGetPointer(values2[i2]), - att1->attlen) == 0); - } + result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen); if (!result) break; } |