From e3b6cebf279e6c495233cbb8c585ba2120dd8a95 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 14 Sep 2026 15:31:38 +0930 Subject: [PATCH] Fix MariaDB driver detection and reblog caption null inserts Laravel 11 exposes MariaDB as a dedicated 'mariadb' driver, so config('database.default') === 'mysql' checks silently misclassified MariaDB as the non-mysql (postgres) branch. - Add App\Util\Database\DatabaseDriver with isMysqlLike()/isPgsql() plus db_is_mysql_like()/db_is_pgsql() global helpers. - Route all database.default driver checks through the helpers so MySQL and MariaDB are treated as one group. - Use '' (not null) for share/compose caption+rendered, valid whether the column is nullable or NOT NULL (it is NOT NULL on MySQL/MariaDB). - Guard pgsql strtolower() in registration against missing fields. - Scope CustomEmoji::duplicateShortcodes to the grouped column for Postgres GROUP BY validity. - Remove stale Postgres guard in status:dedup; use havingRaw for cross-driver HAVING. --- .../Commands/Deprecated/StatusDedupe.php | 7 +-- .../InstanceUpdateTotalLocalPosts.php | 2 +- .../Admin/AdminAutospamController.php | 5 +- .../Admin/AdminReportController.php | 4 +- .../Admin/AdminSettingsController.php | 6 +- app/Http/Controllers/AdminController.php | 2 +- app/Http/Controllers/Api/ApiV1Controller.php | 9 ++- .../Controllers/Api/ApiV1Dot1Controller.php | 2 +- .../Controllers/Api/V1/TagsController.php | 6 +- .../Controllers/Auth/RegisterController.php | 20 +++++-- app/Http/Controllers/ComposeController.php | 13 +++-- .../Controllers/DirectMessageController.php | 2 +- app/Http/Controllers/DiscoverController.php | 2 +- app/Http/Controllers/PlaceController.php | 2 +- app/Http/Controllers/SearchController.php | 2 +- app/Http/Controllers/SeasonalController.php | 6 +- app/Http/Controllers/StatusController.php | 9 +-- .../Stories/StoryApiV1Controller.php | 4 +- app/Http/Controllers/StoryController.php | 2 +- app/Jobs/CommentPipeline/CommentPipeline.php | 2 +- .../StatusPipeline/StatusReplyPipeline.php | 2 +- .../StatusPipeline/StatusTagsPipeline.php | 2 +- app/Models/CustomEmoji.php | 5 +- app/Services/AdminStatsService.php | 4 +- app/Services/CustomEmojiService.php | 2 +- app/Services/DiscoverService.php | 2 +- app/Services/SearchApiV2Service.php | 4 +- app/Util/Database/DatabaseDriver.php | 57 +++++++++++++++++++ app/helpers.php | 24 ++++++++ 29 files changed, 151 insertions(+), 58 deletions(-) create mode 100644 app/Util/Database/DatabaseDriver.php diff --git a/app/Console/Commands/Deprecated/StatusDedupe.php b/app/Console/Commands/Deprecated/StatusDedupe.php index 778057397..a505b6671 100644 --- a/app/Console/Commands/Deprecated/StatusDedupe.php +++ b/app/Console/Commands/Deprecated/StatusDedupe.php @@ -41,11 +41,6 @@ class StatusDedupe extends Command public function handle() { - if (config('database.default') == 'pgsql') { - $this->info('This command is not compatible with Postgres, we are working on a fix.'); - - return; - } // Deterministically keep the earliest-fetched status per uri via // MIN(id). Selecting a non-aggregated id under GROUP BY is // nondeterministic and cannot be influenced by ORDER BY, so the @@ -55,7 +50,7 @@ class StatusDedupe extends Command ->whereNull('deleted_at') ->whereNotNull('uri') ->groupBy('uri') - ->having('occurences', '>', 1) + ->havingRaw('count(uri) > 1') ->orderBy('uri') ->chunk(50, function ($statuses) { foreach ($statuses as $status) { diff --git a/app/Console/Commands/Internal/InstanceUpdateTotalLocalPosts.php b/app/Console/Commands/Internal/InstanceUpdateTotalLocalPosts.php index c318b78c5..3bd00345a 100644 --- a/app/Console/Commands/Internal/InstanceUpdateTotalLocalPosts.php +++ b/app/Console/Commands/Internal/InstanceUpdateTotalLocalPosts.php @@ -77,7 +77,7 @@ class InstanceUpdateTotalLocalPosts extends Command protected function getTotalLocalPosts() { - if ((bool) config('instance.total_count_estimate') && config('database.default') === 'mysql') { + if ((bool) config('instance.total_count_estimate') && db_is_mysql_maria()) { return DB::select("EXPLAIN SELECT COUNT(*) FROM statuses WHERE deleted_at IS NULL AND uri IS NULL and local = 1 AND type != 'share'")[0]->rows; } diff --git a/app/Http/Controllers/Admin/AdminAutospamController.php b/app/Http/Controllers/Admin/AdminAutospamController.php index c33176b82..c9f580ef2 100644 --- a/app/Http/Controllers/Admin/AdminAutospamController.php +++ b/app/Http/Controllers/Admin/AdminAutospamController.php @@ -36,8 +36,9 @@ trait AdminAutospamController }); $thisWeek = Cache::remember('admin-dash:reports:spam-count-stats-this-week ', 86400, function () { - $sr = config('database.default') == 'pgsql' ? "to_char(created_at, 'MM-YYYY')" : "DATE_FORMAT(created_at, '%m-%Y')"; - $gb = config('database.default') == 'pgsql' ? [DB::raw($sr)] : DB::raw($sr); + $isPgsql = db_is_pgsql(); + $sr = $isPgsql ? "to_char(created_at, 'MM-YYYY')" : "DATE_FORMAT(created_at, '%m-%Y')"; + $gb = $isPgsql ? [DB::raw($sr)] : DB::raw($sr); $s = AccountInterstitial::select( DB::raw('count(id) as count'), DB::raw($sr.' as month_year') diff --git a/app/Http/Controllers/Admin/AdminReportController.php b/app/Http/Controllers/Admin/AdminReportController.php index d20ddc0d9..96b183a24 100644 --- a/app/Http/Controllers/Admin/AdminReportController.php +++ b/app/Http/Controllers/Admin/AdminReportController.php @@ -141,7 +141,7 @@ trait AdminReportController }); $avg = Cache::remember('admin-dash:reports:spam-count:avg', 43200, function () { - if (config('database.default') != 'mysql') { + if (! db_is_mysql_maria()) { return 0; } @@ -153,7 +153,7 @@ trait AdminReportController }); $avgOpen = Cache::remember('admin-dash:reports:spam-count:avgopen', 43200, function () { - if (config('database.default') != 'mysql') { + if (! db_is_mysql_maria()) { return '0'; } $seconds = AccountInterstitial::selectRaw('DATE(created_at) AS start_date, AVG(TIME_TO_SEC(TIMEDIFF(appeal_handled_at, created_at))) AS timediff')->whereType('post.autospam')->whereNotNull('appeal_handled_at')->where('created_at', '>', now()->subMonth())->get(); diff --git a/app/Http/Controllers/Admin/AdminSettingsController.php b/app/Http/Controllers/Admin/AdminSettingsController.php index 418bad1a8..a2c580737 100644 --- a/app/Http/Controllers/Admin/AdminSettingsController.php +++ b/app/Http/Controllers/Admin/AdminSettingsController.php @@ -299,11 +299,13 @@ trait AdminSettingsController break; case 'mysql': + case 'mariadb': $exp = DB::raw('select version()'); $expQuery = $exp->getValue(DB::connection()->getQueryGrammar()); + $version = DB::select($expQuery)[0]->{'version()'}; $sys['database'] = [ - 'name' => 'MySQL', - 'version' => DB::select($expQuery)[0]->{'version()'}, + 'name' => stripos($version, 'mariadb') !== false ? 'MariaDB' : 'MySQL', + 'version' => $version, ]; break; diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index b5fa30637..f058b1931 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -553,7 +553,7 @@ class AdminController extends Controller return redirect(route('admin.custom-emoji')); } - $pg = config('database.default') == 'pgsql'; + $pg = db_is_pgsql(); $emojis = CustomEmoji::when($sort, function ($query, $sort) use ($request, $pg) { if ($sort == 'all') { diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 496e414e8..c7a73a472 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -3327,7 +3327,7 @@ class ApiV1Controller extends Controller $pid = $user->profile_id; - $isPgsql = config('database.default') == 'pgsql'; + $isPgsql = db_is_pgsql(); if ($isPgsql) { $dms = DirectMessage::when($scope === 'inbox', function ($q) use ($pid) { @@ -4126,10 +4126,9 @@ class ApiV1Controller extends Controller } } - $defaultCaption = config_cache('database.default') === 'mysql' ? null : ''; $share = Status::firstOrCreate([ - 'caption' => $defaultCaption, - 'rendered' => $defaultCaption, + 'caption' => '', + 'rendered' => '', 'profile_id' => $user->profile_id, 'reblog_of_id' => $status->id, 'type' => 'share', @@ -4230,7 +4229,7 @@ class ApiV1Controller extends Controller 'Invalid permissions for this action' ); - if (config('database.default') === 'pgsql') { + if (db_is_pgsql()) { $tag = Hashtag::where('name', 'ilike', $hashtag) ->orWhere('slug', 'ilike', $hashtag) ->first(); diff --git a/app/Http/Controllers/Api/ApiV1Dot1Controller.php b/app/Http/Controllers/Api/ApiV1Dot1Controller.php index d85890389..178f6f075 100644 --- a/app/Http/Controllers/Api/ApiV1Dot1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Dot1Controller.php @@ -561,7 +561,7 @@ class ApiV1Dot1Controller extends Controller $username = $request->input('username'); $password = $request->input('password'); - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { $username = strtolower($username); $email = strtolower($email); } diff --git a/app/Http/Controllers/Api/V1/TagsController.php b/app/Http/Controllers/Api/V1/TagsController.php index d9fe6af7b..99ab4615b 100644 --- a/app/Http/Controllers/Api/V1/TagsController.php +++ b/app/Http/Controllers/Api/V1/TagsController.php @@ -52,7 +52,7 @@ class TagsController extends Controller $pid = $request->user()->profile_id; $account = AccountService::get($pid); - $operator = config('database.default') == 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $tag = Hashtag::where('name', $operator, $id) ->orWhere('slug', $operator, $id) ->first(); @@ -94,7 +94,7 @@ class TagsController extends Controller $pid = $request->user()->profile_id; $account = AccountService::get($pid); - $operator = config('database.default') == 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $tag = Hashtag::where('name', $operator, $id) ->orWhere('slug', $operator, $id) ->first(); @@ -139,7 +139,7 @@ class TagsController extends Controller $pid = $request->user()->profile_id; $account = AccountService::get($pid); - $operator = config('database.default') == 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $tag = Hashtag::where('name', $operator, $id) ->orWhere('slug', $operator, $id) ->first(); diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 687cac01f..fcfbefb0f 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -59,9 +59,13 @@ class RegisterController extends Controller */ public function validator(array $data) { - if (config('database.default') == 'pgsql') { - $data['username'] = strtolower($data['username']); - $data['email'] = strtolower($data['email']); + if (db_is_pgsql()) { + if (isset($data['username'])) { + $data['username'] = strtolower($data['username']); + } + if (isset($data['email'])) { + $data['email'] = strtolower($data['email']); + } } $usernameRules = [ @@ -109,9 +113,13 @@ class RegisterController extends Controller */ public function create(array $data) { - if (config('database.default') == 'pgsql') { - $data['username'] = strtolower($data['username']); - $data['email'] = strtolower($data['email']); + if (db_is_pgsql()) { + if (isset($data['username'])) { + $data['username'] = strtolower($data['username']); + } + if (isset($data['email'])) { + $data['email'] = strtolower($data['email']); + } } return User::create([ diff --git a/app/Http/Controllers/ComposeController.php b/app/Http/Controllers/ComposeController.php index 2cf59daa2..a477ae34c 100644 --- a/app/Http/Controllers/ComposeController.php +++ b/app/Http/Controllers/ComposeController.php @@ -265,7 +265,7 @@ class ComposeController extends Controller $blocked = UserFilterService::searchExcludedProfileIds($request->user()->profile_id); - $operator = config('database.default') === 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $results = Profile::select([ 'profiles.id', 'profiles.domain', @@ -354,7 +354,7 @@ class ComposeController extends Controller $popular = Cache::remember('pf:search:location:v1:popular', 1209600, function () { $minId = SnowflakeService::byDate(now()->subDays(290)); - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { return Status::selectRaw('id, place_id, count(place_id) as pc') ->whereNotNull('place_id') ->where('id', '>', $minId) @@ -393,7 +393,7 @@ class ComposeController extends Controller }); }); - $wildcard = config('database.default') === 'pgsql' ? 'ilike' : 'like'; + $wildcard = db_is_pgsql() ? 'ilike' : 'like'; $q = '%'.$raw.'%'; $placesQuery = DB::table('places')->where('name', $wildcard, $q); @@ -453,7 +453,7 @@ class ComposeController extends Controller $blocked = UserFilterService::searchExcludedProfileIds($request->user()->profile_id); $currentUserId = $request->user()->profile_id; - $operator = config('database.default') === 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $results = Profile::select([ 'profiles.id', @@ -730,7 +730,10 @@ class ComposeController extends Controller $place = $request->input('place'); $cw = $request->input('cw'); $tagged = $request->input('tagged'); - $defaultCaption = config_cache('database.default') === 'mysql' ? null : ''; + // Empty string is valid whether `caption`/`rendered` are nullable or + // NOT NULL (they are NOT NULL on MySQL/MariaDB in practice), so use it + // regardless of driver rather than inserting null. + $defaultCaption = ''; if ($place && is_array($place)) { $status->place_id = $place['id']; diff --git a/app/Http/Controllers/DirectMessageController.php b/app/Http/Controllers/DirectMessageController.php index 22b0a2cfb..f1120310a 100644 --- a/app/Http/Controllers/DirectMessageController.php +++ b/app/Http/Controllers/DirectMessageController.php @@ -59,7 +59,7 @@ class DirectMessageController extends Controller 'is_hidden', 'meta', 'created_at', 'read_at' )->with(['author', 'status', 'recipient']); - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { $query = match ($action) { 'inbox' => $baseQuery->whereToId($profile) ->whereIsHidden(false) diff --git a/app/Http/Controllers/DiscoverController.php b/app/Http/Controllers/DiscoverController.php index 2fdf8f3b5..29c0c2297 100644 --- a/app/Http/Controllers/DiscoverController.php +++ b/app/Http/Controllers/DiscoverController.php @@ -66,7 +66,7 @@ class DiscoverController extends Controller $end = $page > 1 ? $page * 9 : (($page * 9) + 9); $tag = $request->input('hashtag'); - if (config('database.default') === 'pgsql') { + if (db_is_pgsql()) { $hashtag = Hashtag::where('name', 'ilike', $tag)->firstOrFail(); } else { $hashtag = Hashtag::whereName($tag)->firstOrFail(); diff --git a/app/Http/Controllers/PlaceController.php b/app/Http/Controllers/PlaceController.php index e2f722c8c..bab82afbc 100644 --- a/app/Http/Controllers/PlaceController.php +++ b/app/Http/Controllers/PlaceController.php @@ -46,7 +46,7 @@ class PlaceController extends Controller public function directoryCities(Request $request, $country): View { $country = urldecode($country); - $operator = config('database.default') === 'pgsql' ? 'ilike' : '='; + $operator = db_is_pgsql() ? 'ilike' : '='; $places = Place::where('country', $operator, $country) ->orderBy('name', 'asc') diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index d6af72652..d284703b0 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -37,7 +37,7 @@ class SearchController extends Controller */ protected function likeOperator(): string { - return config('database.default') === 'pgsql' ? 'ilike' : 'like'; + return db_is_pgsql() ? 'ilike' : 'like'; } public function searchAPI(Request $request): JsonResponse diff --git a/app/Http/Controllers/SeasonalController.php b/app/Http/Controllers/SeasonalController.php index dff14b004..ee98fbbb8 100644 --- a/app/Http/Controllers/SeasonalController.php +++ b/app/Http/Controllers/SeasonalController.php @@ -25,7 +25,7 @@ class SeasonalController extends Controller public function yearInReview(): View { abort_if(now()->gt('2021-03-01 00:00:00'), 404); - abort_if(config('database.default') != 'mysql', 404); + abort_if(! db_is_mysql_maria(), 404); $profile = Auth::user()->profile; @@ -35,7 +35,7 @@ class SeasonalController extends Controller public function getData(Request $request): JsonResponse { abort_if(now()->gt('2021-03-01 00:00:00'), 404); - abort_if(config('database.default') != 'mysql', 404); + abort_if(! db_is_mysql_maria(), 404); $uid = $request->user()->id; $pid = $request->user()->profile_id; @@ -227,7 +227,7 @@ class SeasonalController extends Controller public function store(Request $request): JsonResponse { abort_if(now()->gt('2021-03-01 00:00:00'), 404); - abort_if(config('database.default') != 'mysql', 404); + abort_if(! db_is_mysql_maria(), 404); $user = $request->user(); diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 2b2d78252..999dc2a42 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -221,11 +221,12 @@ class StatusController extends Controller } ReblogService::del($profile->id, $status->id); } else { - $defaultCaption = config_cache('database.default') === 'mysql' ? null : ''; - + // A share carries no caption. Empty string is valid whether the + // column is nullable or NOT NULL (it is NOT NULL on MySQL/MariaDB), + // so use it regardless of driver rather than inserting null. $share = new Status; - $share->caption = $defaultCaption; - $share->rendered = $defaultCaption; + $share->caption = ''; + $share->rendered = ''; $share->profile_id = $profile->id; $share->reblog_of_id = $status->id; $share->in_reply_to_profile_id = $status->profile_id; diff --git a/app/Http/Controllers/Stories/StoryApiV1Controller.php b/app/Http/Controllers/Stories/StoryApiV1Controller.php index 19a19baeb..b5178ac38 100644 --- a/app/Http/Controllers/Stories/StoryApiV1Controller.php +++ b/app/Http/Controllers/Stories/StoryApiV1Controller.php @@ -42,7 +42,7 @@ class StoryApiV1Controller extends Controller abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $pid = $request->user()->profile_id; - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') @@ -164,7 +164,7 @@ class StoryApiV1Controller extends Controller abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $pid = $request->user()->profile_id; - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') diff --git a/app/Http/Controllers/StoryController.php b/app/Http/Controllers/StoryController.php index 00437a587..1b86f8dd6 100644 --- a/app/Http/Controllers/StoryController.php +++ b/app/Http/Controllers/StoryController.php @@ -34,7 +34,7 @@ class StoryController extends StoryComposeController } $pid = $user->profile_id; - if (config('database.default') == 'pgsql') { + if (db_is_pgsql()) { $s = Cache::remember('pf:stories:recent-by-id:'.$pid, 900, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') diff --git a/app/Jobs/CommentPipeline/CommentPipeline.php b/app/Jobs/CommentPipeline/CommentPipeline.php index 885fc3a1d..0bf0bc728 100644 --- a/app/Jobs/CommentPipeline/CommentPipeline.php +++ b/app/Jobs/CommentPipeline/CommentPipeline.php @@ -83,7 +83,7 @@ class CommentPipeline implements ShouldQueue return; } - if (config('database.default') === 'mysql') { + if (db_is_mysql_maria()) { // todo: refactor // $exp = DB::raw("select id, in_reply_to_id from statuses, (select @pv := :kid) initialisation where id > @pv and find_in_set(in_reply_to_id, @pv) > 0 and @pv := concat(@pv, ',', id)"); // $expQuery = $exp->getValue(DB::connection()->getQueryGrammar()); diff --git a/app/Jobs/StatusPipeline/StatusReplyPipeline.php b/app/Jobs/StatusPipeline/StatusReplyPipeline.php index 88fd9c897..baff984ac 100644 --- a/app/Jobs/StatusPipeline/StatusReplyPipeline.php +++ b/app/Jobs/StatusPipeline/StatusReplyPipeline.php @@ -97,7 +97,7 @@ class StatusReplyPipeline implements ShouldQueue return 1; } - if (config('database.default') === 'mysql') { + if (db_is_mysql_maria()) { // todo: refactor // $exp = DB::raw("select id, in_reply_to_id from statuses, (select @pv := :kid) initialisation where id > @pv and find_in_set(in_reply_to_id, @pv) > 0 and @pv := concat(@pv, ',', id)"); // $expQuery = $exp->getValue(DB::connection()->getQueryGrammar()); diff --git a/app/Jobs/StatusPipeline/StatusTagsPipeline.php b/app/Jobs/StatusPipeline/StatusTagsPipeline.php index 681ae9948..d882c9444 100644 --- a/app/Jobs/StatusPipeline/StatusTagsPipeline.php +++ b/app/Jobs/StatusPipeline/StatusTagsPipeline.php @@ -93,7 +93,7 @@ class StatusTagsPipeline implements ShouldQueue } } - if (config('database.default') === 'pgsql') { + if (db_is_pgsql()) { $hashtag = DB::transaction(function () use ($name) { $slug = Str::slug($name, '-', false); diff --git a/app/Models/CustomEmoji.php b/app/Models/CustomEmoji.php index fed510273..b69016622 100644 --- a/app/Models/CustomEmoji.php +++ b/app/Models/CustomEmoji.php @@ -22,7 +22,10 @@ class CustomEmoji extends Model */ public function scopeDuplicateShortcodes($query) { - return $query->groupBy('shortcode')->havingRaw('count(*) > 1'); + // Select only the grouped column so the aggregate is valid on + // Postgres (a bare `select *` with GROUP BY is rejected because + // non-grouped columns must appear in GROUP BY or an aggregate). + return $query->select('shortcode')->groupBy('shortcode')->havingRaw('count(*) > 1'); } public static function scan($text, $activitypub = false) diff --git a/app/Services/AdminStatsService.php b/app/Services/AdminStatsService.php index 0b7cf9ae4..bf17b3a52 100644 --- a/app/Services/AdminStatsService.php +++ b/app/Services/AdminStatsService.php @@ -75,7 +75,7 @@ class AdminStatsService protected static function recentData() { - $day = config('database.default') == 'pgsql' ? 'DATE_PART(\'day\',' : 'day('; + $day = db_is_pgsql() ? 'DATE_PART(\'day\',' : 'day('; $ttl = now()->addMinutes(15); return Cache::remember('admin:dashboard:home:data:v0:15min', $ttl, function () { @@ -126,7 +126,7 @@ class AdminStatsService $ttl = now()->addHours(12); return Cache::remember('admin:dashboard:home:data-postsGraph:v0.1:24hr', $ttl, function () { - $gb = config('database.default') == 'pgsql' ? ['statuses.id', 'created_at'] : DB::raw('Date(created_at)'); + $gb = db_is_pgsql() ? ['statuses.id', 'created_at'] : DB::raw('Date(created_at)'); $s = Status::selectRaw('Date(created_at) as date, count(statuses.id) as count') ->where('created_at', '>=', now()->subWeek()) ->groupBy($gb) diff --git a/app/Services/CustomEmojiService.php b/app/Services/CustomEmojiService.php index 6e81c100e..987ad2ae5 100644 --- a/app/Services/CustomEmojiService.php +++ b/app/Services/CustomEmojiService.php @@ -250,7 +250,7 @@ class CustomEmojiService public static function all() { return Cache::rememberForever('pf:custom_emoji', function () { - $pgsql = config('database.default') === 'pgsql'; + $pgsql = db_is_pgsql(); return CustomEmoji::when(! $pgsql, function ($q, $pgsql) { return $q->groupBy('shortcode'); diff --git a/app/Services/DiscoverService.php b/app/Services/DiscoverService.php index ed27e6f73..c86723335 100644 --- a/app/Services/DiscoverService.php +++ b/app/Services/DiscoverService.php @@ -10,7 +10,7 @@ class DiscoverService public static function getDailyIdPool() { $min_id = SnowflakeService::byDate(now()->subMonths(3)); - $sqld = config('database.default') == 'mysql'; + $sqld = db_is_mysql_maria(); return DB::table('statuses') ->whereNull('uri') diff --git a/app/Services/SearchApiV2Service.php b/app/Services/SearchApiV2Service.php index 1bec4d247..5f5ca0083 100644 --- a/app/Services/SearchApiV2Service.php +++ b/app/Services/SearchApiV2Service.php @@ -99,7 +99,7 @@ class SearchApiV2Service ) ); } - $operator = config('database.default') === 'pgsql' ? 'ilike' : 'like'; + $operator = db_is_pgsql() ? 'ilike' : 'like'; $results = Profile::select('username', 'id', 'followers_count', 'domain') ->where('username', $operator, $query) ->orWhere('webfinger', $operator, $webfingerQuery) @@ -132,7 +132,7 @@ class SearchApiV2Service $query = Str::startsWith($q, '#') ? substr($q, 1) : $q; $query = $query.'%'; - if (config('database.default') === 'pgsql') { + if (db_is_pgsql()) { $baseQuery = Hashtag::query() ->where('name', 'ilike', $query) ->where('is_banned', false) diff --git a/app/Util/Database/DatabaseDriver.php b/app/Util/Database/DatabaseDriver.php new file mode 100644 index 000000000..2d3223896 --- /dev/null +++ b/app/Util/Database/DatabaseDriver.php @@ -0,0 +1,57 @@ + + */ + public const MYSQL_LIKE = ['mysql', 'mariadb']; + + /** + * The driver name for the given connection (defaults to the active one). + * + * Resolves the real driver rather than the connection name, so a connection + * named `mysql` that is actually configured with the `mariadb` driver is + * reported correctly. + */ + public static function name(?string $connection = null): ?string + { + return DB::connection($connection)->getDriverName(); + } + + /** + * True when the driver is MySQL or MariaDB. + * + * Prefer this over `config('database.default') === 'mysql'`, which excludes + * MariaDB. + */ + public static function isMysqlMaria(?string $connection = null): bool + { + return in_array(self::name($connection), self::MYSQL_LIKE, true); + } + + /** + * True when the driver is PostgreSQL. + */ + public static function isPgsql(?string $connection = null): bool + { + return self::name($connection) === 'pgsql'; + } +} diff --git a/app/helpers.php b/app/helpers.php index cb1cebd7e..37af3f949 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -1,6 +1,7 @@