@@ -332,13 +332,7 @@ protected function doWrite($sessionId, $data)
332
332
return true ;
333
333
}
334
334
335
- $ updateStmt = $ this ->pdo ->prepare (
336
- "UPDATE $ this ->table SET $ this ->dataCol = :data, $ this ->lifetimeCol = :lifetime, $ this ->timeCol = :time WHERE $ this ->idCol = :id "
337
- );
338
- $ updateStmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
339
- $ updateStmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
340
- $ updateStmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
341
- $ updateStmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
335
+ $ updateStmt = $ this ->getUpdateStatement ($ sessionId , $ data , $ maxlifetime );
342
336
$ updateStmt ->execute ();
343
337
344
338
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
@@ -348,13 +342,7 @@ protected function doWrite($sessionId, $data)
348
342
// false positives due to longer gap locking.
349
343
if (!$ updateStmt ->rowCount ()) {
350
344
try {
351
- $ insertStmt = $ this ->pdo ->prepare (
352
- "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) "
353
- );
354
- $ insertStmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
355
- $ insertStmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
356
- $ insertStmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
357
- $ insertStmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
345
+ $ insertStmt = $ this ->getInsertStatement ($ sessionId , $ data , $ maxlifetime );
358
346
$ insertStmt ->execute ();
359
347
} catch (\PDOException $ e ) {
360
348
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
@@ -547,13 +535,7 @@ protected function doRead($sessionId)
547
535
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
548
536
// until other connections to the session are committed.
549
537
try {
550
- $ insertStmt = $ this ->pdo ->prepare (
551
- "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) "
552
- );
553
- $ insertStmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
554
- $ insertStmt ->bindValue (':data ' , '' , \PDO ::PARAM_LOB );
555
- $ insertStmt ->bindValue (':lifetime ' , 0 , \PDO ::PARAM_INT );
556
- $ insertStmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
538
+ $ insertStmt = $ this ->getInsertStatement ($ sessionId , '' , 0 );
557
539
$ insertStmt ->execute ();
558
540
} catch (\PDOException $ e ) {
559
541
// Catch duplicate key error because other connection created the session already.
@@ -688,6 +670,72 @@ private function getSelectSql()
688
670
return "SELECT $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol FROM $ this ->table WHERE $ this ->idCol = :id " ;
689
671
}
690
672
673
+ /**
674
+ * Returns an insert statement supported by the database for writing session data.
675
+ *
676
+ * @param string $sessionId Session ID
677
+ * @param string $sessionData Encoded session data
678
+ * @param int $maxlifetime session.gc_maxlifetime
679
+ *
680
+ * @return \PDOStatement The insert statement
681
+ */
682
+ private function getInsertStatement ($ sessionId , $ sessionData , $ maxlifetime )
683
+ {
684
+ switch ($ this ->driver ) {
685
+ case 'oci ' :
686
+ $ data = fopen ('php://memory ' , 'r+ ' );
687
+ fwrite ($ data , $ sessionData );
688
+ rewind ($ data );
689
+ $ sql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $ this ->dataCol into :data " ;
690
+ break ;
691
+ default :
692
+ $ data = $ sessionData ;
693
+ $ sql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) " ;
694
+ break ;
695
+ }
696
+
697
+ $ stmt = $ this ->pdo ->prepare ($ sql );
698
+ $ stmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
699
+ $ stmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
700
+ $ stmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
701
+ $ stmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
702
+
703
+ return $ stmt ;
704
+ }
705
+
706
+ /**
707
+ * Returns an update statement supported by the database for writing session data.
708
+ *
709
+ * @param string $sessionId Session ID
710
+ * @param string $sessionData Encoded session data
711
+ * @param int $maxlifetime session.gc_maxlifetime
712
+ *
713
+ * @return \PDOStatement The update statement
714
+ */
715
+ private function getUpdateStatement ($ sessionId , $ sessionData , $ maxlifetime )
716
+ {
717
+ switch ($ this ->driver ) {
718
+ case 'oci ' :
719
+ $ data = fopen ('php://memory ' , 'r+ ' );
720
+ fwrite ($ data , $ sessionData );
721
+ rewind ($ data );
722
+ $ sql = "UPDATE $ this ->table SET $ this ->dataCol = EMPTY_BLOB(), $ this ->lifetimeCol = :lifetime, $ this ->timeCol = :time WHERE $ this ->idCol = :id RETURNING $ this ->dataCol into :data " ;
723
+ break ;
724
+ default :
725
+ $ data = $ sessionData ;
726
+ $ sql = "UPDATE $ this ->table SET $ this ->dataCol = :data, $ this ->lifetimeCol = :lifetime, $ this ->timeCol = :time WHERE $ this ->idCol = :id " ;
727
+ break ;
728
+ }
729
+
730
+ $ stmt = $ this ->pdo ->prepare ($ sql );
731
+ $ stmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
732
+ $ stmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
733
+ $ stmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
734
+ $ stmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
735
+
736
+ return $ stmt ;
737
+ }
738
+
691
739
/**
692
740
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
693
741
*
@@ -699,18 +747,11 @@ private function getSelectSql()
699
747
*/
700
748
private function getMergeStatement ($ sessionId , $ data , $ maxlifetime )
701
749
{
702
- $ mergeSql = null ;
703
750
switch (true ) {
704
751
case 'mysql ' === $ this ->driver :
705
752
$ mergeSql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) " .
706
753
"ON DUPLICATE KEY UPDATE $ this ->dataCol = VALUES( $ this ->dataCol ), $ this ->lifetimeCol = VALUES( $ this ->lifetimeCol ), $ this ->timeCol = VALUES( $ this ->timeCol ) " ;
707
754
break ;
708
- case 'oci ' === $ this ->driver :
709
- // DUAL is Oracle specific dummy table
710
- $ mergeSql = "MERGE INTO $ this ->table USING DUAL ON ( $ this ->idCol = ?) " .
711
- "WHEN NOT MATCHED THEN INSERT ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (?, ?, ?, ?) " .
712
- "WHEN MATCHED THEN UPDATE SET $ this ->dataCol = ?, $ this ->lifetimeCol = ?, $ this ->timeCol = ? " ;
713
- break ;
714
755
case 'sqlsrv ' === $ this ->driver && version_compare ($ this ->pdo ->getAttribute (\PDO ::ATTR_SERVER_VERSION ), '10 ' , '>= ' ):
715
756
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
716
757
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
@@ -725,29 +766,30 @@ private function getMergeStatement($sessionId, $data, $maxlifetime)
725
766
$ mergeSql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) " .
726
767
"ON CONFLICT ( $ this ->idCol ) DO UPDATE SET ( $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) = (EXCLUDED. $ this ->dataCol , EXCLUDED. $ this ->lifetimeCol , EXCLUDED. $ this ->timeCol ) " ;
727
768
break ;
769
+ default :
770
+ // MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
771
+ return null ;
728
772
}
729
773
730
- if (null !== $ mergeSql ) {
731
- $ mergeStmt = $ this ->pdo ->prepare ($ mergeSql );
732
-
733
- if ('sqlsrv ' === $ this ->driver || 'oci ' === $ this ->driver ) {
734
- $ mergeStmt ->bindParam (1 , $ sessionId , \PDO ::PARAM_STR );
735
- $ mergeStmt ->bindParam (2 , $ sessionId , \PDO ::PARAM_STR );
736
- $ mergeStmt ->bindParam (3 , $ data , \PDO ::PARAM_LOB );
737
- $ mergeStmt ->bindParam (4 , $ maxlifetime , \PDO ::PARAM_INT );
738
- $ mergeStmt ->bindValue (5 , time (), \PDO ::PARAM_INT );
739
- $ mergeStmt ->bindParam (6 , $ data , \PDO ::PARAM_LOB );
740
- $ mergeStmt ->bindParam (7 , $ maxlifetime , \PDO ::PARAM_INT );
741
- $ mergeStmt ->bindValue (8 , time (), \PDO ::PARAM_INT );
742
- } else {
743
- $ mergeStmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
744
- $ mergeStmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
745
- $ mergeStmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
746
- $ mergeStmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
747
- }
748
-
749
- return $ mergeStmt ;
774
+ $ mergeStmt = $ this ->pdo ->prepare ($ mergeSql );
775
+
776
+ if ('sqlsrv ' === $ this ->driver ) {
777
+ $ mergeStmt ->bindParam (1 , $ sessionId , \PDO ::PARAM_STR );
778
+ $ mergeStmt ->bindParam (2 , $ sessionId , \PDO ::PARAM_STR );
779
+ $ mergeStmt ->bindParam (3 , $ data , \PDO ::PARAM_LOB );
780
+ $ mergeStmt ->bindParam (4 , $ maxlifetime , \PDO ::PARAM_INT );
781
+ $ mergeStmt ->bindValue (5 , time (), \PDO ::PARAM_INT );
782
+ $ mergeStmt ->bindParam (6 , $ data , \PDO ::PARAM_LOB );
783
+ $ mergeStmt ->bindParam (7 , $ maxlifetime , \PDO ::PARAM_INT );
784
+ $ mergeStmt ->bindValue (8 , time (), \PDO ::PARAM_INT );
785
+ } else {
786
+ $ mergeStmt ->bindParam (':id ' , $ sessionId , \PDO ::PARAM_STR );
787
+ $ mergeStmt ->bindParam (':data ' , $ data , \PDO ::PARAM_LOB );
788
+ $ mergeStmt ->bindParam (':lifetime ' , $ maxlifetime , \PDO ::PARAM_INT );
789
+ $ mergeStmt ->bindValue (':time ' , time (), \PDO ::PARAM_INT );
750
790
}
791
+
792
+ return $ mergeStmt ;
751
793
}
752
794
753
795
/**
0 commit comments