@@ -174,7 +174,7 @@ static void protect_system_table_delupd(thread_db* tdbb, const jrd_rel* relation
174174 bool force_flag = false );
175175static void purge (thread_db*, record_param*);
176176static void replace_record (thread_db*, record_param*, PageStack*, const jrd_tra*);
177- static void refresh_fk_fields (thread_db*, Record*, record_param*, record_param*);
177+ static void refresh_changed_fields (thread_db*, Record*, record_param*, record_param*);
178178static SSHORT set_metadata_id (thread_db*, Record*, USHORT, drq_type_t , const char *);
179179static void set_nbackup_id (thread_db*, Record*, USHORT, drq_type_t , const char *);
180180static void set_owner_name (thread_db*, Record*, USHORT);
@@ -3329,7 +3329,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
33293329 fb_assert (!(org_rpb->rpb_runtime_flags & RPB_undo_read));
33303330
33313331 if (undo_read)
3332- refresh_fk_fields (tdbb, old_record, org_rpb, new_rpb);
3332+ refresh_changed_fields (tdbb, old_record, org_rpb, new_rpb);
33333333 }
33343334
33353335 // If we're the system transaction, modify stuff in place. This saves
@@ -6605,43 +6605,43 @@ static void replace_record(thread_db* tdbb,
66056605}
66066606
66076607
6608- static void refresh_fk_fields (thread_db* tdbb, Record* old_rec, record_param* cur_rpb,
6608+ static void refresh_changed_fields (thread_db* tdbb, Record* old_rec, record_param* cur_rpb,
66096609 record_param* new_rpb)
66106610{
66116611/* *************************************
66126612 *
6613- * r e f r e s h _ f k _ f i e l d s
6613+ * r e f r e s h _ c h a n g e d _ f i e l d s
66146614 *
66156615 **************************************
66166616 *
66176617 * Functional description
66186618 * Update new_rpb with foreign key fields values changed by cascade triggers.
66196619 * Consider self-referenced foreign keys only.
6620+ * Also, if AllowUpdateOverwrite is set to false, raise error when non
6621+ * self-referenced foreign key fields were changed by user triggers.
66206622 *
66216623 * old_rec - old record before modify
6622- * cur_rpb - just read record with possibly changed FK fields
6624+ * cur_rpb - just read record with possibly changed fields
66236625 * new_rpb - new record evaluated by modify statement and before-triggers
66246626 *
66256627 **************************************/
6628+ const Database* dbb = tdbb->getDatabase ();
6629+ const auto allowOverwrite = dbb->dbb_config ->getAllowUpdateOverwrite ();
6630+
66266631 jrd_rel* relation = cur_rpb->rpb_relation ;
66276632
66286633 MET_scan_partners (tdbb, relation);
66296634
6630- if (!(relation->rel_foreign_refs .frgn_relations ))
6631- return ;
6632-
6633- const FB_SIZE_T frgnCount = relation->rel_foreign_refs .frgn_relations ->count ();
6634- if (!frgnCount)
6635- return ;
6635+ const FB_SIZE_T frgnCount = relation->rel_foreign_refs .frgn_relations ?
6636+ relation->rel_foreign_refs .frgn_relations ->count () : 0 ;
66366637
66376638 RelationPages* relPages = cur_rpb->rpb_relation ->getPages (tdbb);
66386639
6639- // Collect all fields of all foreign keys
6640+ // Collect all fields of self-referenced foreign keys
66406641 SortedArray<int , InlineStorage<int , 16 > > fields;
66416642
66426643 for (FB_SIZE_T i = 0 ; i < frgnCount; i++)
66436644 {
6644- // We need self-referenced FK's only
66456645 if ((*relation->rel_foreign_refs .frgn_relations )[i] == relation->rel_id )
66466646 {
66476647 index_desc idx;
@@ -6663,26 +6663,61 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu
66636663 }
66646664
66656665 if (fields.isEmpty ())
6666- return ;
6666+ {
6667+ if (allowOverwrite)
6668+ return ;
66676669
6668- DSC desc1, desc2;
6669- for (FB_SIZE_T idx = 0 ; idx < fields.getCount (); idx++)
6670+ if (cur_rpb->rpb_record ->getFormat ()->fmt_version == old_rec->getFormat ()->fmt_version )
6671+ {
6672+ if (memcmp (cur_rpb->rpb_address , old_rec->getData (), cur_rpb->rpb_length ) == 0 )
6673+ return ;
6674+
6675+ ERR_post (Arg::Gds (isc_update_overwrite)); // UPDATE will overwrite changes made by the trigger or by another UPDATE in the same cursor
6676+ }
6677+ // Else compare field-by-field
6678+ }
6679+
6680+ for (FB_SIZE_T fld = 0 , frn = 0 ; fld < relation->rel_current_format ->fmt_count ; fld++)
66706681 {
6671- // Detect if user changed FK field by himself.
6672- const int fld = fields[idx];
6673- const bool flag_old = EVL_field (relation, old_rec, fld, &desc1);
6674- const bool flag_new = EVL_field (relation, new_rpb->rpb_record , fld, &desc2);
6682+ dsc dsc_old;
6683+ const bool flag_old = EVL_field (relation, old_rec, fld, &dsc_old);
66756684
6676- // If field was not changed by user - pick up possible modification by
6677- // system cascade trigger
6678- if (flag_old == flag_new &&
6679- (!flag_old || (flag_old && !MOV_compare (tdbb, &desc1, &desc2))))
6685+ const bool is_fk = (frn < fields.getCount () && fields[frn] == fld);
6686+ if (!is_fk)
66806687 {
6681- const bool flag_tmp = EVL_field (relation, cur_rpb->rpb_record , fld, &desc1);
6682- if (flag_tmp)
6683- MOV_move (tdbb, &desc1, &desc2);
6684- else
6685- new_rpb->rpb_record ->setNull (fld);
6688+ if (allowOverwrite)
6689+ continue ;
6690+
6691+ dsc dsc_cur;
6692+ const bool flag_cur = EVL_field (relation, cur_rpb->rpb_record , fld, &dsc_cur);
6693+
6694+ // Check if current record differs from old record
6695+ if ((flag_cur != flag_old) ||
6696+ (flag_cur && flag_old && MOV_compare (tdbb, &dsc_old, &dsc_cur) != 0 ))
6697+ {
6698+ // Record was modified by trigger.
6699+ ERR_post (Arg::Gds (isc_update_overwrite)); // UPDATE will overwrite changes made by the trigger or by another UPDATE in the same cursor
6700+ }
6701+ }
6702+ else
6703+ {
6704+ dsc dsc_new;
6705+ const bool flag_new = EVL_field (relation, new_rpb->rpb_record , fld, &dsc_new);
6706+
6707+ // If field was not changed by user - pick up possible modification by
6708+ // system cascade trigger
6709+ if (flag_old == flag_new &&
6710+ (!flag_old || (flag_old && !MOV_compare (tdbb, &dsc_old, &dsc_new))))
6711+ {
6712+ dsc dsc_cur;
6713+ const bool flag_cur = EVL_field (relation, cur_rpb->rpb_record , fld, &dsc_cur);
6714+ if (flag_cur)
6715+ MOV_move (tdbb, &dsc_cur, &dsc_new);
6716+ else
6717+ new_rpb->rpb_record ->setNull (fld);
6718+ }
6719+
6720+ frn++;
66866721 }
66876722 }
66886723}
0 commit comments