@@ -178,7 +178,7 @@ static void protect_system_table_delupd(thread_db* tdbb, const jrd_rel* relation
178178 bool force_flag = false );
179179static void purge (thread_db*, record_param*);
180180static void replace_record (thread_db*, record_param*, PageStack*, const jrd_tra*);
181- static void refresh_fk_fields (thread_db*, Record*, record_param*, record_param*);
181+ static void refresh_changed_fields (thread_db*, Record*, record_param*, record_param*);
182182static SSHORT set_metadata_id (thread_db*, Record*, USHORT, drq_type_t , const char *);
183183static void set_nbackup_id (thread_db*, Record*, USHORT, drq_type_t , const char *);
184184static void set_owner_name (thread_db*, Record*, USHORT);
@@ -3317,7 +3317,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
33173317 fb_assert (!(org_rpb->rpb_runtime_flags & RPB_undo_read));
33183318
33193319 if (undo_read)
3320- refresh_fk_fields (tdbb, old_record, org_rpb, new_rpb);
3320+ refresh_changed_fields (tdbb, old_record, org_rpb, new_rpb);
33213321 }
33223322
33233323 // If we're the system transaction, modify stuff in place. This saves
@@ -7014,83 +7014,118 @@ static void replace_record(thread_db* tdbb,
70147014}
70157015
70167016
7017- static void refresh_fk_fields (thread_db* tdbb, Record* old_rec, record_param* cur_rpb,
7017+ static void refresh_changed_fields (thread_db* tdbb, Record* old_rec, record_param* cur_rpb,
70187018 record_param* new_rpb)
70197019{
70207020/* *************************************
70217021 *
7022- * r e f r e s h _ f k _ f i e l d s
7022+ * r e f r e s h _ c h a n g e d _ f i e l d s
70237023 *
70247024 **************************************
70257025 *
70267026 * Functional description
70277027 * Update new_rpb with foreign key fields values changed by cascade triggers.
70287028 * Consider self-referenced foreign keys only.
7029+ * Also, if AllowUpdateOverwrite is set to false, raise error when non
7030+ * self-referenced foreign key fields were changed by user triggers.
70297031 *
70307032 * old_rec - old record before modify
7031- * cur_rpb - just read record with possibly changed FK fields
7033+ * cur_rpb - just read record with possibly changed fields
70327034 * new_rpb - new record evaluated by modify statement and before-triggers
70337035 *
70347036 **************************************/
7035- auto * relation = cur_rpb->rpb_relation ->getPermanent ();
7037+ const Database* dbb = tdbb->getDatabase ();
7038+ const auto allowOverwrite = dbb->dbb_config ->getAllowUpdateOverwrite ();
70367039
7037- relation->scanPartners (tdbb);
7040+ jrd_rel* relation = cur_rpb->rpb_relation ;
7041+ auto * relPerm = relation->getPermanent ();
70387042
7039- const auto * frgn = relation->rel_foreign_refs ;
7040- if (!frgn)
7041- return ;
7042-
7043- if (!frgn->getCount ())
7044- return ;
7045-
7046- RelationPages* relPages = cur_rpb->rpb_relation ->getPages (tdbb);
7043+ relPerm->scanPartners (tdbb);
70477044
7048- // Collect all fields of all foreign keys
7045+ // Collect all fields of self-referenced foreign keys
70497046 SortedArray<int , InlineStorage<int , 16 > > fields;
70507047
7051- for ( auto & dep : *frgn )
7048+ if ( const auto * frgn = relPerm-> rel_foreign_refs )
70527049 {
7053- // We need self-referenced FK's only
7054- if ( dep. dep_relation == relation-> getId () )
7050+ RelationPages* relPages = relation-> getPages (tdbb);
7051+ for ( auto & dep : *frgn )
70557052 {
7056- index_desc idx;
7057- idx.idx_id = idx_invalid;
7058-
7059- if (BTR_lookup (tdbb, relation, dep.dep_reference_id , &idx, relPages))
7053+ if (dep.dep_relation == relPerm->getId ())
70607054 {
7061- fb_assert (idx.idx_flags & idx_foreign);
7055+ index_desc idx;
7056+ idx.idx_id = idx_invalid;
70627057
7063- for ( int fld = 0 ; fld < idx. idx_count ; fld++ )
7058+ if ( BTR_lookup (tdbb, relPerm, dep. dep_reference_id , & idx, relPages) )
70647059 {
7065- const int fldNum = idx.idx_rpt [fld].idx_field ;
7066- if (!fields.exist (fldNum))
7067- fields.add (fldNum);
7060+ fb_assert (idx.idx_flags & idx_foreign);
7061+
7062+ for (int fld = 0 ; fld < idx.idx_count ; fld++)
7063+ {
7064+ const int fldNum = idx.idx_rpt [fld].idx_field ;
7065+ if (!fields.exist (fldNum))
7066+ fields.add (fldNum);
7067+ }
70687068 }
70697069 }
70707070 }
70717071 }
70727072
70737073 if (fields.isEmpty ())
7074- return ;
7074+ {
7075+ if (allowOverwrite)
7076+ return ;
7077+
7078+ if (cur_rpb->rpb_record ->getFormat ()->fmt_version == old_rec->getFormat ()->fmt_version )
7079+ {
7080+ if (memcmp (cur_rpb->rpb_address , old_rec->getData (), cur_rpb->rpb_length ) == 0 )
7081+ return ;
70757082
7076- DSC desc1, desc2;
7077- for (FB_SIZE_T idx = 0 ; idx < fields.getCount (); idx++)
7083+ ERR_post (Arg::Gds (isc_update_overwrite)); // UPDATE will overwrite changes made by the trigger or by another UPDATE in the same cursor
7084+ }
7085+ // Else compare field-by-field
7086+ }
7087+
7088+ for (FB_SIZE_T fld = 0 , frn = 0 ; fld < relation->rel_current_format ->fmt_count ; fld++)
70787089 {
7079- // Detect if user changed FK field by himself.
7080- const int fld = fields[idx];
7081- const bool flag_old = EVL_field (cur_rpb->rpb_relation , old_rec, fld, &desc1);
7082- const bool flag_new = EVL_field (cur_rpb->rpb_relation , new_rpb->rpb_record , fld, &desc2);
7090+ dsc dsc_old;
7091+ const bool flag_old = EVL_field (relation, old_rec, fld, &dsc_old);
70837092
7084- // If field was not changed by user - pick up possible modification by
7085- // system cascade trigger
7086- if (flag_old == flag_new &&
7087- (!flag_old || (flag_old && !MOV_compare (tdbb, &desc1, &desc2))))
7093+ const bool is_fk = (frn < fields.getCount () && fields[frn] == fld);
7094+ if (!is_fk)
70887095 {
7089- const bool flag_tmp = EVL_field (cur_rpb->rpb_relation , cur_rpb->rpb_record , fld, &desc1);
7090- if (flag_tmp)
7091- MOV_move (tdbb, &desc1, &desc2);
7092- else
7093- new_rpb->rpb_record ->setNull (fld);
7096+ if (allowOverwrite)
7097+ continue ;
7098+
7099+ dsc dsc_cur;
7100+ const bool flag_cur = EVL_field (relation, cur_rpb->rpb_record , fld, &dsc_cur);
7101+
7102+ // Check if current record differs from old record
7103+ if ((flag_cur != flag_old) ||
7104+ (flag_cur && flag_old && MOV_compare (tdbb, &dsc_old, &dsc_cur) != 0 ))
7105+ {
7106+ // Record was modified by trigger.
7107+ ERR_post (Arg::Gds (isc_update_overwrite)); // UPDATE will overwrite changes made by the trigger or by another UPDATE in the same cursor
7108+ }
7109+ }
7110+ else
7111+ {
7112+ dsc dsc_new;
7113+ const bool flag_new = EVL_field (relation, new_rpb->rpb_record , fld, &dsc_new);
7114+
7115+ // If field was not changed by user - pick up possible modification by
7116+ // system cascade trigger
7117+ if (flag_old == flag_new &&
7118+ (!flag_old || (flag_old && !MOV_compare (tdbb, &dsc_old, &dsc_new))))
7119+ {
7120+ dsc dsc_cur;
7121+ const bool flag_cur = EVL_field (relation, cur_rpb->rpb_record , fld, &dsc_cur);
7122+ if (flag_cur)
7123+ MOV_move (tdbb, &dsc_cur, &dsc_new);
7124+ else
7125+ new_rpb->rpb_record ->setNull (fld);
7126+ }
7127+
7128+ frn++;
70947129 }
70957130 }
70967131}
0 commit comments