@@ -330,13 +330,7 @@ public function write($sessionId, $data)
330
330
return true ;
331
331
}
332
332
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 );
340
334
$ updateStmt ->execute ();
341
335
342
336
// 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)
346
340
// false positives due to longer gap locking.
347
341
if (!$ updateStmt ->rowCount ()) {
348
342
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 );
356
344
$ insertStmt ->execute ();
357
345
} catch (\PDOException $ e ) {
358
346
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
@@ -521,13 +509,7 @@ private function doRead($sessionId)
521
509
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
522
510
// until other connections to the session are committed.
523
511
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 );
531
513
$ insertStmt ->execute ();
532
514
} catch (\PDOException $ e ) {
533
515
// Catch duplicate key error because other connection created the session already.
@@ -662,6 +644,72 @@ private function getSelectSql()
662
644
return "SELECT $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol FROM $ this ->table WHERE $ this ->idCol = :id " ;
663
645
}
664
646
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
+
665
713
/**
666
714
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
667
715
*
@@ -673,18 +721,11 @@ private function getSelectSql()
673
721
*/
674
722
private function getMergeStatement ($ sessionId , $ data , $ maxlifetime )
675
723
{
676
- $ mergeSql = null ;
677
724
switch (true ) {
678
725
case 'mysql ' === $ this ->driver :
679
726
$ mergeSql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) " .
680
727
"ON DUPLICATE KEY UPDATE $ this ->dataCol = VALUES( $ this ->dataCol ), $ this ->lifetimeCol = VALUES( $ this ->lifetimeCol ), $ this ->timeCol = VALUES( $ this ->timeCol ) " ;
681
728
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 ;
688
729
case 'sqlsrv ' === $ this ->driver && version_compare ($ this ->pdo ->getAttribute (\PDO ::ATTR_SERVER_VERSION ), '10 ' , '>= ' ):
689
730
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
690
731
// 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)
699
740
$ mergeSql = "INSERT INTO $ this ->table ( $ this ->idCol , $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) VALUES (:id, :data, :lifetime, :time) " .
700
741
"ON CONFLICT ( $ this ->idCol ) DO UPDATE SET ( $ this ->dataCol , $ this ->lifetimeCol , $ this ->timeCol ) = (EXCLUDED. $ this ->dataCol , EXCLUDED. $ this ->lifetimeCol , EXCLUDED. $ this ->timeCol ) " ;
701
742
break ;
743
+ default :
744
+ // MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
745
+ return null ;
702
746
}
703
747
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 );
724
764
}
765
+
766
+ return $ mergeStmt ;
725
767
}
726
768
727
769
/**
0 commit comments