-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(database): add support for nested transactions using savepoints #8833
base: 4.6
Are you sure you want to change the base?
Conversation
4b94ec2
to
753765d
Compare
Maybe this should be targeted at 4.6, but IMHO since it fixes nested transactions for when using phpunit which uses the SQLite3 driver by default, and nested transactions are clearly documented, this may also be considered a fix? |
367d5d9
to
47db608
Compare
Thank you for sending this PR. This is not a bug fix. Read the documentation. The current behavior is clearly documented.
The problems:
Please fix the problems. If you have any questions, feel free to ask. |
Ah thanks for your quick and detailed feedback! I clearly overread the important part here with the outermost transaction. I'll definitly rebase and target 4.6 now. I also tested the MySQLi driver with mariadb on debian bookworm with the same SAVEPOINT (https://mariadb.com/kb/en/savepoint/) Queries as above, and they seem to be compatible. I was thinking about creating a trait like In order not to introduce a breaking change and be backwards-compatible, should I add a configuration option like Or do you prefer not cluttering? Thanks for your help! |
I might be able to implement this as well for PostgreSQL which seems to suppor the same savepoint syntax, but I have no access or experience to the other database systems. |
I've now implemented most of this on the BaseConnection directly, while also providing a configuration option with the very verbose name Nested transactions should now be supported by mysqli, sqlite3 as well as the postgre driver |
I'll now wait for some feedback, before cleaning up everything to hopefully make it ready to merge. |
whoops, missed the MockConnection |
this should fix most if not all of the linting mistakes |
82c06cd
to
2f4bd3a
Compare
I've now added some more tests and the Although i removed the setting, because the BaseConnection just checks whether a property exists, its still settable via a BaseConnection option as before. If this is not desired, how should I avoid this? Additionally I deliberately only talk about transaction nesting instead of savepoints in the Context of BaseConnection as of now, as it technically doesn't need to know how nesting is implemented. Is this useful or just nonsense? Some of the tests may not check for the correct behaviour, regarding error handling and exception handling in the context of transactions. What should the interactions between DBDebug, transException and transNest look like? Addtionally there are probably some more details with each driver automatically |
I've added some fixes for the tests and I'm getting a clearer picture now. But I've been thinking about another thing: strict mode. Strict mode is turned on by default, which causes everything to be rolled back when it is enabled as soon as a single transaction (even nested at a deep level) causes all future ones to be rolled back. Is the solution to apply strict to a transaction layer, meaning that future transactions in the same or lower level automatically fail, when it is set? We'd have to keep track of transStatus (+ maybe transFailure) as well as the transStrict setting on a per "transaction layer" basis. |
0e1cdd4
to
ed7127b
Compare
I think we don't need to avoid it. |
It is recommended to always set DBDebug to true. During transactions, by default CI4 does not throw exceptions. |
Test whether we can start two nested transactions ... 1. ... complete them and see the result in the database 2. ... rollback the outer one after completing the inner one, and see that the result is _not_ in the database. 3. ... roll back the inner one and see if the result of the outer one is in the database after completing.
This option will be false by default, so the previous documented behaviour of CodeIgniter gets kept & to avoid introducing a breaking change. CodeIgniter allows for nesting transactions, but only ever cared about, actually committing/rolling back the most outer one. This commit keeps this behaviour by default, but adds the database driver option 'enableNestedTransactions' to the BaseConnection. When set to true, codeigniter now will call _transBeginNested()/_transCommitNested()/_transRollbackNested() on the respective driver implemented, when further transactions are nested into the outermost transaction.
By using SAVEPOINT SQL statements to manage transactions deeper than 1 level, it's possible to provide full nested transaction semantics for the following drivers: - SQLite3: - https://sqlite.org/lang_transaction.html - Savepoints: https://sqlite.org/lang_savepoint.html - MySQLi - https://mariadb.com/kb/en/savepoint/ - Postgre - https://www.postgresql.org/docs/current/sql-savepoint.html The implementation starts a transaction when transBegin() is first called as before. But then all nested transactions will be managed by creating, releasing (commiting) or rolling back savepoint. Only when the outermost transaction is completed a COMMIT or ROLLBACK will be executed, either committing the transaction and all inner _committed_ transactions, or rolling everything back. This feature is currently disabled for SQLSrv & OCI8 and they keep on. working with just 1 transaction layer & nested ones getting ignored. Revert "feat(sqlite3): add support for nested transactions using SAVEPOINT" This reverts commit 47db608. feat(database): use savepoints for nested transactions for MySQLi, SQLite3 + Postgre driver
…is enabled, plus an extreme example
…ust transRollback()
…are implemented now
1254e83
to
6970c48
Compare
Thanks a lot for your clarifications, this helps a lot. I've added Savepoints to the missing drivers OCI8 and SQLSRV as well. I'd be glad if you could run the workflows again, to know what to fix. |
👋 Hi, @mkuettel! |
This branch has conflicts. Please resolve. |
It's possible to provide nested transaction semantics when using SQLite3
by using
SAVEPOINT
statements to manage transactions deeper than 1 level.The implementation starts a transaction when
transBegin()
is first called as before.But then all nested transactions will be managed by creating,
releasing (commiting) or rolling back savepoint.
Only when the outermost transaction is completed a COMMIT or ROLLBACK
will be executed, either committing the transaction and all inner
committed transactions, or rolling everything back.
Description
Explain what you have changed, and why.
Checklist: