Skip to content

[Cache] Add PdoTagAwareAdapter #58296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: 7.4
Choose a base branch
from
Prev Previous commit
Next Next commit
Implemented the rest of the drivers and todos
  • Loading branch information
Toflar committed Nov 25, 2024
commit 8bc548b7d5344cdcea1d6c36aef568fdd3d34838
86 changes: 58 additions & 28 deletions src/Symfony/Component/Cache/Adapter/PdoAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,21 @@ public function createTagsTable(): void
// - case-insensitivity
// - language processing like é == e
'mysql' => "CREATE TABLE $this->tagsTable ($this->idCol VARBINARY(255) NOT NULL, $this->tagCol VARBINARY(255) NOT NULL, PRIMARY KEY($this->idCol, $this->tagCol), INDEX idx_id_col ($this->idCol), INDEX idx_tag_col ($this->tagCol)) COLLATE utf8mb4_bin, ENGINE = InnoDB",
'sqlite' => "CREATE TABLE $this->tagsTable ($this->idCol TEXT NOT NULL, $this->tagCol TEXT NOT NULL, PRIMARY KEY ($this->idCol, $this->tagCol));CREATE INDEX idx_id_col ON $this->tagsTable($this->idCol);CREATE INDEX idx_tag_col ON $this->tagsTable($this->tagCol);",
// TODO: Need help for oci, sqlsrv and pgsql here
'sqlite' => "CREATE TABLE $this->tagsTable ($this->idCol TEXT NOT NULL, $this->tagCol TEXT NOT NULL, PRIMARY KEY ($this->idCol, $this->tagCol));CREATE INDEX idx_id_col ON $this->tagsTable($this->idCol);CREATE INDEX idx_tag_col ON $this->tagsTable($this->tagCol)",
'pgsql', 'sqlsrv' => "CREATE TABLE $this->tagsTable ($this->idCol VARCHAR(255) NOT NULL, $this->tagCol VARCHAR(255) NOT NULL, PRIMARY KEY($this->idCol, $this->tagCol);CREATE INDEX idx_id_col ON $this->tagsTable($this->idCol);CREATE INDEX idx_tag_col ON $this->tagsTable($this->tagCol)",
'oci' => "CREATE TABLE $this->tagsTable ($this->idCol VARCHAR2(255) NOT NULL, $this->tagCol VARCHAR2(255) NOT NULL, PRIMARY KEY($this->idCol, $this->tagCol);CREATE INDEX idx_id_col ON $this->tagsTable($this->idCol);CREATE INDEX idx_tag_col ON $this->tagsTable($this->tagCol)",
default => throw new \DomainException(\sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $driver)),
};

$this->getConnection()->exec($sql);
}

public function prune(): bool
{
return $this->pruneExpiredItems() && $this->pruneOrphanedTags();
}

public function pruneExpiredItems(): bool
{
$deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time";

Expand All @@ -183,8 +189,6 @@ public function prune(): bool
} catch (\PDOException) {
return true;
}

$this->pruneOrphanedTags();
}

protected function doFetch(array $ids): iterable
Expand Down Expand Up @@ -288,19 +292,26 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],

switch (true) {
case 'mysql' === $driver:
$sql = $insertSql.' ON DUPLICATE KEY IGNORE';
$sql = $insertSql." ON DUPLICATE KEY UPDATE $this->idCol = VALUES($this->idCol), $this->tagCol = VALUES($this->tagCol)";
break;
case 'oci' === $driver:
throw new LogicException('oci driver support must be added.'); // TODO: I need help here
// DUAL is Oracle specific dummy table
$sql = "MERGE INTO $this->tagsTable USING DUAL ON ($this->idCol = :id, $this->tagCol = :tagId) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->tagCol) VALUES (:id, :tagId) ".
"WHEN MATCHED THEN UPDATE SET $this->idCol = :id, $this->tagCol = :tagId";
break;
case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='):
throw new LogicException('sqlsrv driver support must be added.'); // TODO: I need help here
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
$sql = "MERGE INTO $this->tagsTable WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id, $this->tagCol = :tagId) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->tagCol) VALUES (:id, :tagId) ".
"WHEN MATCHED THEN UPDATE SET $this->idCol = :id, $this->tagCol = :tagId;";
break;
case 'sqlite' === $driver:
$sql = 'INSERT OR REPLACE'.substr($insertSql, 6);
break;
case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='):
throw new LogicException('pgsql driver support must be added.'); // TODO: I need help here
$sql = $insertSql." ON CONFLICT ($this->idCol, $this->tagCol) DO UPDATE SET ($this->idCol, $this->tagCol) = (EXCLUDED.$this->idCol, EXCLUDED.$this->tagCol)";
break;
default:
$driver = null;
Expand All @@ -319,7 +330,10 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],

$stmt->bindParam(':id', $id);
$stmt->bindParam(':tagId', $tagId);
$stmt->execute();

$this->executeStatementWithFallback($stmt, function () {
$this->createTagsTable();
});
} catch (\PDOException $e) {
$failed[] = $id;
}
Expand All @@ -341,7 +355,10 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],

$stmt->bindParam(':id', $id);
$stmt->bindParam(':tagId', $tagId);
$stmt->execute();

$this->executeStatementWithFallback($stmt, function () {
$this->createTagsTable();
});
} catch (\PDOException $e) {
$failed[] = $id;
}
Expand Down Expand Up @@ -393,14 +410,10 @@ protected function doSaveCache(array $values, int $lifetime): array|bool

$now = time();
$lifetime = $lifetime ?: null;
try {
$stmt = $conn->prepare($sql);
} catch (\PDOException $e) {
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
$this->createTable();
}
$stmt = $conn->prepare($sql);
}

$stmt = $this->prepareStatementWithFallback($sql, function () {
$this->createTable();
});

// $id and $data are defined later in the loop. Binding is done by reference, values are read on execution.
if ('sqlsrv' === $driver || 'oci' === $driver) {
Expand Down Expand Up @@ -428,14 +441,10 @@ protected function doSaveCache(array $values, int $lifetime): array|bool
}

foreach ($values as $id => $data) {
try {
$stmt->execute();
} catch (\PDOException $e) {
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
$this->createTable();
}
$stmt->execute();
}
$this->executeStatementWithFallback($stmt, function () {
$this->createTagsTable();
});

if (null === $driver && !$stmt->rowCount()) {
try {
$insertStmt->execute();
Expand Down Expand Up @@ -549,11 +558,32 @@ private function prepareStatementWithFallback(string $query, \Closure $createTab
}
}

private function executeStatementWithFallback(\PDOStatement $statement, \Closure $createTable): void
{
$driver = $this->getDriver();
$conn = $this->getConnection();

try {
$statement->execute();
} catch (\PDOException $e) {
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
$createTable();
}

$statement->execute();
}
}

/**
* Prunes the tags table and removes all tags that are not used anywhere anymore.
*/
private function pruneOrphanedTags(): void
private function pruneOrphanedTags(): bool
{
// TODO: implement me
$conn = $this->getConnection();

$stmt = $conn->prepare("DELETE FROM $this->tagsTable WHERE $this->idCol NOT IN (SELECT $this->idCol FROM $this->table)");
$stmt->execute();

return true;
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy