Inleiding
Dezer dagen vraagt een gemiddelde Ubuntu installatie met regelmaat voor een upgrade naar versie 18.04 LTS (Long Term Support), een uitgave met verlengde ondersteuning (tot vijf jaar) voor een deel van de geleverde software. Aangezien ik tot nu toe vrij tevreden was over Ubuntu, besloot ik er dan maar meteen een frisse herinstallatie van te maken.
In de afgelopen jaren had ik een tekstbestand gevuld met commando's en geheugensteuntjes die ik als leidraad kon gebruiken bij de installatie. Zo zou ik zeker geen belangrijke zaken vergeten te installeren of te configureren. Daaronder ook de MySQL-databaseserver en de koppeling naar PHP.
Gelijkvloerse drempel
Bij het inrichten van mijn webapplicaties op http://localhost/
bleek dat ze de
verbinding met de database niet konden maken. In eerdere versies van MySQL en
Ubuntu verscheen er tijdens de installatie een venster met de vraag om een
wachtwoord. Nu was die vraag niet gesteld. Wat bleek? Er was een blanco
wachtwoord voor de beheerdersaccount (root
) ingesteld.
Aldus dacht ik slim te zijn en met een eerder geschreven artikel het wachtwoord opnieuw in te stellen. Jammer genoeg werkten deze instructies niet meer. Wat nu?
Root-toegang
Met behulp van onderstaande commando's kon ik de root
-account toch weer met
een wachtwoord laten verbinden. In de shell opende ik een MySQL-client met
beheerdersrechten (sudo
):
sudo mysql -u root
Bij de standaard installatie wordt de authenticatieplugin auth_socket
gebruikt.
Hierbij speelt het wachtwoord totaal geen rol. In plaats daarvan wordt de
gebruikersnaam uit de shell vergeleken met de gebruikersnaam in de database. Met
de volgende queries wordt deze manier van authenticatie uitgeschakeld:
use mysql;
update user set plugin='' where User='root';
flush privileges;
\q;
Tot slot volgt het opnieuw instellen van het root
-wachtwoord, met nog een
aantal verdere acties ter betere beveiliging van de databaseserver. Dit commando
wordt ten strengste aanbevolen, zeker als een server 'live', in productie is.
sudo mysql_secure_installation
Overstap
Al geruime tijd zijn de merknaam en ontwikkeling van MySQL
in handen van
Oracle, een groot commercieel softwarebedrijf uit de Verenigde Staten. Omdat een
deel van de ontwikkelaars zich niet kon vinden in de koers van dat bedrijf,
hebben ze de broncode geforkt. Vanaf dat moment was er een alternatieve
databaseserver genaamd MariaDB
. De installatie verloopt zonder problemen,
maar let wel op bovengenoemd akkefietje met het root
-wachtwoord:
sudo apt install mariadb-server
Debugging
Na de overstap voerde ik de scripts uit, om alle bestaande databases terug in te lezen. Bij één van mijn applicaties ging dit niet naar behoren. Ik zag geen enkele foutmelding tijdens het importeren, maar er werd ook geen data getoond in de applicatie. Wat was hier aan de hand?
Het enige directe verschil tussen deze en mijn voorgaande applicaties was het
gebruik van PHP Data Objects, oftewel PDO. Bij het inlezen van de SQL-scripts
maakte ik gebruik van PDO's exec()
-commando; niet wetende dat deze functie
eigenlijk bedoeld is om slechts één statement uit te voeren.
Dat bleek toen ik met de oude vertrouwde var_dump(); die();
al debuggend op
zoek ging naar waarom bepaalde stored procedures niet werden geïmporteerd. Ook
ontbrak er een complete tabel; terwijl anderen zonder problemen, inclusief data
voorhanden waren.
Unieke tekenreeks
Door het stap voor stap importeren vond ik de boosdoener. MariaDB beklaagde zich
over een UNIQUE
-sleutel die ik op een VARCHAR(400)
-veld had gelegd met de
volgende foutmelding:
1071 - Specified key was too long; max key length is 767 bytes
De oplossing was even eenvoudig als elegant. Ik maakte er een VARCHAR(255)
van
en paste tenslotte alle procedures die met de betreffende tabel in aanraking
kwamen aan. Gelukkig was er op dat moment geen enkele rij met méér dan 255
tekens in het betreffende veld.
Debug-hulp
Uit deze zoektocht blijkt maar weer eens, hoe belangrijk een effectieve debugger
kan zijn voor iedere software-ontwikkelaar. Ik zocht en vond een mogelijkheid
om bij de eerst optredende fout direct de uitvoering te stoppen, en de betreffende
melding te tonen. Ik verwerkte dit stuk code tot een eigen implementatie van
exec()
genaamd execMulti()
:
private static $_instance = null;
public static function getInstance()
{
if (is_null(self::$_instance)) {
$dsn = 'mysql:host='.DBHOSTNAME.';charset=utf8';
self::$_instance = new \PDO($dsn, DBUSERNAME, DBPASSWORD);
$createSQL = sprintf(
'CREATE DATABASE IF NOT EXISTS `%1$s` DEFAULT CHARACTER SET '.
'utf8 DEFAULT COLLATE utf8_general_ci; USE `%1$s`;',
DBNAME
);
self::$_instance->execMulti($createSQL);
}
return self::$_instance;
}
public static function execMulti(string $queries, string &$errorMessage = null)
{
$db = self::getInstance();
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$db->setAttribute(\PDO::ATTR_EMULATE_PREPARES, 1);
$stmt = $db->prepare($queries);
$stmt->execute();
$i = 1;
do {
$i++;
} while ($stmt->nextRowset());
$error = $stmt->errorInfo();
if ($error[0] != "00000") {
$errorMessage = "Query $i failed: ".$error[2].PHP_EOL;
return false;
}
return true;
}