bug #19048 [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5 (nicolas-grekas)
This PR was merged into the 2.7 branch.
Discussion
----------
[HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5
| Q | A
| ------------- | ---
| Branch? | 2.7
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
Commits
-------
9569c74
[HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5
This commit is contained in:
commit
347cd8712e
@ -180,7 +180,7 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
$updateStmt->execute();
|
$updateStmt->execute();
|
||||||
|
|
||||||
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
|
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
|
||||||
// duplicate key errors when the same session is written simultaneously. We can just catch such an
|
// duplicate key errors when the same session is written simultaneously. We can just catch such an
|
||||||
// error and re-execute the update. This is similar to a serializable transaction with retry logic
|
// error and re-execute the update. This is similar to a serializable transaction with retry logic
|
||||||
// on serialization failures but without the overhead and without possible false positives due to
|
// on serialization failures but without the overhead and without possible false positives due to
|
||||||
@ -224,11 +224,11 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
{
|
{
|
||||||
$platform = $this->con->getDatabasePlatform()->getName();
|
$platform = $this->con->getDatabasePlatform()->getName();
|
||||||
|
|
||||||
switch ($platform) {
|
switch (true) {
|
||||||
case 'mysql':
|
case 'mysql' === $platform:
|
||||||
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
|
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
case 'oracle':
|
case 'oracle' === $platform:
|
||||||
// DUAL is Oracle specific dummy table
|
// DUAL is Oracle specific dummy table
|
||||||
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
@ -239,8 +239,11 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
|
||||||
case 'sqlite':
|
case 'sqlite' === $platform:
|
||||||
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
|
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
|
||||||
|
case 'postgresql' === $platform && version_compare($this->con->getServerVersion(), '9.5', '>='):
|
||||||
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
|
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->timeCol) = (:data, :time) WHERE $this->idCol = :id";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
$updateStmt->execute();
|
$updateStmt->execute();
|
||||||
|
|
||||||
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
|
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
|
||||||
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
|
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
|
||||||
// We can just catch such an error and re-execute the update. This is similar to a serializable
|
// We can just catch such an error and re-execute the update. This is similar to a serializable
|
||||||
// transaction with retry logic on serialization failures but without the overhead and without possible
|
// transaction with retry logic on serialization failures but without the overhead and without possible
|
||||||
@ -659,11 +659,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
*/
|
*/
|
||||||
private function getMergeSql()
|
private function getMergeSql()
|
||||||
{
|
{
|
||||||
switch ($this->driver) {
|
switch (true) {
|
||||||
case 'mysql':
|
case 'mysql' === $this->driver:
|
||||||
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
case 'oci':
|
case 'oci' === $this->driver:
|
||||||
// DUAL is Oracle specific dummy table
|
// DUAL is Oracle specific dummy table
|
||||||
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
@ -674,8 +674,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
|
||||||
case 'sqlite':
|
case 'sqlite' === $this->driver:
|
||||||
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
||||||
|
case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
|
||||||
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
|
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (:data, :lifetime, :time) WHERE $this->idCol = :id";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user