From 3b38bc4e4e493c12ed15da0529703e513201d7d6 Mon Sep 17 00:00:00 2001 From: sarwar <88341752+p1xel007@users.noreply.github.com> Date: Sun, 4 Jan 2026 10:41:53 +0600 Subject: [PATCH 1/7] feat: Add charset and collation support to PostgreSQLDatabaseManager --- .../TenantDatabaseManagers/PostgreSQLDatabaseManager.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 4fff7202..496ece87 100644 --- a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -10,7 +10,11 @@ class PostgreSQLDatabaseManager extends TenantDatabaseManager { public function createDatabase(TenantWithDatabase $tenant): bool { - return $this->connection()->statement("CREATE DATABASE \"{$tenant->database()->getName()}\" WITH TEMPLATE=template0"); + $database = $tenant->database()->getName(); + $charset = $this->connection()->getConfig('charset'); + $collation = $this->connection()->getConfig('collation'); + + return $this->connection()->statement("CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0 ENCODING='{$charset}' LC_COLLATE='{$collation}' LC_CTYPE='{$collation}'"); } public function deleteDatabase(TenantWithDatabase $tenant): bool From 3b0c1b0988ab2b9fe8de77792f7e0bf85a9c5b1b Mon Sep 17 00:00:00 2001 From: sarwar <88341752+p1xel007@users.noreply.github.com> Date: Sun, 4 Jan 2026 10:58:44 +0600 Subject: [PATCH 2/7] Ensure tenant DB charset is uppercase and database name is validated --- .../TenantDatabaseManagers/PostgreSQLDatabaseManager.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 496ece87..6b2b465e 100644 --- a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -11,10 +11,9 @@ class PostgreSQLDatabaseManager extends TenantDatabaseManager public function createDatabase(TenantWithDatabase $tenant): bool { $database = $tenant->database()->getName(); - $charset = $this->connection()->getConfig('charset'); - $collation = $this->connection()->getConfig('collation'); + $charset = strtoupper($this->connection()->getConfig('charset') ?? 'UTF8'); - return $this->connection()->statement("CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0 ENCODING='{$charset}' LC_COLLATE='{$collation}' LC_CTYPE='{$collation}'"); + return $this->connection()->statement("CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0 ENCODING='{$charset}'"); } public function deleteDatabase(TenantWithDatabase $tenant): bool From 7001e2d161dcd555be723f2092632552fe35f827 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 24 Apr 2026 11:52:18 +0200 Subject: [PATCH 3/7] Only specify `ENCODING=` in `CREATE DATABASE` if charset is configured If the charset is null, Postgres creates the DB with the server's default charset Co-authored-by: Copilot --- .../PostgreSQLDatabaseManager.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 6b2b465e..83bc65d8 100644 --- a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -11,9 +11,16 @@ class PostgreSQLDatabaseManager extends TenantDatabaseManager public function createDatabase(TenantWithDatabase $tenant): bool { $database = $tenant->database()->getName(); - $charset = strtoupper($this->connection()->getConfig('charset') ?? 'UTF8'); + // If null, Postgres creates the DB with the server's default charset + $charset = $this->connection()->getConfig('charset'); - return $this->connection()->statement("CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0 ENCODING='{$charset}'"); + $query = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0"; + + if ($charset !== null) { + $query .= " ENCODING='" . strtoupper($charset) . "'"; + } + + return $this->connection()->statement($query); } public function deleteDatabase(TenantWithDatabase $tenant): bool From d41ac46340f1f5a6c801a4529037e9fd2525c18b Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 24 Apr 2026 12:02:13 +0200 Subject: [PATCH 4/7] Test how the Postgres DB manager handles charsets If DB charset is configured to null, new databases will use the Postgres server's default charset (which is utf8 by default) Co-authored-by: Copilot --- tests/TenantDatabaseManagerTest.php | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index 0d83e70e..ccb5cf64 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -539,6 +539,43 @@ test('partial tenant connection templates get merged into the central connection expect($manager->connection()->getConfig('url'))->toBeNull(); }); +test('newly created postgres databases use the correct charset', function (string|null $charset) { + config([ + 'tenancy.database.managers.pgsql' => PostgreSQLDatabaseManager::class, + 'database.connections.pgsql.charset' => $charset, // If null, Postgres creates the DB with the server's default charset + ]); + + // Purge connection to make sure the updated charset config is used + DB::purge('pgsql'); + + Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) { + return $event->tenant; + })->toListener()); + + $tenant = Tenant::create([ + 'tenancy_db_connection' => 'pgsql', + ]); + + $databaseName = $tenant->database()->getName(); + + // Postgres server's default charset (utf8) + $serverCharset = DB::connection('pgsql') + ->selectOne("SELECT pg_encoding_to_char(encoding) AS encoding FROM pg_database WHERE datname = 'template1'") + ->encoding; + + // Charset of the newly created tenant database + $dbCharset = DB::connection('pgsql') + ->selectOne('SELECT pg_encoding_to_char(encoding) AS encoding FROM pg_database WHERE datname = ?', [$databaseName]) + ->encoding; + + $expectedCharset = $charset !== null ? strtoupper($charset) : $serverCharset; + + expect($dbCharset)->toBe($expectedCharset); +})->with([ + null, + 'utf8', +]); + // Datasets dataset('database_managers', [ ['mysql', MySQLDatabaseManager::class], From 6b0ff48a863af6bebc9b632f493ece4330e50ae7 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 24 Apr 2026 12:15:40 +0200 Subject: [PATCH 5/7] Delete the "utf8" specification in test comment --- tests/TenantDatabaseManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index ccb5cf64..d9dd05c8 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -558,7 +558,7 @@ test('newly created postgres databases use the correct charset', function (strin $databaseName = $tenant->database()->getName(); - // Postgres server's default charset (utf8) + // Postgres server's default charset $serverCharset = DB::connection('pgsql') ->selectOne("SELECT pg_encoding_to_char(encoding) AS encoding FROM pg_database WHERE datname = 'template1'") ->encoding; From e400b28daa21c89181abd3af17057fd380cfa687 Mon Sep 17 00:00:00 2001 From: Samuel Stancl Date: Fri, 1 May 2026 16:21:49 +0200 Subject: [PATCH 6/7] query -> statement, remove unnecessary strtoupper() --- .../TenantDatabaseManagers/PostgreSQLDatabaseManager.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 83bc65d8..6714137b 100644 --- a/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -11,16 +11,17 @@ class PostgreSQLDatabaseManager extends TenantDatabaseManager public function createDatabase(TenantWithDatabase $tenant): bool { $database = $tenant->database()->getName(); + // If null, Postgres creates the DB with the server's default charset $charset = $this->connection()->getConfig('charset'); - $query = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0"; + $statement = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=template0"; if ($charset !== null) { - $query .= " ENCODING='" . strtoupper($charset) . "'"; + $statement .= " ENCODING='{$charset}'"; } - return $this->connection()->statement($query); + return $this->connection()->statement($statement); } public function deleteDatabase(TenantWithDatabase $tenant): bool From c2c2ca2e6a2cfb99a8a9a8701c41d5c13953f56f Mon Sep 17 00:00:00 2001 From: Samuel Stancl Date: Fri, 1 May 2026 16:25:37 +0200 Subject: [PATCH 7/7] use a different encoding than utf8 in tests, more robust assertions Before we were really just testing utf8 twice. With this change tests start failing in a way that makes it seem like passing *just* ENCODING isn't sufficient - if the encoding differs from the server locale we may need to pass LC_COLLATE / LC_CTYPE or some other things? --- tests/TenantDatabaseManagerTest.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index d9dd05c8..870a78f3 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -568,12 +568,17 @@ test('newly created postgres databases use the correct charset', function (strin ->selectOne('SELECT pg_encoding_to_char(encoding) AS encoding FROM pg_database WHERE datname = ?', [$databaseName]) ->encoding; - $expectedCharset = $charset !== null ? strtoupper($charset) : $serverCharset; + if ($charset) { + expect($dbCharset)->toBe(strtoupper($charset)); + } else { + expect($dbCharset)->toBe($serverCharset); - expect($dbCharset)->toBe($expectedCharset); + // Server charset should be UTF8 by default + expect($dbCharset)->toBe('UTF8'); + } })->with([ null, - 'utf8', + 'latin1', ]); // Datasets