Skip to content

New setting UpdateOverwriteMode : defines how UPDATE should handle the case when a record was updated by trigger.#8957

Open
hvlad wants to merge 2 commits intov5.0-releasefrom
work/UpdateOverwriteMode
Open

New setting UpdateOverwriteMode : defines how UPDATE should handle the case when a record was updated by trigger.#8957
hvlad wants to merge 2 commits intov5.0-releasefrom
work/UpdateOverwriteMode

Conversation

@hvlad
Copy link
Copy Markdown
Member

@hvlad hvlad commented Mar 26, 2026

After #8538 there was long private discussion with customer (big sponsor of Firebird).
The root of the issue is the way Firebird implements per-row triggers - it is far from SQL standard in regards of consistency.
There is well-known historical reasons for it and I don't want to repeat it here.
By my opinion, the best way to fix the issue would be to re-implement per-row triggers according to SQL standard - to fire before and after whole statement processing. But this is not quick nor easy task.
Finally this patch was implemented and customer uses it to prepare own migration to v5.
Now it is time to decide if it could be accepted into codebase.

…e case when a record was updated by trigger.
@hvlad hvlad self-assigned this Mar 26, 2026
@AlexPeshkoff
Copy link
Copy Markdown
Member

AlexPeshkoff commented Mar 26, 2026 via email

@hvlad
Copy link
Copy Markdown
Member Author

hvlad commented Mar 26, 2026

Vlad, am I missing something or this patch works only with system triggers that implement cascade FK?

The routine, I modified in the patch, was initially used to workaroud issue with self-referenced FK's

@dyemanov dyemanov self-requested a review April 3, 2026 10:11
Copy link
Copy Markdown
Member

@dyemanov dyemanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I expected this PR to implement the same logic as seems to exist for FKs:

If field was not changed by user - pick up possible modification by trigger

instead of raising an error unconditionally.

However, AFAIU, we cannot detect this reliably at the VIO layer, because cannot distinguish between new.field explicitly assigned to the old value or copied from org_record. This would need accessing the ModifyNode's assignment list and checking for modified fields there. Although I'm not sure this is going to be as simple.

Did you consider this approach? Because the new mode reminds me the famous "table is mutating" error in Oracle, if raised even for non-conflicting changes.

#
# Per-database configurable.
#
# Type: integer
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why integer and 0/1 instead of boolean and false/true? And rename to something like UpdateOverwriteStrictMode or RestrictUpdateOverwrite?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I thought about 3rd value - to issue warning, not error. But it was not implemented as I found it unpractical, iirc.

As for the setting name - I can agree with anything reasonable ;) Lets decide if we need such changes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I going to add forgoten note that this setting is temporary and could be removed in future versions.

src/jrd/vio.cpp Outdated
return;

fb_assert(overwriteMode == 1);
ERR_post(Arg::Gds(isc_random) << "UPDATE will overwrite changes made by trigger");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a new ISC code, unless this gonna be reimplemented in v6.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will do

const bool flag_cur = EVL_field(relation, cur_rpb->rpb_record, fld, &dsc_cur);

// Check if current record differs from old record
if ((flag_cur != flag_old) ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the field didn't exist in the old format but exists in the new format and gets some value updated by a trigger (e.g. because the new field is NOT NULL), an error will be raised. Is it intended?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any field of current record was changed by trigger, that fired for another record - yes, this is error.
Despite of formats.

@hvlad
Copy link
Copy Markdown
Member Author

hvlad commented Apr 3, 2026

Honestly, I expected this PR to implement the same logic as seems to exist for FKs:

If field was not changed by user - pick up possible modification by trigger

instead of raising an error unconditionally.

I consider this as wrong way because it let user triggers to implicitly change original UPDATE semantic.
Also, it is unpredictable and depends on internal code path and physical records order.
This is definitely not consistent behavior.

Case with self-referenced FK's is more predictable and consistent (unless same fields not changed by user trigger).

However, AFAIU, we cannot detect this reliably at the VIO layer, because cannot distinguish between new.field explicitly assigned to the old value or copied from org_record. This would need accessing the ModifyNode's assignment list and checking for modified fields there. Although I'm not sure this is going to be as simple.

Did you consider this approach?

No. By the reasons as explained above.

Because the new mode reminds me the famous "table is mutating" error in Oracle, if raised even for non-conflicting changes.

The more I think on this issue, the more I agree with Oracle - it should not be allowed. And, actually, I've implemented such check when works on the issue past year. But - not a surprise - it was too restrictive and customer does not accepted it. Finally, the setting UpdateOverwriteMode was introduced and agreed as compromise solution.

Ideally, we should reimplement the way per-row triggers works to provide consistensy and comply with SQL standard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants