Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

New driver fixes from manual testing #51

Merged
merged 9 commits into from
May 30, 2025
Merged

New driver fixes from manual testing #51

merged 9 commits into from
May 30, 2025

Conversation

JanJakes
Copy link
Contributor

@JanJakes JanJakes commented May 28, 2025

List of discovered issues:

  • Data types from _mysql_data_types_cache are sometimes not correctly fetched, as they can be saved in multiple formates (lowercase, uppercase, with or without backticks).
  • MySQL applies type casting when saving data in non-strict mode, resulting in things like empty string being saved as 0 for an integer, 0000-00-00 00:00:00 for a date, etc.
  • Functional defaults are incorrectly applied in non-strict mode (DEFAULT CURRENT_TIMESTAMP saves a 'CURRENT_TIMESTAMP' string rather than the correct value).
  • MySQL BINARY type is saved as INTEGER, but it's actually a binary string, and BLOB would be more suitable.
  • MySQL supports date and time components without a zero padding, but that doesn't work with date and time functions in SQLite. (E.g.: 2025-3-7 9:5:2 is a valid datetime/timestamp value in MySQL, but SQLite requires it to be 2025-03-07 09:05:02.)
  • In multi-process environments (php-fpm), concurrent writes can cause "SQLSTATE[HY000]: General error: 5 database is locked" (SQLITE_BUSY).

@JanJakes JanJakes force-pushed the fixes branch 3 times, most recently from ec80726 to 34999cd Compare May 29, 2025 08:46
@JanJakes JanJakes changed the title Fixes New driver fixes from manual testing May 29, 2025
@JanJakes JanJakes marked this pull request as ready for review May 29, 2025 14:59
@JanJakes JanJakes requested a review from adamziel May 29, 2025 14:59
@@ -735,7 +735,31 @@ public function execute_sqlite_query( string $sql, array $params = array() ): PD
*/
public function begin_transaction(): void {
if ( 0 === $this->transaction_level ) {
$this->execute_sqlite_query( 'BEGIN' );
/*
* When we're executing a statement that will write to the database,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd add this excerpt:

Transactions can be DEFERRED, IMMEDIATE, or EXCLUSIVE. The default transaction behavior is DEFERRED.

DEFERRED means that the transaction does not actually start until the database is first accessed.

IMMEDIATE causes the database connection to start a new write immediately, without waiting for a write statement. The BEGIN IMMEDIATE might fail with SQLITE_BUSY if another write transaction is already active on another database connection.

It also makes me question why BEGIN IMMEDIATE works – this passage suggests it should not. Is there any way we can add a deterministic unit test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@adamziel I added the docs: 31c319c

It also makes me question why BEGIN IMMEDIATE works – this passage suggests it should not.

I think it works because even though upgrading a read transaction to a write transaction fails immediately when another write is in progress, the BEGIN IMMEDIATE statement can honor the busy_timeout, as there is no need to restart a full transaction.

Is there any way we can add a deterministic unit test?

I can give it a shot. Probably not so easy to do in PHP.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! Maybe starting two connections in a single thread would suffice? If we can avoid synchronizing multiple processes it may be pretty easy

Copy link
Contributor Author

@JanJakes JanJakes May 30, 2025

Choose a reason for hiding this comment

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

@adamziel I don't have any async query APIs at hand, so I'm quite limited in what I can do. I can create a locked state, and I could measure that it applies a timeout, but that's about it:

$driver1->query('CREATE TABLE t (id INT)');
$driver1->query('BEGIN');
$driver2->query('INSERT INTO t (id) VALUES (2)'); // this waits for $driver1 to finish, and timeouts
$driver1->query('COMMIT'); // but we never get to this, because all the APIs are sync

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see what you mean. Would this help? https://github.com/clue/reactphp-sqlite

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@adamziel I think so, yeah, should we add it for this test?

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@adamziel Ah, but installing that package will actually not give the SQLite driver the async APIs (we're using PDO), so I'm afraid it's not an easy solution either. I had some luck with pcntl_fork, maybe I can try to finish that.

Copy link
Contributor

@adamziel adamziel May 30, 2025

Choose a reason for hiding this comment

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

Symfony\Process is pretty neat FWIW. I use it in the new Blueprint runner. But it's not quite forks so pcntl might be for the best

Copy link
Contributor

@adamziel adamziel left a comment

Choose a reason for hiding this comment

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

I only have one note that may or may not be blocking. Really good work here, thank you 🙌

@JanJakes JanJakes merged commit a6a760e into develop May 30, 2025
12 checks passed
@JanJakes JanJakes deleted the fixes branch May 30, 2025 13:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants