Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
'transaction_mode' => 'DEFERRED',
],

'mysql' => [
Expand Down
8 changes: 6 additions & 2 deletions src/Illuminate/Database/Concerns/ManagesTransactions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
namespace Illuminate\Database\Concerns;

use Closure;
use Illuminate\Database\Connection;
use Illuminate\Database\DeadlockException;
use RuntimeException;
use Throwable;

/**
* @mixin Connection
*/
trait ManagesTransactions
{
/**
Expand Down Expand Up @@ -148,7 +152,7 @@ protected function createTransaction()
$this->reconnectIfMissingConnection();

try {
$this->getPdo()->beginTransaction();
$this->executeBeginTransactionStatement();
} catch (Throwable $e) {
$this->handleBeginTransactionException($e);
}
Expand Down Expand Up @@ -184,7 +188,7 @@ protected function handleBeginTransactionException(Throwable $e)
if ($this->causedByLostConnection($e)) {
$this->reconnect();

$this->getPdo()->beginTransaction();
$this->executeBeginTransactionStatement();
} else {
throw $e;
}
Expand Down
10 changes: 10 additions & 0 deletions src/Illuminate/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,16 @@ public function unsetTransactionManager()
$this->transactionsManager = null;
}

/**
* Run the statement to start a new transaction.
*
* @return void
*/
public function executeBeginTransactionStatement()
{
$this->getPdo()->beginTransaction();
}

/**
* Determine if the connection is in a "dry run".
*
Expand Down
13 changes: 13 additions & 0 deletions src/Illuminate/Database/SQLiteConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,17 @@ protected function getDefaultPostProcessor()
{
return new SQLiteProcessor;
}

public function executeBeginTransactionStatement()
{
if (version_compare(PHP_VERSION, '8.4.0') >= 0) {
$mode = $this->getConfig('transaction_mode') ?? 'DEFERRED';

$this->getPdo()->exec("BEGIN $mode TRANSACTION");
Copy link
Contributor

Choose a reason for hiding this comment

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

Just for some context, as far as I can tell, there is no issue whatsoever with using a manual exec("BEGIN ...") instead of beginTransaction() with Pdo\Sqlite. They are equivalent.

PDO has some extra logic for tracking whether a connection is in a transaction, but it looks like this:

static bool pdo_is_in_transaction(pdo_dbh_t *dbh) {
	if (dbh->methods->in_transaction) {
		return dbh->methods->in_transaction(dbh);
	}
	return dbh->in_txn;
}

Bypassing beginTransaction() bypasses setting in_txn = true. However, the SQLite driver does implement in_transaction, so that serves as the source of truth for whether the connection is in a transaction.

Simple test:

$ cat /tmp/intxn.php
<?php

$pdo = PDO::connect('sqlite::memory:');
$pdo->beginTransaction();
var_dump($pdo->inTransaction());
$pdo->rollBack();
var_dump($pdo->inTransaction());
$pdo->exec('begin transaction');
var_dump($pdo->inTransaction());
$ php /tmp/intxn.php
bool(true)
bool(false)
bool(true)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe if you run this same script in PHP 8.3 and below you will get

bool(true)
bool(false)
bool(false)

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch. Seems this behavior was fixed here php/php-src#14268, so it's PHP 8.4 only yeah.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right I see your comment above now, missed that before sorry.


return;
}

$this->getPdo()->beginTransaction();
}
}