Valider 84f049c0 rédigé par cerdic's avatar cerdic
Parcourir les fichiers

Report de r20534 : Ferme #3002 : lorsque la table des rubriques est mal formée...

Report de r20534 : Ferme #3002 : lorsque la table des rubriques est mal formée et contient une branche morte ou une reference circulaire dans les parents, la fonction propager_les_secteurs() tourne en boucle infinie.
Correction :
- proteger de ce risque par un maxiter initialisé par le nombre de rubriques de la table
- revoir l'algo de mise a jour en procedant de proche en proche, par profondeur croissante et non pas "tant qu'il y a une rubrique mal rangee".
Cela permet de plus de detecter le cas d'une table rubrique mal formee et de produire un log en consequence
parent cde5b3d6
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+52 −4
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -293,6 +293,7 @@ function calculer_rubriques_publiees() {
 *
 * Cherche les rubriques ayant des id_secteur ou profondeurs ne correspondant pas
 * avec leur parent, et les met à jour. De même avec les articles et leur id_secteur
 * On procede en iterant la profondeur de 1 en 1 pour ne pas risquer une boucle infinie sur reference circulaire
 *
 * @pipeline_appel trig_propager_les_secteurs
 * 
@@ -300,18 +301,65 @@ function calculer_rubriques_publiees() {
**/
function propager_les_secteurs()
{
	// fixer les id_secteur des rubriques racines
	// Profondeur 0
	// Toutes les rubriques racines sont de profondeur 0
	// et fixer les id_secteur des rubriques racines
	sql_update('spip_rubriques', array('id_secteur'=>'id_rubrique','profondeur'=>0), "id_parent=0");
	// Toute rubrique non racine est de profondeur >0
	sql_updateq('spip_rubriques', array('profondeur'=>1), "id_parent<>0 AND profondeur=0");

	// securite : pas plus d'iteration que de rubriques dans la base
	$maxiter = sql_countsel("spip_rubriques");

	// reparer les rubriques qui n'ont pas l'id_secteur de leur parent
	// on fait profondeur par profondeur

	$prof = 0;
	do {
		$continuer = false;
		$r = sql_select("A.id_rubrique AS id, R.id_secteur AS secteur, R.profondeur+1 as profondeur", "spip_rubriques AS A, spip_rubriques AS R", "A.id_parent = R.id_rubrique AND (A.id_secteur <> R.id_secteur OR A.profondeur <> R.profondeur+1)");

		// Par recursivite : si toutes les rubriques de profondeur $prof sont bonnes
		// on fixe le profondeur $prof+1

		// Toutes les rubriques dont le parent est de profondeur $prof ont une profondeur $prof+1
		// on teste A.profondeur > $prof+1 car :
		// - toutes les rubriques de profondeur 0 à $prof sont bonnes
		// - si A.profondeur = $prof+1 c'est bon
		// - cela nous protege de la boucle infinie en cas de reference circulaire dans les rubriques
		$r = sql_select("A.id_rubrique AS id, R.id_secteur AS secteur, R.profondeur+1 as profondeur",
			"spip_rubriques AS A, spip_rubriques AS R",
			"A.id_parent = R.id_rubrique AND R.profondeur=".intval($prof)." AND (A.id_secteur <> R.id_secteur OR A.profondeur > R.profondeur+1)");

		while ($row = sql_fetch($r)) {
			sql_update("spip_rubriques", array("id_secteur" => $row['secteur'],'profondeur' => $row['profondeur']), "id_rubrique=".$row['id']);
		}

		// Toutes les rubriques de profondeur $prof+1 qui n'ont pas un parent de profondeur $prof sont decalees
		$maxiter2 = $maxiter;
		while ($maxiter2--
			AND $rows = sql_allfetsel(
			"id_rubrique as id",
			"spip_rubriques",
			"profondeur=".intval($prof+1)." AND id_parent NOT IN (".sql_get_select("zzz.id_rubrique","spip_rubriques AS zzz","zzz.profondeur=".intval($prof)).")",'','','0,100')){
			$rows = array_map('reset',$rows);
			sql_updateq("spip_rubriques", array('profondeur' => $prof+2), sql_in("id_rubrique",$rows));
		}

		// ici on a fini de valider $prof+1, toutes les rubriques de prondeur 0 a $prof+1 sont OK
		// si pas de rubrique a profondeur $prof+1 pas la peine de continuer
		// si il reste des rubriques non vues, c'est une branche morte ou reference circulaire (base foireuse)
		// on arrete les frais
		if (sql_countsel("spip_rubriques","profondeur=".intval($prof+1))){
			$prof++;
			$continuer = true;
		}
	} while ($continuer);
	}
	while ($continuer AND $maxiter--);

	// loger si la table des rubriques semble foireuse
	if (sql_countsel("spip_rubriques","profondeur>".intval($prof+1))){
		spip_log("Les rubriques de profondeur>".($prof+1)." semblent suspectes (branches morte ou reference circulaire dans les parents)",_LOG_CRITIQUE);
	}

	// reparer les articles
	$r = sql_select("A.id_article AS id, R.id_secteur AS secteur", "spip_articles AS A, spip_rubriques AS R", "A.id_rubrique = R.id_rubrique AND A.id_secteur <> R.id_secteur");