From 712d7aabec2ea6b3e7710a33c597c696c72716d0 Mon Sep 17 00:00:00 2001 From: Hayatunnabi Nabil Date: Fri, 7 Nov 2025 15:41:38 +0600 Subject: [PATCH] fix: Refactor database management methods to use parameterized queries and add identifier quoting --- src/Concerns/ManagesRLSPolicies.php | 14 ++++++++++++-- .../MySQLDatabaseManager.php | 18 ++++++++++++++---- .../PostgreSQLDatabaseManager.php | 18 +++++++++++++++--- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/Concerns/ManagesRLSPolicies.php b/src/Concerns/ManagesRLSPolicies.php index 6b804fb7..fb7e4eaa 100644 --- a/src/Concerns/ManagesRLSPolicies.php +++ b/src/Concerns/ManagesRLSPolicies.php @@ -17,18 +17,28 @@ trait ManagesRLSPolicies { return array_map( fn (stdClass $policy) => $policy->policyname, - DB::select("SELECT policyname FROM pg_policies WHERE tablename = '{$table}' AND policyname LIKE '%_rls_policy%'") + DB::select( + "SELECT policyname FROM pg_policies WHERE tablename = ? AND policyname LIKE ?", + [$table, '%_rls_policy%'] + ) ); } public static function dropRLSPolicies(string $table): int { $policies = static::getRLSPolicies($table); + $quotedTable = static::quoteIdentifier($table); foreach ($policies as $policy) { - DB::statement('DROP POLICY ? ON ?', [$policy, $table]); + $quotedPolicy = static::quoteIdentifier($policy); + DB::statement("DROP POLICY {$quotedPolicy} ON {$quotedTable}"); } return count($policies); } + + protected static function quoteIdentifier(string $identifier): string + { + return '"' . str_replace('"', '""', $identifier) . '"'; + } } diff --git a/src/Database/TenantDatabaseManagers/MySQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/MySQLDatabaseManager.php index b86faef2..5db7a6d3 100644 --- a/src/Database/TenantDatabaseManagers/MySQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/MySQLDatabaseManager.php @@ -10,20 +10,30 @@ class MySQLDatabaseManager extends TenantDatabaseManager { public function createDatabase(TenantWithDatabase $tenant): bool { - $database = $tenant->database()->getName(); + $database = $this->quoteIdentifier($tenant->database()->getName()); $charset = $this->connection()->getConfig('charset'); $collation = $this->connection()->getConfig('collation'); - return $this->connection()->statement("CREATE DATABASE `{$database}` CHARACTER SET `$charset` COLLATE `$collation`"); + return $this->connection()->statement("CREATE DATABASE {$database} CHARACTER SET `$charset` COLLATE `$collation`"); } public function deleteDatabase(TenantWithDatabase $tenant): bool { - return $this->connection()->statement("DROP DATABASE `{$tenant->database()->getName()}`"); + $database = $this->quoteIdentifier($tenant->database()->getName()); + + return $this->connection()->statement("DROP DATABASE {$database}"); } public function databaseExists(string $name): bool { - return (bool) $this->connection()->select("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$name'"); + return (bool) $this->connection()->selectOne( + 'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ? LIMIT 1', + [$name] + ); + } + + protected function quoteIdentifier(string $identifier): string + { + return '`' . str_replace('`', '``', $identifier) . '`'; } } diff --git a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 4fff7202..6290c832 100644 --- a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -10,16 +10,28 @@ class PostgreSQLDatabaseManager extends TenantDatabaseManager { public function createDatabase(TenantWithDatabase $tenant): bool { - return $this->connection()->statement("CREATE DATABASE \"{$tenant->database()->getName()}\" WITH TEMPLATE=template0"); + $database = $this->quoteIdentifier($tenant->database()->getName()); + + return $this->connection()->statement("CREATE DATABASE {$database} WITH TEMPLATE=template0"); } public function deleteDatabase(TenantWithDatabase $tenant): bool { - return $this->connection()->statement("DROP DATABASE \"{$tenant->database()->getName()}\""); + $database = $this->quoteIdentifier($tenant->database()->getName()); + + return $this->connection()->statement("DROP DATABASE {$database}"); } public function databaseExists(string $name): bool { - return (bool) $this->connection()->selectOne("SELECT datname FROM pg_database WHERE datname = '$name'"); + return (bool) $this->connection()->selectOne( + 'SELECT datname FROM pg_database WHERE datname = ? LIMIT 1', + [$name] + ); + } + + protected function quoteIdentifier(string $identifier): string + { + return '"' . str_replace('"', '""', $identifier) . '"'; } }