From d788391f4a2e3fcdfdfd3d75a1e7973e41dd77cb Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sat, 14 Feb 2026 15:40:46 +0100 Subject: [PATCH] Refactor fragile SQLite regex rewrites into driver-aware query methods Move the ON DUPLICATE KEY UPDATE and UPDATE...AS alias SQL rewrites out of Database::rewriteForSQLite() and into Tag::createNew() and Tag::recount() as driver-aware branches via a new Database::getDriver() method. This keeps dialect-specific SQL explicit at the call site rather than buried in fragile regex transforms. Also fix Tag::getAlbums() and Tag::getPeople() failing on SQLite when id_parent is NULL by adding an IS NULL fallback for root-level queries. Co-Authored-By: Claude Opus 4.6 --- models/Database.php | 32 +++++++--------------------- models/Tag.php | 51 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/models/Database.php b/models/Database.php index 0fd37d9..d591d34 100644 --- a/models/Database.php +++ b/models/Database.php @@ -73,36 +73,18 @@ class Database }, 0); } + public function getDriver() + { + return $this->driver; + } + private function rewriteForSQLite($sql) { // REPLACE INTO → INSERT OR REPLACE INTO $sql = preg_replace('/\bREPLACE\s+INTO\b/i', 'INSERT OR REPLACE INTO', $sql); - // ON DUPLICATE KEY UPDATE → ON CONFLICT(slug) DO UPDATE SET - // When present, strip INSERT IGNORE down to INSERT (OR IGNORE conflicts with ON CONFLICT). - if (preg_match('/\bON\s+DUPLICATE\s+KEY\s+UPDATE\b/i', $sql)) - { - $sql = preg_replace('/\bINSERT\s+IGNORE\s+INTO\b/i', 'INSERT INTO', $sql); - $sql = preg_replace( - '/\bON\s+DUPLICATE\s+KEY\s+UPDATE\s+(.+)/i', - 'ON CONFLICT(slug) DO UPDATE SET $1', - $sql - ); - } - else - { - // INSERT IGNORE INTO → INSERT OR IGNORE INTO - $sql = preg_replace('/\bINSERT\s+IGNORE\s+INTO\b/i', 'INSERT OR IGNORE INTO', $sql); - } - - // UPDATE table AS alias SET ... → UPDATE table SET ... (with alias replaced by table name) - if (preg_match('/\bUPDATE\s+(\w+)\s+AS\s+(\w+)\s+SET\b/i', $sql, $m)) - { - $table = $m[1]; - $alias = $m[2]; - $sql = preg_replace('/\bUPDATE\s+\w+\s+AS\s+\w+\s+SET\b/i', "UPDATE $table SET", $sql); - $sql = preg_replace('/\b' . preg_quote($alias, '/') . '\.(\w+)\b/', "$table.$1", $sql); - } + // INSERT IGNORE INTO → INSERT OR IGNORE INTO + $sql = preg_replace('/\bINSERT\s+IGNORE\s+INTO\b/i', 'INSERT OR IGNORE INTO', $sql); // LIMIT :offset, :limit → LIMIT :limit OFFSET :offset $sql = preg_replace( diff --git a/models/Tag.php b/models/Tag.php index 3ef392d..782e10c 100644 --- a/models/Tag.php +++ b/models/Tag.php @@ -122,10 +122,12 @@ class Tag public static function getAlbums($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array') { + $parent_clause = empty($id_parent) ? '(id_parent = :id_parent OR id_parent IS NULL)' : 'id_parent = :id_parent'; + $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags - WHERE id_parent = :id_parent AND kind = :kind + WHERE ' . $parent_clause . ' AND kind = :kind ORDER BY tag ASC LIMIT :offset, :limit', [ @@ -163,10 +165,12 @@ class Tag public static function getPeople($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array') { + $parent_clause = empty($id_parent) ? '(id_parent = :id_parent OR id_parent IS NULL)' : 'id_parent = :id_parent'; + $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags - WHERE id_parent = :id_parent AND kind = :kind + WHERE ' . $parent_clause . ' AND kind = :kind ORDER BY tag ASC LIMIT :offset, :limit', [ @@ -249,7 +253,21 @@ class Tag public static function recount(array $id_tags = []) { - return Registry::get('db')->query(' + $db = Registry::get('db'); + + if ($db->getDriver() === 'sqlite') + { + return $db->query(' + UPDATE tags SET count = ( + SELECT COUNT(*) + FROM `assets_tags` AS at + WHERE at.id_tag = tags.id_tag + )' . (!empty($id_tags) ? ' + WHERE tags.id_tag IN(@id_tags)' : ''), + ['id_tags' => $id_tags]); + } + + return $db->query(' UPDATE tags AS t SET count = ( SELECT COUNT(*) FROM `assets_tags` AS at @@ -272,13 +290,26 @@ class Tag if (!isset($data['count'])) $data['count'] = 0; - $res = $db->query(' - INSERT IGNORE INTO tags - (id_parent, tag, slug, kind, description, count) - VALUES - (:id_parent, :tag, :slug, :kind, :description, :count) - ON DUPLICATE KEY UPDATE count = count + 1', - $data); + if ($db->getDriver() === 'sqlite') + { + $res = $db->query(' + INSERT INTO tags + (id_parent, tag, slug, kind, description, count) + VALUES + (:id_parent, :tag, :slug, :kind, :description, :count) + ON CONFLICT(slug) DO UPDATE SET count = count + 1', + $data); + } + else + { + $res = $db->query(' + INSERT IGNORE INTO tags + (id_parent, tag, slug, kind, description, count) + VALUES + (:id_parent, :tag, :slug, :kind, :description, :count) + ON DUPLICATE KEY UPDATE count = count + 1', + $data); + } if (!$res) throw new Exception('Could not create the requested tag.');