SPIP et problème à la création d'une table avec une clé trop grande

Si vous faites une opération de changement de la structure de la base de données de SPIP, comme la création d'une base ou la restauration d'une base, il se peut que ça échoue. Il peut y avoir bien des causes à cela. Nous partirons du postulat que vous utilisez MySQL ou MariaDB comme système de gestion de base de données relationnels (ou SGBDR pour les intimes) et non SQLite (qui est bien aussi, mais on n'aborde pas là son cas). Sur votre serveur (via accès direct, FTP ou SSH), dans le dossier où est installé SPIP, allez donc regarder le fichier "tmp/log/mysql.log". Dedans cherchez en étant insensible à la casse "erreur" et "error". Si vous tombez sur une erreur en lien avec MySQL ou MariaDB, alors cet article vous sera peut-être utile, le cas échéant passez votre chemin.

CREATE  TABLE IF NOT EXISTS `database_name`.spip_urls (
	id_parent bigint(21) DEFAULT '0' NOT NULL,
	url VARCHAR(255)  NOT NULL,
	type varchar(25)  DEFAULT 'article' NOT NULL,
	id_objet bigint(21) NOT NULL,
	date DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL,
	segments SMALLINT(3) DEFAULT '1' NOT NULL,
	perma TINYINT(1) DEFAULT '0' NOT NULL,
	langue VARCHAR(10)  DEFAULT '' NOT NULL,
	PRIMARY KEY (id_parent, url),
	KEY type (type, id_objet),
	KEY langue (langue),
	KEY url (url)) ENGINE=MyISAM
Erreur 1071 de mysql: Specified key was too long; max key length is 1000 bytes

Si vous n'avez pas une erreur du même genre, disant que la clé (key) est trop longue (too long), alors cette fois vous n'avez pas de chance, car ce mini-article ne vous aidera pas. Dans le cas contraire, continuons donc. Bien que ce salaud de MySQL ou MariaDB soit bien trop peu verbeux sur l'erreur, ce n'est en fait pas bien compliqué. Pourtant dans notre cas, rien ne semble dépasser 1000. Mais c'est ne pas avoir été attentif de quel type il est explicitement question. Il est question de la taille en octets et non de la taille logique du type. En l'occurrence, on a VARCHAR(255) et un caractère peut être sur plusieurs octets. Pour avoir la réponse, on peut utiliser la requête SQL suivante :

SELECT default_character_set_name
FROM information_schema.SCHEMATA
WHERE schema_name = "database_name";

Si vous n'avez pas un accès direct au SGBDR, à savoir MySQL ou MariaDB dans notre cas, vous y avez au moins accès par le serveur PHP, car SPIP utilise un serveur PHP. Vous pouvez donc déposer un script PHP sur le serveur avec des instructions pour exécuter le SQL et déclencher le script PHP par une requête web à son adresse. Mais c'est chiant et laborieux, donc utilisez plutôt quelque chose comme PHP MySQL Shell Emulator.

Trêve d'interlude, le résultat pour nous de la requête SQL a été le suivant : utf8mb4. Ça signifie unicode sur 4 octets. Or 255 × 4 = 1020, ce qui est bien supérieur à 1000, diantre ! Pour corriger le problème, il faut changer la manière d'encoder les caractères de votre base de données. Cette opération n'est pas anecdotique, il est donc hautement recommandé d'avoir une sauvegarde avant de la faire. Une fois les saines précautions prises (ou pas !), voila la requête SQL à faire exécuter : ALTER DATABASE database_name CHARACTER SET utf8 COLLATE utf8_general_ci;

Annexe 1 : harmoniser la base de données

Cela devrait avoir réglé votre problème ou toutefois ce qui posait problème à SPIP (auquel il suffit de re-demander de créer la table ou les tables). Toutefois, bien que ce ne soit à priori pas grave, cela a créé une incohérence dans la base de données. En effet, celle-ci est maintenant configuré pour un certain encodage des caractères, mais les tables déjà créées sont dans l'ancienne. Pour harmoniser la base de données, vous devez faire le changement pour chaque table. Commencez par en obtenir la liste avec SHOW TABLES in database_name; Ensuite pour chacune, faites les 3 requêtes SQL suivantes :

SET foreign_key_checks = 0;
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
SET foreign_key_checks = 1;

Vous avez trop la flemme de faire ça pour chaque table ? Quel manque de motivation vous faites preuve ! Enregistrez donc la listes des tables dans un fichier et exécutez la commande suivante dans un terminal POSIX (vous devriez en avoir un par défaut sur GNU/Linux et *BSD, ainsi que même le privateur Apple macOS, sur Windows vous pourriez installer et utiliser git-bash qui est gratuit) : cat tables.txt | awk '{print "SET foreign_key_checks = 0; ALTER TABLE", $1, "CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; SET foreign_key_checks = 1; "}'

Malheureusement votre éventuel shell texte PHP, ne vous permez pas forcément d'exécuter plusieurs requêtes SQL d'un coup. Vous pouvez alors faire le travail via un script. Vous pouvez même lui faire faire ce que je vous ai proposé de faire avec awk. Je vous propose par exemple celui-là :

<?php

$DB_HOST = "À REMPLACER";
$DB_USER = "À REMPLACER";
$DB_PASS = "À REMPLACER";
$DB_NAME = "À REMPLACER";

error_reporting(E_ALL);
ini_set("display_errors", "1");
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

$mysqli_connection = new mysqli(
    $DB_HOST,
    $DB_USER,
    $DB_PASS,
    $DB_NAME
);
if($mysqli_connection->connect_error)
{
    printf("Connection Error: \n%s", $mysqli_connection->connect_error);
    return;
}

$result = $mysqli_connection->query("SHOW TABLES in ". $DB_NAME .";");
if($result == false)
{
    printf("Error on SQL request to get names of tables!\n");
    return;
}
$sql_queries = "";
while($row = $result->fetch_array())
{
    $sql_queries +=
        "SET foreign_key_checks = 0;\n".
        "ALTER TABLE ". $row[0] ." CONVERT TO ".
        ."CHARACTER SET utf8 COLLATE utf8_general_ci;\n"
        ."SET foreign_key_checks = 1;\n\n";
}
$result->free();

$result = $mysqli_connection->multi_query($sql_queries);
if($result == false)
{
    printf("Error on SQL request to change character set!\n");
    return;
}
$result->free();
$mysqli_connection->close();				
printf("Everything seems to have been done well!");

Annexe 2 : impossible de sauvegarder dans SPIP

Malgré que vous devriez maintenant avoir réussi à créer toutes les tables dont SPIP a besoin, il se peut que sa fonction de sauvegarde ne sauvegarde pas certaines tables ! Je n'en ai aucune idée de la raison ou les raisons. Mais le système de SPIP pour importer et exporter se base sur son propre format et finira peut-être par disparaitre. Il existe bien sûr une autre solution de sauvegarde et de restauration de la base de données, c'est celle de votre SGBDR. Vous pouvez peut-être avoir accès à la fonction en-dehors de SPIP.

Si ce n'est pas le cas ou que vous avez envie que SPIP fasse des sauvegardes automatiques, vous pourriez utiliser un module complémentaire (dit plug-in en anglais). On propose le suivant qui porte bien son nom : saveauto. Une fois installé, vous aurez accès à ses paramètres via l'interface de gestion des modules complémentaires (accessible via "Configuration") et vous pourrez faire des sauvegardes manuelles (via "Sauvegarder la base" de "Maintenance").