Skip to content

Commit 891d354

Browse files
committed
bug #25430 Fixes for Oracle in PdoSessionHandler (elislenio)
This PR was squashed before being merged into the 2.7 branch (closes #25430). Discussion ---------- Fixes for Oracle in PdoSessionHandler | Q | A | ------------- | --- | Branch? | 2.7 <!-- see below --> | Bug fix? | yes | New feature? | no <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no | Deprecations? | no <!-- don't forget to update UPGRADE-*.md files --> | Tests pass? | yes | Fixed tickets | #18305 <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | <!--highly recommended for new features--> <!-- Fixes missing session data for Oracle in PdoSessionHandler --> Commits ------- e7a4002 Fixes for Oracle in PdoSessionHandler
2 parents b316e11 + e7a4002 commit 891d354

File tree

1 file changed

+90
-48
lines changed

1 file changed

+90
-48
lines changed

src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,7 @@ public function write($sessionId, $data)
330330
return true;
331331
}
332332

333-
$updateStmt = $this->pdo->prepare(
334-
"UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
335-
);
336-
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
337-
$updateStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
338-
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
339-
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
333+
$updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime);
340334
$updateStmt->execute();
341335

342336
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
@@ -346,13 +340,7 @@ public function write($sessionId, $data)
346340
// false positives due to longer gap locking.
347341
if (!$updateStmt->rowCount()) {
348342
try {
349-
$insertStmt = $this->pdo->prepare(
350-
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
351-
);
352-
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
353-
$insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
354-
$insertStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
355-
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
343+
$insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime);
356344
$insertStmt->execute();
357345
} catch (\PDOException $e) {
358346
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
@@ -521,13 +509,7 @@ private function doRead($sessionId)
521509
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
522510
// until other connections to the session are committed.
523511
try {
524-
$insertStmt = $this->pdo->prepare(
525-
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
526-
);
527-
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
528-
$insertStmt->bindValue(':data', '', \PDO::PARAM_LOB);
529-
$insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT);
530-
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
512+
$insertStmt = $this->getInsertStatement($sessionId, '', 0);
531513
$insertStmt->execute();
532514
} catch (\PDOException $e) {
533515
// Catch duplicate key error because other connection created the session already.
@@ -662,6 +644,72 @@ private function getSelectSql()
662644
return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id";
663645
}
664646

647+
/**
648+
* Returns a insert statement supported by the database for writing session data.
649+
*
650+
* @param string $sessionId Session ID
651+
* @param string $sessionData Encoded session data
652+
* @param int $maxlifetime session.gc_maxlifetime
653+
*
654+
* @return \PDOStatement The insert statement
655+
*/
656+
private function getInsertStatement($sessionId, $sessionData, $maxlifetime)
657+
{
658+
switch ($this->driver) {
659+
case 'oci':
660+
$data = fopen('php://memory', 'r+');
661+
fwrite($data, $sessionData);
662+
rewind($data);
663+
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data";
664+
break;
665+
default:
666+
$data = $sessionData;
667+
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
668+
break;
669+
}
670+
671+
$stmt = $this->pdo->prepare($sql);
672+
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
673+
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
674+
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
675+
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
676+
677+
return $stmt;
678+
}
679+
680+
/**
681+
* Returns a update statement supported by the database for writing session data.
682+
*
683+
* @param string $sessionId Session ID
684+
* @param string $sessionData Encoded session data
685+
* @param int $maxlifetime session.gc_maxlifetime
686+
*
687+
* @return \PDOStatement The update statement
688+
*/
689+
private function getUpdateStatement($sessionId, $sessionData, $maxlifetime)
690+
{
691+
switch ($this->driver) {
692+
case 'oci':
693+
$data = fopen('php://memory', 'r+');
694+
fwrite($data, $sessionData);
695+
rewind($data);
696+
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
697+
break;
698+
default:
699+
$data = $sessionData;
700+
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
701+
break;
702+
}
703+
704+
$stmt = $this->pdo->prepare($sql);
705+
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
706+
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
707+
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
708+
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
709+
710+
return $stmt;
711+
}
712+
665713
/**
666714
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
667715
*
@@ -673,18 +721,11 @@ private function getSelectSql()
673721
*/
674722
private function getMergeStatement($sessionId, $data, $maxlifetime)
675723
{
676-
$mergeSql = null;
677724
switch (true) {
678725
case 'mysql' === $this->driver:
679726
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
680727
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
681728
break;
682-
case 'oci' === $this->driver:
683-
// DUAL is Oracle specific dummy table
684-
$mergeSql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
685-
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
686-
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
687-
break;
688729
case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
689730
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
690731
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
@@ -699,29 +740,30 @@ private function getMergeStatement($sessionId, $data, $maxlifetime)
699740
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
700741
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
701742
break;
743+
default:
744+
// MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
745+
return null;
702746
}
703747

704-
if (null !== $mergeSql) {
705-
$mergeStmt = $this->pdo->prepare($mergeSql);
706-
707-
if ('sqlsrv' === $this->driver || 'oci' === $this->driver) {
708-
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
709-
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
710-
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
711-
$mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
712-
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
713-
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
714-
$mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
715-
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
716-
} else {
717-
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
718-
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
719-
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
720-
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
721-
}
722-
723-
return $mergeStmt;
748+
$mergeStmt = $this->pdo->prepare($mergeSql);
749+
750+
if ('sqlsrv' === $this->driver) {
751+
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
752+
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
753+
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
754+
$mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
755+
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
756+
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
757+
$mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
758+
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
759+
} else {
760+
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
761+
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
762+
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
763+
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
724764
}
765+
766+
return $mergeStmt;
725767
}
726768

727769
/**

0 commit comments

Comments
 (0)