From 1e60e3e9338a5bb41f2e96342e63d9badfc85c1d Mon Sep 17 00:00:00 2001 From: "Committo,Ergo:sum" <esj@rezo.net> Date: Wed, 28 Nov 2007 16:13:25 +0000 Subject: [PATCH] =?UTF-8?q?Un=20fichier=20s=C3=A9par=C3=A9=20pour=20les=20?= =?UTF-8?q?fonctions=20g=C3=A9rant=20les=20jointures:=20elles=20n'ont=20ja?= =?UTF-8?q?mais=20=C3=A9t=C3=A9=20limit=C3=A9es=20=C3=A0=20la=20compilatio?= =?UTF-8?q?n=20des=20crit=C3=A8res,=20il=20aurait=20d=C3=BB=20exister=20de?= =?UTF-8?q?puis=20le=20d=C3=A9but.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 1 + ecrire/public/compiler.php | 8 +- ecrire/public/composer.php | 4 +- ecrire/public/criteres.php | 202 ++---------------------------------- ecrire/public/jointures.php | 195 ++++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+), 198 deletions(-) create mode 100644 ecrire/public/jointures.php diff --git a/.gitattributes b/.gitattributes index f0b7662532..68356c9fc2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -713,6 +713,7 @@ ecrire/maj/v019.php -text ecrire/maj/v019_pre193.php -text ecrire/prive.php -text ecrire/public/index.php -text +ecrire/public/jointures.php -text ecrire/public/vertebrer.php -text ecrire/req/pg.php -text ecrire/rien.gif -text diff --git a/ecrire/public/compiler.php b/ecrire/public/compiler.php index c6526aa502..557a8fc783 100644 --- a/ecrire/public/compiler.php +++ b/ecrire/public/compiler.php @@ -36,6 +36,9 @@ include_spip('public/criteres'); // definition des balises include_spip('public/balises'); +// Gestion des jointures +include_spip('public/jointures'); + // http://doc.spip.org/@argumenter_inclure function argumenter_inclure($struct, $descr, &$boucles, $id_boucle, $echap=true){ $l = array(); @@ -303,8 +306,7 @@ function calculer_requete_sql(&$boucle) ', # WHERE ' . calculer_dump_join($boucle->join) . ', # WHERE pour jointure - ' . (!$boucle->group ? "''" : - ('"' . join(", ", $boucle->group)) . '"') . + ' . ('array(' . join(',',array_map('_q', $boucle->group)) . ')') . ', # GROUP array(' . calculer_order($boucle) . @@ -330,7 +332,7 @@ function calculer_dump_array($a) " : " . calculer_dump_array($a[3]) . ")"); else { - foreach($a as $k => $v) $res .= ", " . calculer_dump_array($v); + foreach($a as $v) $res .= ", " . calculer_dump_array($v); return "\n\t\t\tarray(" . substr($res,2) . ')'; } } diff --git a/ecrire/public/composer.php b/ecrire/public/composer.php index 90fab5b2d7..3c5f044ebf 100644 --- a/ecrire/public/composer.php +++ b/ecrire/public/composer.php @@ -472,7 +472,7 @@ function lang_parametres_forum($qs, $lang) { // http://doc.spip.org/@calculer_select function calculer_select ($select = array(), $from = array(), $where = array(), $join=array(), - $groupby = '', $orderby = array(), $limit = '', + $groupby = array(), $orderby = array(), $limit = '', $having=array(), $table = '', $id = '', $serveur='') { // retirer les criteres vides: @@ -507,7 +507,7 @@ function calculer_select ($select = array(), $from = array(), OR calculer_jointnul($cle, $select) OR calculer_jointnul($cle, $join) OR calculer_jointnul($cle, $where)) - $sfrom = " INNER JOIN " . $from[$cle] . " AS $cle ON $t.$c=$cle.$c" . $sfrom; + $sfrom = "\n\tINNER JOIN " . $from[$cle] . " AS $cle ON $t.$c=$cle.$c" . $sfrom; else { unset($join[$k]);} unset($from[$cle]); } diff --git a/ecrire/public/criteres.php b/ecrire/public/criteres.php index ee3e8059d6..c1aaa3ea8f 100644 --- a/ecrire/public/criteres.php +++ b/ecrire/public/criteres.php @@ -243,14 +243,10 @@ function critere_branche_dist($idb, &$boucles, $crit) { $desc = $boucle->show; //Seulement si necessaire if (!array_key_exists('id_rubrique', $desc['field'])) { - $cle = trouver_champ_exterieur('id_rubrique', $boucle->jointures, $boucle); - if ($cle) - $cle = calculer_jointure($boucle, array($boucle->id_table, $desc), $cle, false); - } + $cle = trouver_jointure_champ('id_rubrique', $boucle); + } else $cle = $boucle->id_table; - $c = "sql_in('" . - ($cle ? "L$cle" : $boucle->id_table) . - ".id_rubrique', calcul_branche($arg), '')"; + $c = "sql_in('" . $cle . ".id_rubrique', calcul_branche($arg), '')"; if ($crit->cond) $c = "($arg ? $c : 1)"; if ($not) @@ -378,8 +374,12 @@ function critere_parinverse($idb, &$boucles, $crit, $sens='') { } // par titre_mot ou type_mot voire d'autres else if (isset($exceptions_des_jointures[$par])) { - $order = critere_par_jointure($boucle, $exceptions_des_jointures[$par]); - } + list($table, $champ) = $exceptions_des_jointures[$par]; + $t = array_search($table, $boucle->from); + if (!$t) $t = trouver_jointure_champ($champ, $boucle); + if (!$t) erreur_squelette(_T('zbug_info_erreur_squelette'), "{par ?} BOUCLE$idb"); + $order = "'" . $t . '.' . $champ . "'"; + } else if ($par == 'date' AND isset($GLOBALS['table_date'][$boucle->type_requete])) { $m = $GLOBALS['table_date'][$boucle->type_requete]; @@ -409,24 +409,6 @@ function critere_parinverse($idb, &$boucles, $crit, $sens='') { } } -// http://doc.spip.org/@critere_par_jointure -function critere_par_jointure(&$boucle, $join) -{ - list($table, $champ) = $join; - $t = array_search($table, $boucle->from); - if (!$t) { - $type = $boucle->type_requete; - $desc = $boucle->show; - $cle = trouver_champ_exterieur($champ, $boucle->jointures, $boucle); - - if ($cle) - $cle = calculer_jointure($boucle, array($desc['table'], $desc), $cle, false); - if ($cle) $t = "L$cle"; - else erreur_squelette(_T('zbug_info_erreur_squelette'), "{par ?} BOUCLE$idb"); - - } - return "'" . $t . '.' . $champ . "'"; -} // {inverse} // http://www.spip.net/@inverse @@ -838,172 +820,6 @@ function trouver_champ($champ, $where) } } -// deduction automatique d'une chaine de jointures - -// http://doc.spip.org/@calculer_jointure -function calculer_jointure(&$boucle, $depart, $arrivee, $col='', $cond=false) -{ - - $res = calculer_chaine_jointures($boucle, $depart, $arrivee); - if (!$res) return ""; - - list($nom,$desc) = $depart; - return fabrique_jointures($boucle, $res, $cond, $desc, $nom, $col); -} - -function fabrique_jointures(&$boucle, $res, $cond=false, $desc, $nom='', $col='') -{ - static $num=array(); - $id_table = ""; - $cpt = &$num[$boucle->descr['nom']][$boucle->id_boucle]; - foreach($res as $r) { - list($d, $a, $j) = $r; - if (!$id_table) $id_table = $d; - $n = ++$cpt; - $boucle->join[$n]= array("'$id_table'","'$j'"); - $boucle->from[$id_table = "L$n"] = $a[0]; - } - - - // pas besoin de group by - // (cf http://article.gmane.org/gmane.comp.web.spip.devel/30555) - // si une seule jointure et sur une table avec primary key formee - // de l'index principal et de l'index de jointure (non conditionnel! [6031]) - // et operateur d'egalite (http://trac.rezo.net/trac/spip/ticket/477) - - if ($pk = ((count($boucle->from) == 2) && !$cond)) { - if ($pk = $a[1]['key']['PRIMARY KEY']) { - $id_primary = $desc['key']['PRIMARY KEY']; - $pk = (preg_match("/^$id_primary, *$col$/", $pk) OR - preg_match("/^$col, *$id_primary$/", $pk)); - } - } - - // la clause Group by est en conflit avec ORDER BY, a completer - if (!$pk) foreach(liste_champs_jointures($nom,$desc) as $id_prim){ - $id_field = $nom . '.' . $id_prim; - if (!in_array($id_field, $boucle->group)) { - $boucle->group[] = $id_field; - // postgres exige que le champ pour GROUP soit dans le SELECT - if (!in_array($id_field, $boucle->select)) - $boucle->select[] = $id_field; - } - } - - $boucle->modificateur['lien'] = true; - return $n; - } - - -// http://doc.spip.org/@liste_champs_jointures -function liste_champs_jointures($nom,$desc){ - - static $nojoin = array('idx','maj','date','statut'); - - // les champs declares explicitement pour les jointures - if (isset($desc['join'])) return $desc['join']; - /*elseif (isset($GLOBALS['tables_principales'][$nom]['join'])) return $GLOBALS['tables_principales'][$nom]['join']; - elseif (isset($GLOBALS['tables_auxiliaires'][$nom]['join'])) return $GLOBALS['tables_auxiliaires'][$nom]['join'];*/ - - // sinon la cle primaire - if (isset($desc['key']['PRIMARY KEY'])) - return split_key($desc['key']['PRIMARY KEY']); - - // ici on se rabat sur les cles secondaires, - // en eliminant celles qui sont pas pertinentes (idx, maj) - // si jamais le resultat n'est pas pertinent pour une table donnee, - // il faut declarer explicitement le champ 'join' de sa description - $join = array(); - foreach($desc['key'] as $v) $join = split_key($v, $join); - foreach($join as $k) if (in_array($k, $nojoin)) unset($join[$k]); - return $join; -} - -function split_key($v, $join = array()) -{ - foreach (preg_split('/,\s*/', $v) as $k) $join[$k] = $k; - return $join; -} - -// http://doc.spip.org/@calculer_chaine_jointures -function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu=array(), $milieu_prec = false) -{ - static $trouver_table; - if (!$trouver_table) - $trouver_table = charger_fonction('trouver_table', 'base'); - - list($dnom,$ddesc) = $depart; - list($anom,$adesc) = $arrivee; - if (!count($vu)) - $vu[] = $dnom; // ne pas oublier la table de depart - - $akeys = $adesc['key']; - if ($v = $akeys['PRIMARY KEY']) { - unset($akeys['PRIMARY KEY']); - $akeys = array_merge(preg_split('/,\s*/', $v), $akeys); - } - if ($keys = liste_champs_jointures($dnom,$ddesc)){ - $v = array_intersect(array_values($keys), $akeys); - } - if ($v) - return array(array($dnom, $arrivee, array_shift($v))); - else { - $new = $vu; - foreach($boucle->jointures as $v) { - if ($v && (!in_array($v,$vu)) && - ($def = $trouver_table($v, $boucle->sql_serveur))) { - $milieu = array_intersect($ddesc['key'], trouver_cles_table($def['key'])); - $new[] = $v; - foreach ($milieu as $k) - if ($k!=$milieu_prec) // ne pas repasser par la meme cle car c'est un chemin inutilement long - { - $r = calculer_chaine_jointures($boucle, array($v, $def), $arrivee, $new, $k); - if ($r) { - array_unshift($r, array($dnom, array($def['table'], $def), $k)); - return $r; - } - } - } - } - } - return array(); -} - -// applatit les cles multiples - -// http://doc.spip.org/@trouver_cles_table -function trouver_cles_table($keys) -{ - $res =array(); - foreach ($keys as $v) { - if (!strpos($v,",")) - $res[$v]=1; - else { - foreach (split(" *, *", $v) as $k) { - $res[$k]=1; - } - } - } - return array_keys($res); -} - -// http://doc.spip.org/@trouver_champ_exterieur -function trouver_champ_exterieur($cle, $joints, &$boucle, $checkarrivee = false) -{ - static $trouver_table; - if (!$trouver_table) - $trouver_table = charger_fonction('trouver_table', 'base'); - - foreach($joints as $k => $join) { - if ($join && $table = $trouver_table($join, $boucle->sql_serveur)) { - if (isset($table['field']) && array_key_exists($cle, $table['field']) - && ($checkarrivee==false || $checkarrivee==$table['table'])) // si on sait ou on veut arriver, il faut que ca colle - return array($table['table'], $table); - } - - } - return ""; -} // determine l'operateur et les operandes diff --git a/ecrire/public/jointures.php b/ecrire/public/jointures.php new file mode 100644 index 0000000000..24595e02c9 --- /dev/null +++ b/ecrire/public/jointures.php @@ -0,0 +1,195 @@ +<?php + +/***************************************************************************\ + * SPIP, Systeme de publication pour l'internet * + * * + * Copyright (c) 2001-2007 * + * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * + * * + * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * + * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * +\***************************************************************************/ + +if (!defined("_ECRIRE_INC_VERSION")) return; + +// deduction automatique d'une chaine de jointures + +// http://doc.spip.org/@calculer_jointure +function calculer_jointure(&$boucle, $depart, $arrivee, $col='', $cond=false) +{ + + $res = calculer_chaine_jointures($boucle, $depart, $arrivee); + if (!$res) return ""; + + list($nom,$desc) = $depart; + return fabrique_jointures($boucle, $res, $cond, $desc, $nom, $col); +} + +function fabrique_jointures(&$boucle, $res, $cond=false, $desc=array(), $nom='', $col='') +{ + static $num=array(); + $id_table = ""; + $cpt = &$num[$boucle->descr['nom']][$boucle->id_boucle]; + foreach($res as $r) { + list($d, $a, $j) = $r; + if (!$id_table) $id_table = $d; + $n = ++$cpt; + $boucle->join[$n]= array("'$id_table'","'$j'"); + $boucle->from[$id_table = "L$n"] = $a[0]; + } + + + // pas besoin de group by + // (cf http://article.gmane.org/gmane.comp.web.spip.devel/30555) + // si une seule jointure et sur une table avec primary key formee + // de l'index principal et de l'index de jointure (non conditionnel! [6031]) + // et operateur d'egalite (http://trac.rezo.net/trac/spip/ticket/477) + + if ($pk = ((count($boucle->from) == 2) && !$cond)) { + if ($pk = $a[1]['key']['PRIMARY KEY']) { + $id_primary = $desc['key']['PRIMARY KEY']; + $pk = (preg_match("/^$id_primary, *$col$/", $pk) OR + preg_match("/^$col, *$id_primary$/", $pk)); + } + } + + // la clause Group by est en conflit avec ORDER BY, a completer + if (!$pk) foreach(liste_champs_jointures($nom,$desc) as $id_prim){ + $id_field = $nom . '.' . $id_prim; + if (!in_array($id_field, $boucle->group)) { + $boucle->group[] = $id_field; + // postgres exige que le champ pour GROUP soit dans le SELECT + if (!in_array($id_field, $boucle->select)) { + spip_log("ref $id_field"); + $boucle->select[] = $id_field; + } + } + } + + $boucle->modificateur['lien'] = true; + return $n; + } + + +// http://doc.spip.org/@liste_champs_jointures +function liste_champs_jointures($nom,$desc){ + + static $nojoin = array('idx','maj','date','statut'); + + // les champs declares explicitement pour les jointures + if (isset($desc['join'])) return $desc['join']; + /*elseif (isset($GLOBALS['tables_principales'][$nom]['join'])) return $GLOBALS['tables_principales'][$nom]['join']; + elseif (isset($GLOBALS['tables_auxiliaires'][$nom]['join'])) return $GLOBALS['tables_auxiliaires'][$nom]['join'];*/ + + // sinon la cle primaire + if (isset($desc['key']['PRIMARY KEY'])) + return split_key($desc['key']['PRIMARY KEY']); + + // ici on se rabat sur les cles secondaires, + // en eliminant celles qui sont pas pertinentes (idx, maj) + // si jamais le resultat n'est pas pertinent pour une table donnee, + // il faut declarer explicitement le champ 'join' de sa description + $join = array(); + if (is_array($desc['key']))foreach($desc['key'] as $v) $join = split_key($v, $join); + foreach($join as $k) if (in_array($k, $nojoin)) unset($join[$k]); + return $join; +} + +function split_key($v, $join = array()) +{ + foreach (preg_split('/,\s*/', $v) as $k) $join[$k] = $k; + return $join; +} + +// http://doc.spip.org/@calculer_chaine_jointures +function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu=array(), $milieu_prec = false) +{ + static $trouver_table; + if (!$trouver_table) + $trouver_table = charger_fonction('trouver_table', 'base'); + + list($dnom,$ddesc) = $depart; + list($anom,$adesc) = $arrivee; + if (!count($vu)) + $vu[] = $dnom; // ne pas oublier la table de depart + + $akeys = $adesc['key']; + if ($v = $akeys['PRIMARY KEY']) { + unset($akeys['PRIMARY KEY']); + $akeys = array_merge(preg_split('/,\s*/', $v), $akeys); + } + + if ($keys = liste_champs_jointures($dnom,$ddesc)){ + $v = array_intersect(array_values($keys), $akeys); + } + if ($v) + return array(array($dnom, $arrivee, array_shift($v))); + else { + $new = $vu; + foreach($boucle->jointures as $v) { + if ($v && (!in_array($v,$vu)) && + ($def = $trouver_table($v, $boucle->sql_serveur))) { + $milieu = array_intersect($ddesc['key'], trouver_cles_table($def['key'])); + $new[] = $v; + foreach ($milieu as $k) + if ($k!=$milieu_prec) // ne pas repasser par la meme cle car c'est un chemin inutilement long + { + $r = calculer_chaine_jointures($boucle, array($v, $def), $arrivee, $new, $k); + if ($r) { + array_unshift($r, array($dnom, array($def['table'], $def), $k)); + return $r; + } + } + } + } + } + return array(); +} + +// applatit les cles multiples + +// http://doc.spip.org/@trouver_cles_table +function trouver_cles_table($keys) +{ + $res =array(); + foreach ($keys as $v) { + if (!strpos($v,",")) + $res[$v]=1; + else { + foreach (split(" *, *", $v) as $k) { + $res[$k]=1; + } + } + } + return array_keys($res); +} + +// http://doc.spip.org/@trouver_champ_exterieur +function trouver_champ_exterieur($cle, $joints, &$boucle, $checkarrivee = false) +{ + static $trouver_table; + if (!$trouver_table) + $trouver_table = charger_fonction('trouver_table', 'base'); + + foreach($joints as $k => $join) { + if ($join && $table = $trouver_table($join, $boucle->sql_serveur)) { + if (isset($table['field']) && array_key_exists($cle, $table['field']) + && ($checkarrivee==false || $checkarrivee==$table['table'])) // si on sait ou on veut arriver, il faut que ca colle + return array($table['table'], $table); + } + } + return ""; +} + +function trouver_jointure_champ($champ, $boucle) +{ + $cle = trouver_champ_exterieur($champ, $boucle->jointures, $boucle); + if ($cle) { + $desc = $boucle->show; + $cle = calculer_jointure($boucle, array($desc['id_table'], $desc), $cle, false); + } + if ($cle) return "L$cle"; + spip_log("trouver_jointure_champ: $champ inconnu"); + return ''; +} +?> -- GitLab