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.


blog comments powered by Disqus