Valider a6012c93 rédigé par esj's avatar esj
Parcourir les fichiers

Table des metas et procédure de mise à jour: généralisation.

La fonctionnalité [14862] introduite en retard dans SPIP 2.1 par [15624] permettant aux plugins d'utiliser la fonction {{{maj_while}}} pour leur propre mise à jour avait deux lacunes.

La première était d'arrêter tout le processus en cas de problème, alors que pour un plugin c'est un blocage rarement légitime. La fonction {{{maj_while}}} retourne donc à présent un résultat (vide si ok, infos si echec), et c'est l'appelant qui décide d'arrêter les frais ou pas. Testé sur une grosse mise à jour 1.9.2 -> 2.1.0dev sans pb apparent.

L'autre lacune concernait une confusion possible sur la meta à utiliser pour assurer la reprise sur interruption. C'était assez théorique, mais de fil en aiguille, on se retrouve avec une fonctionnalité encore plus générale: les fonctions {{{ecrire_meta effacer_meta lire_metas inc_meta_dist}}} s'appliquent sur éventuellement d'autres tables que spip_meta, en gérant un cache pour chacune. Cette fonctionnalité permet en particulier à chaque plugin d'avoir sa table des meta, ce qui facilite la sauvegarde de tout ce qui le concerne, et fournit un accès a priori bien plus rapide que l'avalanche d'objets et de tableaux que CFG alloue à chaque appel de lire_config. 

Utilissation:
{{{
// creer sa table identique a spip_meta:
sql_create('spip_MONPLUGIN_metas', 
		$tables_auxiliaires['spip_meta']['field'],
		$tables_auxiliaires['spip_meta']['key'],
		false, false)) {

// dans son mes_options.php:
# reprendre la fonction deja chargee par inc_version:
$inc_meta = charger_fonction('meta', 'inc'); 
# charger dans la globale $MONPLUGIN_metas la table ou son cache
$inc_meta('spip_MONPLUGIN_metas');
}}}
parent 82b4b83d
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+39 −23
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -33,12 +33,22 @@ function base_upgrade_dist($titre='', $reprise='')
		}
		$meta = _request('meta');
		if (!$meta)
			maj_base();
			$res = maj_base();
		// reprise sur demande de mise a jour interrompue pour plugin 
		else 	maj_while($GLOBALS['meta'][$meta],
		else $res= maj_while($GLOBALS['meta'][$meta],
				  $GLOBALS[$meta]['cible'],
				  $GLOBALS[$meta]['maj'],
				  $meta);
				  $meta,
				  _request('table'));
		if ($res) {
			if (!is_array($res))
				spip_log("Pb d'acces SQL a la mise a jour");
			else {
				include_spip('inc/minipres');
				echo minipres(_T('avis_operation_echec') . ' ' . join(' ', $res));
				exit;
			}
		}
	}
	spip_log("Fin de mise a jour SQL. Debut m-a-j acces et config");
	
@@ -76,9 +86,9 @@ function maj_base($version_cible = 0) {
		      array('nom' => 'version_installee',
			    'valeur' => $spip_version_base,
			    'impt' => 'non'));
		return;
		return false;
	}
	if (!upgrade_test()) return;
	if (!upgrade_test()) return true;
	
	$cible = ($version_cible ? $version_cible : $spip_version_base);

@@ -105,7 +115,7 @@ function maj_base($version_cible = 0) {
		$cible = $cible*1000;

	include_spip('maj/svn10000');
	maj_while($version_installee, $cible, $GLOBALS['maj'], 'version_installee');
	return maj_while($version_installee, $cible, $GLOBALS['maj'], 'version_installee');
}

// A partir des > 1.926 (i.e SPIP > 1.9.2), cette fonction gere les MAJ.
@@ -116,14 +126,17 @@ function maj_base($version_cible = 0) {
// 1. le numero de version courant (nombre entier; ex: numero de commit)
// 2. le numero de version a atteindre (idem)
// 3. le tableau des instructions de mise a jour a executer
// Pour profiter du mecanisme de reprise sur interruption
// il faut donner comme 4e arg le nom de la meta permettant de retrouver tout ca
// Pour profiter du mecanisme de reprise sur interruption il faut de plus
// 4. le nom de la meta permettant de retrouver tout ca
// 5. la table des meta ou elle se trouve ($table_prefix . '_meta' par defaut)
// (cf debut de fichier)
// en cas d'echec, cette fonction retourne un tableau (etape,sous-etape)
// sinon elle retourne un tableau vide

define('_UPGRADE_TIME_OUT', 20);

// http://doc.spip.org/@maj_while
function maj_while($installee, $cible, $maj, $meta='')
function maj_while($installee, $cible, $maj, $meta='', $table='meta')
{
	$n = 0;
	$time = time();
@@ -131,42 +144,45 @@ function maj_while($installee, $cible, $maj, $meta='')
	while ($installee < $cible) {
		$installee++;
		if (isset($maj[$installee])) {
			serie_alter($installee, $maj[$installee]);
			$etape = serie_alter($installee, $maj[$installee], $meta, $table);
			
			if ($etape) return array($installe, $etape);
			$n = time() - $time;
			spip_log("$meta: $installee en $n secondes",'maj');
			if ($meta) ecrire_meta($meta, $installee,'non');
			spip_log("$table $meta: $installee en $n secondes",'maj');
			if ($meta) ecrire_meta($meta, $installee,'non', $table);
		} // rien pour SQL
		if ($n >= _UPGRADE_TIME_OUT) {
			redirige_url_ecrire('upgrade', "reinstall=$installee&meta=$meta");
			redirige_url_ecrire('upgrade', "reinstall=$installee&meta=$meta&table=$table");
		}
	}
	// indispensable pour les chgt de versions qui n'ecrivent pas en base
	// tant pis pour la redondance eventuelle avec ci-dessus
	if ($meta) ecrire_meta($meta, $installee,'non');
	spip_log("MAJ terminee. $meta: $installee",'maj');
	return array();
}

// Appliquer une serie de chgt qui risquent de partir en timeout
// (Alter cree une copie temporaire d'une table, c'est lourd)

// http://doc.spip.org/@serie_alter
function serie_alter($serie, $q = array()) {
	$etape = intval(@$GLOBALS['meta']['upgrade_etape_'.$serie]);
function serie_alter($serie, $q = array(), $meta='', $table='meta') {
	$meta .= '_maj_' . $serie;
	$etape = intval(@$GLOBALS[$table][$meta]);
	foreach ($q as $i => $r) {
		if ($i >= $etape) {
			$msg = "maj $table $meta etape $i";
			if (is_array($r)
			AND function_exists($f = array_shift($r))) {
				spip_log("$serie/$i: $f " . join(',',$r),'maj');
				ecrire_meta('upgrade_etape_'.$serie, $i+1); // attention on enregistre le meta avant de lancer la fonction, de maniere a eviter de boucler sur timeout
				spip_log("$msg: $f " . join(',',$r),'maj');
				ecrire_meta($meta, $i+1, 'non', $table); // attention on enregistre le meta avant de lancer la fonction, de maniere a eviter de boucler sur timeout
				call_user_func_array($f, $r);
				spip_log("$serie/$i: ok", 'maj');
			} else {
			  echo "maj $serie etape $i incorrecte";
			  exit;
			}
				spip_log("$meta: ok", 'maj');
			} else return $i+1;
		}
	}
	effacer_meta('upgrade_etape_'.$serie);
	effacer_meta($meta, $table);
	return 0;
}


+53 −44
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -19,63 +19,67 @@ if (!defined("_ECRIRE_INC_VERSION")) return;
define('_META_CACHE_TIME', 1<<24);

// http://doc.spip.org/@inc_meta_dist
function inc_meta_dist()
function inc_meta_dist($table='meta')
{
	// Lire les meta, en cache si present, valide et lisible
	// en cas d'install ne pas faire confiance au meta_cache eventuel
	$cache = cache_meta($table);

	if ((_request('exec')!=='install' OR !test_espace_prive())
	AND $new = jeune_fichier(_FILE_META, _META_CACHE_TIME)
	AND lire_fichier_securise(_FILE_META,$meta)
	AND $new = jeune_fichier($cache, _META_CACHE_TIME)
	AND lire_fichier_securise($cache, $meta)
	AND $meta = @unserialize($meta))
		$GLOBALS['meta'] = $meta;
		$GLOBALS[$table] = $meta;

	if (isset($GLOBALS['meta']['touch']) && ($GLOBALS['meta']['touch']<time()-_META_CACHE_TIME))
		unset($GLOBALS['meta']);
	if (isset($GLOBALS[$table]['touch']) 
	AND ($GLOBALS[$table]['touch']<time()-_META_CACHE_TIME))
		$GLOBALS[$table] = array();
	// sinon lire en base
	if (!$GLOBALS['meta']) $new = !lire_metas();
	// renouveller l'alea au besoin
	if (!$GLOBALS[$table]) $new = !lire_metas($table);

	// renouveller l'alea general si trop vieux ou sur demande explicite
	if ((test_espace_prive() || isset($_GET['renouvelle_alea']))
	AND $GLOBALS['meta']
	AND (time() > _RENOUVELLE_ALEA + @$GLOBALS['meta']['alea_ephemere_date'])) {
	AND isset($GLOBALS['meta']['alea_ephemere_date'])
	AND (time() > _RENOUVELLE_ALEA + $GLOBALS['meta']['alea_ephemere_date'])) {
		// si on n'a pas l'acces en ecriture sur le cache,
		// ne pas renouveller l'alea sinon le cache devient faux
		if (supprimer_fichier(_FILE_META)) {
		if (supprimer_fichier($cache)) {
			include_spip('inc/acces');
			renouvelle_alea();
			$new = false; 
		} else spip_log("impossible d'ecrire dans " . _FILE_META);
		} else spip_log("impossible d'ecrire dans " . $cache);
	}
	// et refaire le cache si on a du lire en base
	if (!$new) touch_meta();
	if (!$new) touch_meta(false, $table);
}

// fonctions aussi appelees a l'install ==> spip_query en premiere requete 
// pour eviter l'erreur fatale (serveur non encore configure)

// http://doc.spip.org/@lire_metas
function lire_metas() {
function lire_metas($table='meta') {

	if ($result = spip_query("SELECT nom,valeur FROM spip_meta")) {
	if ($result = spip_query("SELECT nom,valeur FROM spip_$table")) {
		include_spip('base/abstract_sql');
		$GLOBALS['meta'] = array();
		$GLOBALS[$table] = array();
		while ($row = sql_fetch($result))
			$GLOBALS['meta'][$row['nom']] = $row['valeur'];
			$GLOBALS[$table][$row['nom']] = $row['valeur'];

		if (!$GLOBALS['meta']['charset']
		  OR $GLOBALS['meta']['charset']=='_DEFAULT_CHARSET' // hum, correction d'un bug ayant abime quelques install
		if (!$GLOBALS[$table]['charset']
		  OR $GLOBALS[$table]['charset']=='_DEFAULT_CHARSET' // hum, correction d'un bug ayant abime quelques install
		)
			ecrire_meta('charset', _DEFAULT_CHARSET);
			ecrire_meta('charset', _DEFAULT_CHARSET, NULL, $table);
	}
	return $GLOBALS['meta'];
	return $GLOBALS[$table];
}

// Mettre en cache la liste des meta, sauf les valeurs sensibles 
// pour qu'elles ne soient pas visibiles dans un fichier.souvent en 777
// http://doc.spip.org/@touch_meta
function touch_meta($antidate= false){

	if (!$antidate OR !@touch(_FILE_META, $antidate)) {
		$r = $GLOBALS['meta'];
function touch_meta($antidate= false, $table='meta'){
	$file = cache_meta($table);
	if (!$antidate OR !@touch($file, $antidate)) {
		$r = $GLOBALS[$table];
		unset($r['alea_ephemere']);
		unset($r['alea_ephemere_ancien']);
		// le secret du site est utilise pour encoder les contextes ajax que l'on considere fiables
@@ -83,52 +87,57 @@ function touch_meta($antidate= false){
		// meme si son squelette est en cache
		//unset($r['secret_du_site']);
		if ($antidate) $r['touch']= $antidate;
		ecrire_fichier_securise(_FILE_META, serialize($r));
		ecrire_fichier_securise($file, serialize($r));
	}
}

// http://doc.spip.org/@effacer_meta
function effacer_meta($nom) {
function effacer_meta($nom, $table='meta') {
	// section critique sur le cache:
	// l'invalider avant et apres la MAJ de la BD
	// c'est un peu moints bien qu'un vrai verrou mais ca suffira
	// c'est un peu moins bien qu'un vrai verrou mais ca suffira
	// et utiliser une statique pour eviter des acces disques a repetition
	static $touch = true;
	static $touch = array();
	$antidate = time() - (_META_CACHE_TIME<<4);
	if ($touch) {touch_meta($antidate);}
	sql_delete("spip_meta", "nom='$nom'");
	unset($GLOBALS['meta'][$nom]);
	if ($touch) {touch_meta($antidate); $touch = false;}
	if (!isset($touch[$table])) {touch_meta($antidate, $table);}
	sql_delete('spip_' . $table, "nom='$nom'");
	unset($GLOBALS[$table][$nom]);
	if (!isset($touch[$table])) {touch_meta($antidate, $table); $touch[$table] = false;}
}

// http://doc.spip.org/@ecrire_meta
function ecrire_meta($nom, $valeur, $importable = NULL) {
function ecrire_meta($nom, $valeur, $importable = NULL, $table='meta') {

	static $touch = true;
	static $touch = array();
	if (!$nom) return;
	include_spip('base/abstract_sql');
	$res = sql_select("*","spip_meta","nom=" . sql_quote($nom),'','','','','','continue');
	$res = sql_select("*",'spip_' . $table,"nom=" . sql_quote($nom),'','','','','','continue');
	// table pas encore installee, travailler en php seulement
	if (!$res) {
		$GLOBALS['meta'][$nom] = $valeur;
		$GLOBALS[$table][$nom] = $valeur;
		return;
	}
	$res = sql_fetch($res);
	// ne pas invalider le cache si affectation a l'identique
	// (tant pis si impt aurait du changer)
	if ($res AND $valeur == $res['valeur'] AND $GLOBALS['meta'][$nom] == $valeur) return;
	$GLOBALS['meta'][$nom] = $valeur;
	if ($res AND $valeur == $res['valeur'] AND $GLOBALS[$table][$nom] == $valeur) return;
	$GLOBALS[$table][$nom] = $valeur;
	// cf effacer pour comprendre le double touch
	$antidate = time() - (_META_CACHE_TIME<<1);
	if ($touch) {touch_meta($antidate);}
	if (!isset($touch[$table])) {touch_meta($antidate, $table);}
	$r = array('nom' => $nom, 'valeur' => $valeur);
	// Gerer les vieilles versions ou impt n'existait pas
	// Gaffe aux tables sans impt (vieilles versions de SPIP notamment)
	if ($importable AND isset($res['impt'])) $r['impt'] = $importable;
	if ($res) {
		sql_updateq('spip_meta', $r,"nom=" . sql_quote($nom));
		sql_updateq('spip_' . $table, $r,"nom=" . sql_quote($nom));
	} else {
		sql_insertq('spip_meta', $r);
		sql_insertq('spip_' . $table, $r);
	}
	if ($touch) {touch_meta($antidate); $touch = false;}
	if (!isset($touch[$table])) {touch_meta($antidate, $table); $touch[$table] = false;}
}

function cache_meta($table='meta')
{
	return ($table=='meta') ? _FILE_META : (_DIR_CACHE . $table . '.php');
}
?>