Chyba każdy, kto przenosił swoje zasoby baz danych do MySql w wersji 5.7 spotkał się z pewnym irytującym problemem. Od tej wersji serwer domyślnie uruchamia się z włączoną w sql_mode
restrykcją ONLY_FULL_GROUP_BY
, co bywa przyczyną nerwowych reakcji, a czasami napadów furii.
Jeżeli chcemy być w zgodzie z zaleceniami Oracle, to możemy ambitnie przepisywać nasze dotychczasowe zapytania np. stosując tam, gdzie to możliwe obejście wykorzystujące funkcję any_value(field)
i w wielu przypadkach jest to rozwiązanie wystarczające. W przypadkach bardziej skomplikowanych czeka nas mozolne przedefiniowanie zapytań, co nie zawsze przynosi oczekiwany efekt.
W Laravel może to być szczególnie uciążliwe, gdy nasze dotychczas działające skrypty wykorzystujące Eloquent ORM, zaczynają masowo zgłaszać błędy Incompatible with sql_mode 'only_full_group_by'
. Zwykle pomaga przepisanie zapytań do wersji raw i wykonywanie ich już poza Eloquent, co skutkuje oczywistą utratą korzyści z tego płynących. Czasami, przy prostych zapytaniach, wystarcza lekka modyfikacja z wykorzystaniem any_value()
, ale w wielu sytuacjach jedynym rozwiązaniem jest tylko wyłączenie tego irytującego ograniczenia przez dokonanie odpowiedniego wpisu w pliku konfiguracyjnym serwera MySql. Polega to na zmodyfikowaniu lub dopisaniu w sekcji [mysqld]
parametru sql_mode
, z którego musi być usunięta pozycja ONLY_FULL_GROUP_BY
.
Po restarcie serwera MySql problemy powinny zniknąć i generalnie tak to działa. Jednak w Laravel czasami bywa, że mimo wyłączonia tej restrykcji w konfiguracji serwera MySql, problem nadal występuje i cały czas niektóre operacje kończą się będem SQL Error (1055)
. Na pierwszy rzut oka sytuacja wygląda na taką z gatunku magicznych - coś jest, chociaż teoretycznie być nie powinno.
Korzystając z klienta mysql możemy sprawdzić stan zmiennej sql_mode
i oczywiście widzimy tam, że zgodnie ze zmianami, które wykonaliśmy w konfiguracji ograniczenie ONLY_FULL_GROUP_BY
nie powinno działać. A działa. W takiej sytuacji można skorzystać z konsoli Laravel uruchamianej poleceniem:
$ php artisan tinker
w katolugu głównym naszego projektu.
Poleceniem:
Psy Shell v0.9.3 (PHP 7.1.16 — cli) by Justin Hileman
>>> DB::select(DB::raw('show variables like "%sql_mode%"'))
wyświtlimy wartość zmiennej sql_mode
=> [
{#3181
+"Variable_name": "sql_mode",
+"Value": "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
},
]
i ze zdziwieniem widzimy, że włączone są wszystkie ograniczenia trybu strict
. Oczywiście może to wprawić w zakłopotanie, ale jak to zwykle bywa, wytłumaczenie jest banalne.
Domślna instalacja nowego projektu Laravel ustawia w konfiguracji bazy danych parametr strict = true
. Takie ustawienie wymusza stosowanie trybu strict
bez względu na ustawienia serwera. Żeby to zmienić należy wykonać drobną zmianę w plku konfiguracyjnym:
$ nano config/database.php
i w sekcji mysql
zmienamy wartość parametru strict
:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => false, <=== zmieniamy true na false
'engine' => null,
],
Takie rozwiązanie sprawdzi się idealnie w środowisku, w którym mamy bezpośredni dostęp do serwera MySql i możemy w pełni kontrolować jego stan i konfigurację. W sytacji ograniczonego dostępu Laravel oferuje dodatkowe możliwości. W konfiguracji mysql
możemy wskazać, które tryby (restrykcje) chcemy włączyć. W tym celu w sekcji mysql
konfiguracji możemy umieścić parametr modes
z jedną z poniższych wartości:
'mysql' => [
// korzystaj z ustawienia parametru strict
'modes' => null,
// wyłącz wszystkie tryby, pomiń ustawienie parametru strict
'modes' => [],
// włącz wskazane tryby, pomiń ustawienie parametru strict
'modes' => [
'STRICT_TRANS_TABLES',
'ERROR_FOR_DIVISION_BY_ZERO',
],
]
Dzięki temu nawet w sytuacji ograniczonego dostępu do konfiguracji serwera MySql możemy elastycznie decydować, których trybów pracy używać, bez konieczności odwoływania się do uprzejmości dysponenta serwera.