[spip-dev] bug générateur de requête

Salut,

Je viens de tomber sur un petit bug dans la génération des requêtes
à partir des boucles. J'avoue m'être un peu perdu dans les différents
endroits où trouver de l'information et je ne sais pas si ce problème
a déjà été remonté. Si c'est déjà le cas, désolé.

A tout hasard, le voici :
Une boucle de ce type :
  <BOUCLE_articles_interface(ARTICLES){id_auteur}{id_mot=1}{unique}>
  </BOUCLE_articles_interface>

provoque ça :
<BOUCLE_articles_interface>
Erreur MySQL
SELECT articles.id_article,...,articles.id_trad
FROM spip_articles AS articles
,spip_auteurs_articles AS rel_articles
,spip_mots_articles AS rel_articles
WHERE articles.id_article NOT IN (0)
AND articles.id_article=rel_articles.id_article
AND (rel_articles.id_auteur='1')
AND articles.id_article=rel_articles.id_article
AND (rel_articles.id_mot='1')
AND articles.statut='publie' GROUP
BY articles.id_article
Not unique table/alias: 'rel_articles'
</BOUCLE_articles_interface>

Le moteur de requête a attribué deux fois le même alias de table
rel_articles'. La solution la plus simple consiste à utiliser des noms
d'alias non significatif, simplement indexé (exemple t1, t2, ...).
Si l'on veux conserver des noms d'alias significatifs, il faut alors
indexer chacuns de ces noms (ce qui est moins simple à mettre en oeuvre).
La première solution doit également être plus efficace à l'exécution,
puisqu'il s'agit juste d'incrémenter un compteur.

J'ai écrit ma première ligne de php hier, en plus en la copiant
depuis une contribution de SPIP-CONTRIB, alors, je ne me suis pas
aventuré à aller voir où le problème était...

Cordialement

loul <grasset <at> club-internet.fr> writes:

Salut,

Je viens de tomber sur un petit bug dans la génération des requêtes
à partir des boucles. J'avoue m'être un peu perdu dans les différents
endroits où trouver de l'information et je ne sais pas si ce problème
a déjà été remonté. Si c'est déjà le cas, désolé.

A tout hasard, le voici :
Une boucle de ce type :
  <BOUCLE_articles_interface(ARTICLES){id_auteur}{id_mot=1}{unique}>
  </BOUCLE_articles_interface>

provoque ça :
<BOUCLE_articles_interface>
Erreur MySQL
SELECT articles.id_article,...,articles.id_trad
FROM spip_articles AS articles
,spip_auteurs_articles AS rel_articles
,spip_mots_articles AS rel_articles
WHERE articles.id_article NOT IN (0)
AND articles.id_article=rel_articles.id_article
AND (rel_articles.id_auteur='1')
AND articles.id_article=rel_articles.id_article
AND (rel_articles.id_mot='1')
AND articles.statut='publie' GROUP
BY articles.id_article
Not unique table/alias: 'rel_articles'
</BOUCLE_articles_interface>

Le moteur de requête a attribué deux fois le même alias de table
rel_articles'. La solution la plus simple consiste à utiliser des noms
d'alias non significatif, simplement indexé (exemple t1, t2, ...).
Si l'on veux conserver des noms d'alias significatifs, il faut alors
indexer chacuns de ces noms (ce qui est moins simple à mettre en oeuvre).
La première solution doit également être plus efficace à l'exécution,
puisqu'il s'agit juste d'incrémenter un compteur.

J'ai écrit ma première ligne de php hier, en plus en la copiant
depuis une contribution de SPIP-CONTRIB, alors, je ne me suis pas
aventuré à aller voir où le problème était...

Cordialement

J'ai oublié un truc : ce bug se produit en version 1.7.2.
Comme j'ai deux jours de plus d'expérience en php, je me lance :

le problème est dans le fichier inc-calcul-squel.php3. Un recherche de
"rel_$type" donne tout de suite l'endroit. Une correction est la suivante :

à la place de :
              $col_table = "rel_$type";
il faut mettre :
              $col_table = "t".($idx_alias++);
et bien sur ajouter l'initialisation de la variable avant la boucle qui parse
la commande (juste après le commentaire "// Parser la commande de la boucle") :
  $idx_alias = 1;

Ca corrige le problème ce problème.

La solution peut s'étendre à tous les $table = "toto" en remplaçant :
        $table = "articles";
par
        $table = "t".($idx_alias++);
Ca marche aussi.
Mais :
1) Je n'ai pas identifié de cas avec le code actuel pouvant générer un
disfonctionnement (si j'ai bien compris, on a forcément un seul $table
pour une boucle)
2) Pour être vraiment propre, il faudrait vraiment l'étendre à tous les alias.
Or, dans le cade actuel, il reste quelques alias écrit en dur et référencé en
dur plus loin dans le code. Il faudrait donc créer des variables spécifiques
pour remplacer cela proprement. Comme là aussi je ne sais pas s'il y a un
disfonctionnement potentiel, le jeu n'en vaut peut-être pas la chandelle
dans une version stable.

Il y a peut-être un problème potentiel avec le code qui traite le "Cas
particulier pour les raccourcis 'type_mot' et 'titre_mot'" et qui est
le suivant :

else if ($type != 'mots' AND ($col == 'type_mot' OR $col == 'titre_mot'
OR $col == 'id_groupe')) {
  if ($type == 'forums')
    $col_lien = "spip_mots_forum";
  else if ($type == 'syndication')
    $col_lien = "spip_mots_syndic";
  else
    $col_lien = 'spip_mots_'.$type;
  $req_from = "$col_lien AS lien_mot";
  $req_from = 'spip_mots AS mots';
  $req_where = "$table.$id_objet=lien_mot.$id_objet";
  $req_where = "lien_mot.id_mot=mots.id_mot";
  $req_group = " GROUP BY $table.$id_objet";
  $col_table = 'mots';
  $flag_lien = true;
  if ($col == 'type_mot')
    $col = 'type';
  else if ($col == 'titre_mot')
    $col = 'titre';
  else if ($col == 'id_groupe')
    $col = 'id_groupe';
}

Pour éviter le problème potentiel si on a une boucle avec une combinaison
type_mot et/ou titre_mot et/ou id_groupe (même si a vue de pif ça n'a pas
bien de sens), et dans le même ordre d'idée on peut faire :

else if ($type != 'mots' AND ($col == 'type_mot' OR $col == 'titre_mot'
OR $col == 'id_groupe')) {
  if ($type == 'forums')
    $col_lien = "spip_mots_forum";
  else if ($type == 'syndication')
    $col_lien = "spip_mots_syndic";
  else
    $col_lien = 'spip_mots_'.$type;
  $col_table = "t".($idx_alias++);
  $col_lien_table = "t".($idx_alias++);
  $req_from = "$col_lien AS $col_lien_table";
  $req_from = 'spip_mots AS $col_table';
  $req_where = "$table.$id_objet=$col_lien_table.$id_objet";
  $req_where = "$col_lien_table.id_mot=$col_table.id_mot";
  $req_group = " GROUP BY $table.$id_objet";
  $flag_lien = true;
  if ($col == 'type_mot')
    $col = 'type';
  else if ($col == 'titre_mot')
    $col = 'titre';
  else if ($col == 'id_groupe')
    $col = 'id_groupe';
}

Voilà tout.

En éspérant avoir aidé quelqu'un.
A+