|
[
Permalink
| « Hide
]
Andrea Casati added a comment - 02/Jul/08 06:50 PM
Same behaviour on FB2.0.3
Isn't this change a rather big deal?
I suspect quite a few people have used the current behavior to implement locking (on purpose) -- lock the parent record by applying a dummy update to it, to be sure no new children will be added during some operation. I would generally use "id=id", but not everyone would. Besides, that doesn't actually "change" the field, so will it prevent insertions now? The logic I remember getting from Helen on this was that once a transaction acquires a record write lock by applying any updates to it, other transactions have no FK guarantees on that row -- transaction 1 may have only updated one field to start with, but may later come back around and delete the entire record, or modify its PK! You can't assume that a transaction will only touch a record once. The lock is there to protect other transactions from assuming something that isn't guaranteed, and won't be re-verified later (no deferred constraints, particularly FK constraints.) Example: Transaction 1: update parent set useless_field = useless_field + 1 where id = 4; -- not an update on the PK field Transaction 2: insert into child (parent_id, other_field) values (4, 7); -- FK check succeeds, record is inserted Transaction 1: delete from parent where id = 4; -- an update on the PK field, but the damage is done; on-delete-cascade cannot see other newly inserted record Transaction 1: commit; Transaction 2: commit; -- either FB auto-deletes the record without informing transaction 1 (bad), or the record stays (bad) or you need to implement deferred constraints so you can catch this at commit time, and throw an exception here Not really a workaround: Transaction 1: update parent set useless_field = useless_field + 1 where id = 4; -- not an update on the PK field Transaction 2: insert into child (parent_id, other_field) values (4, 7); -- let's assume this gives TX2 a record lock on parent=4, to prevent the parent from being deleted or relocated (pk change) Transaction 1: delete from parent where id = 4; -- raise an exception; although TX1 should have a record lock (and does?), another transaction also does, so now it can't be deleted? Phillip,
delete from parent will fail in both cases, because a) fk check works independent if transactions isolation and it see all child records, even not committed b) child record is active (not committed) so cascade action (if specified) will raise "update conflict" error Vlad, thanks. To make sure I understand (documentation? what?):
If the parent record is deleted (TX1) before the child is created (TX2), the child transaction will notice the error -- it can see the parent, but the FK check sees the parent has been deleted (or its PK changed), so it fails anyway. If the parent is deleted (TX1) after the child is created (TX2), the FK check on the parent will notice the new children and attempt updates or deletes, which will fail because of record locks held by TX2, even if the child record has since been committed. (Or it will attempt no action, and fail because of the presence of child records.) Even if TX2 commits before TX1, TX1 cannot delete the parent record, because it cannot modify the record inserted by TX2, as TX2 was not already committed when TX1 started. Because an update to the invisible child record can never succeed, it shouldn't matter that there may be triggers defined on the child table; those triggers won't get run, so there's no opportunity for TX1 to run triggers on data visible only in TX2, leading to confusion in TX1. So my second example was correct: TX1 will fail to delete a record on which it could reasonably think it already had a record lock by having performed an update. If a table has two separate unique fields (candidate keys), with two child tables defined with FK's referencing one of those unique fields each, a transaction can only truly acquire a record lock via a row update if it modifies (touches?) both of those fields, to ensure both FK's will fail immediately? There won't be a data-integrity problem, just a weirdness with record locks unexpectedly going missing. Philip,
At first, there is no such thing as "record lock" in Firebird, thanks to MVCC. There are transactions, its states and rules of transactions visibility to each other. > So my second example was correct: TX1 will fail to delete a record Yes, and i confirmed it > If a table has two separate unique fields (candidate keys), with two child tables defined with FK's referencing one of those unique fields each, a transaction can only truly acquire a record lock via a row update if it modifies (touches?) both of those fields, to ensure both FK's will fail immediately? There won't be a data-integrity problem, just a weirdness with record locks unexpectedly going missing. Again, there is no such thing as "record lock ". As soon as *any* field of master record included into *any* PK\UK is modified, record marked with special flag and no child record can create reference to this master record. Yes, this is compromiss between complexity and usability. And this was also discussed in fb-devel. If you need more details, please, re-read corresponding discussion in fb-devel ("Serious error in FB 2.0 and 2.1 Foreign Key handling" by Sean Leyne at 11 Apr 2008) and ask there. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||