[spip-dev] {branche?} provoque une jointure / SPIP 3.1.1 SVN

Bonjour,

Je suis tombé sur le problème avec le plugin agenda qui a une boucle dans agenda-ical.html :
<BOUCLE_evenement2_branche(EVENEMENTS) {branche?}{id_article?}{id_mot?}{statut=publie}{par date_fin} {age_fin<=0} {0,50} {doublons}>

Le code SQL généré quand il n'y a pas de id_rubrique dans l'URL est :
SELECT evenements.date_fin, evenements.titre, evenements.id_evenement, evenements.date_debut, evenements.horaire, evenements.id_article, evenements.descriptif, evenements.lieu, evenements.date_creation, evenements.maj
FROM spip_evenements AS `evenements`
INNER JOIN spip_articles AS L1 ON ( L1.id_article = evenements.id_article )
WHERE ((L1.id_rubrique IN (0,626,630,636,644,647,666,681,923,627,629,648,652,653,624,631,635,926,927,638,645,673,680,825,924,925)))
  AND (evenements.statut = 'publie')
  AND (TIMESTAMPDIFF(HOUR,evenements.date_fin,NOW())/24 <= 0)
GROUP BY evenements.id_evenement
ORDER BY evenements.date_fin
LIMIT 0,50

Si j'enlève {branche?}
Le code se simplifie en :
SELECT evenements.date_fin, evenements.titre, evenements.id_evenement, evenements.date_debut, evenements.horaire, evenements.id_article, evenements.descriptif, evenements.lieu, evenements.date_creation, evenements.maj
FROM spip_evenements AS `evenements`
WHERE (evenements.statut = 'publie')
  AND (TIMESTAMPDIFF(HOUR,evenements.date_fin,NOW())/24 <= 0)
GROUP BY evenements.id_evenement
ORDER BY evenements.date_fin
LIMIT 0,50

Pourquoi est-ce que {branche?} produit un code SQL alors que les autres ne produisent rien ?

PS : ce "bug" fait disparaître les événements des pages uniques (du plugin de même nom)
PS² : j'ai un patch violent qui doublonne les 3 boucle en les entourant d'une boucle condition :
<BOUCLE_Branche(AUTEURS){si #ENV{id_rubrique}}{0,1}>

Bah {branche} travaille sur les rubriques. Or les événements ne sont absolument pas liés à des rubriques mais à des articles. Et donc il y a un cas de {branche} où ça va chercher les événements *des articles* liées aux rubriques demandées. Donc jointure sur les articles.

Je ne vois pas ce qu'il y a de bizarre.

RastaPopoulos a écrit le 12/08/2016 à 09:34 :

Bah {branche} travaille sur les rubriques. Or les événements ne sont
absolument pas liés à des rubriques mais à des articles. Et donc il y a
un cas de {branche} où ça va chercher les événements *des articles*
liées aux rubriques demandées. Donc jointure sur les articles.

Je ne vois pas ce qu'il y a de bizarre.

Ce qui est bizarre, c'est que {branche?} (la version sensée être facultative), au lieu de ne rien modifier à la requête SQL quand il n'y a pas de id_rubrique dans le contexte, rajoute au contraire systématiquement un code comportant toutes les rubriques.
D'une part, en terme de performances, ça rajoute une jointure et in IN coûteux.
D'autre part, ça plante la recherche d'événements rattachés à une page unique.

RealET a écrit le 12/08/2016 à 10:10 :

RastaPopoulos a écrit le 12/08/2016 à 09:34 :

Bah {branche} travaille sur les rubriques. Or les événements ne sont
absolument pas liés à des rubriques mais à des articles. Et donc il y a
un cas de {branche} où ça va chercher les événements *des articles*
liées aux rubriques demandées. Donc jointure sur les articles.

Je ne vois pas ce qu'il y a de bizarre.

Ce qui est bizarre, c'est que {branche?} (la version sensée être
facultative), au lieu de ne rien modifier à la requête SQL quand il n'y
a pas de id_rubrique dans le contexte, rajoute au contraire
systématiquement un code comportant toutes les rubriques.
D'une part, en terme de performances, ça rajoute une jointure et in IN
coûteux.
D'autre part, ça plante la recherche d'événements rattachés à une page
unique.

J'ai tenté de lire le code du critère branche.
D'après ce que j'en comprends, on commence par aller chercher l'id de rubrique (soit passé en paramètre, soit dans le contexte :
https://core.spip.net/projects/spip/repository/entry/branches/spip-3.1/ecrire/public/criteres.php#L496

AMHA, on devrait sortir du critère sans rien modifier à la boucle :
- s'il n'y a pas d'id_rubrique trouvé
- et que le critère est conditionnel {branche?}

Est-ce que c'est ce que https://core.spip.net/projects/spip/repository/entry/branches/spip-3.1/ecrire/public/criteres.php#L522 tente de faire ?
$boucle->where = !$crit->cond ? $c :
      ("($arg ? $c : " . ($not ? "'0=1'" : "'1=1'") . ')');

Ok, on a bien un bug ici. Je reproduis.

Le code généré est : (sql_quote(@$Pile[0]['id_rubrique']) ? sql_in('L1.id_rubrique', calcul_branche_in(sql_quote(@$Pile[0]['id_rubrique']))) : '1=1')

Mais sql_quote(0) donne '0' considérant que c'est du texte, sans précision du type de champ.
Du coup, ça entre toujours dans la condition.

Il faut ajouter le type int attendu par sql_quote ici...
Ce sql_quote est généré par kwote. Qui peut prendre le type en argument.
Donc , passer l'appel de :

$arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles));

à

$arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles), $boucle->sql_serveur, 'int NOT NULL');

Mais la fonction kwote peut utiliser 2 sql_quote, et le second qui sera utilisé ici n'utilise pas l'argument de type : je pense que c'est un oubli et que ça devrait être ajouté.

Donc dans kwote() :
Passer de
return "sql_quote($lisp)";
à
return "sql_quote($lisp, '$serveur', '$type')";

Je suppose que l'oubli (si c'est le cas) vient de https://core.spip.net/projects/spip/repository/revisions/19257

Avec ces 2 corrections les boucles semblent fonctionner comme attendu :
?page=test :
<BOUCLE_br(EVENEMENTS){branche?}>
=> pas de jointure

?page=test&id_rubrique=1 :
<BOUCLE_br(EVENEMENTS){branche?}>
=> jointure OK

?page=test&id_rubrique[]=1&id_rubrique[]=2 :
<BOUCLE_br(EVENEMENTS){branche?}>
=> jointure OK

MM.

Oui, donc, croisons les liens :
Ticket là https://core.spip.net/issues/3817
Et résolution là : https://core.spip.net/projects/spip/repository/revisions/23132

Done.

Matthieu Marcillaud a écrit le 17/08/2016 à 08:40 :