[Session] fix PDO transaction aborted under PostgreSQL
This commit is contained in:
parent
86552ea2dc
commit
f8eefa0748
@ -510,54 +510,51 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
$selectSql = $this->getSelectSql();
|
$selectSql = $this->getSelectSql();
|
||||||
$selectStmt = $this->pdo->prepare($selectSql);
|
$selectStmt = $this->pdo->prepare($selectSql);
|
||||||
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||||
$selectStmt->execute();
|
|
||||||
|
|
||||||
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
|
do {
|
||||||
|
$selectStmt->execute();
|
||||||
|
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
|
||||||
|
|
||||||
if ($sessionRows) {
|
if ($sessionRows) {
|
||||||
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
|
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
|
||||||
$this->sessionExpired = true;
|
$this->sessionExpired = true;
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
|
||||||
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
|
||||||
// until other connections to the session are committed.
|
|
||||||
try {
|
|
||||||
$insertStmt = $this->pdo->prepare(
|
|
||||||
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
|
|
||||||
);
|
|
||||||
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
|
||||||
$insertStmt->bindValue(':data', '', \PDO::PARAM_LOB);
|
|
||||||
$insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT);
|
|
||||||
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
|
||||||
$insertStmt->execute();
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
// Catch duplicate key error because other connection created the session already.
|
|
||||||
// It would only not be the case when the other connection destroyed the session.
|
|
||||||
if (0 === strpos($e->getCode(), '23')) {
|
|
||||||
// Retrieve finished session data written by concurrent connection. SELECT
|
|
||||||
// FOR UPDATE is necessary to avoid deadlock of connection that starts reading
|
|
||||||
// before we write (transform intention to real lock).
|
|
||||||
$selectStmt->execute();
|
|
||||||
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
|
|
||||||
|
|
||||||
if ($sessionRows) {
|
|
||||||
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw $e;
|
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||||
|
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
||||||
|
// until other connections to the session are committed.
|
||||||
|
try {
|
||||||
|
$insertStmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
|
||||||
|
);
|
||||||
|
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||||
|
$insertStmt->bindValue(':data', '', \PDO::PARAM_LOB);
|
||||||
|
$insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT);
|
||||||
|
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
|
$insertStmt->execute();
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// Catch duplicate key error because other connection created the session already.
|
||||||
|
// It would only not be the case when the other connection destroyed the session.
|
||||||
|
if (0 === strpos($e->getCode(), '23')) {
|
||||||
|
// Retrieve finished session data written by concurrent connection by restarting the loop.
|
||||||
|
// We have to start a new transaction as a failed query will mark the current transaction as
|
||||||
|
// aborted in PostgreSQL and disallow further queries within it.
|
||||||
|
$this->rollback();
|
||||||
|
$this->beginTransaction();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,4 +362,8 @@ class MockPdo extends \PDO
|
|||||||
public function beginTransaction()
|
public function beginTransaction()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rollBack()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user