3 /* *************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2012 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
14 * Ce fichier contient les fonctions gerant
15 * les instructions SQL pour Sqlite
17 * @package SPIP\SQL\SQLite
20 if (!defined('_ECRIRE_INC_VERSION')) return;
22 // TODO: get/set_caracteres ?
27 * regroupe le maximum de fonctions qui peuvent cohabiter
28 * D'abord les fonctions d'abstractions de SPIP
31 // http://doc.spip.org/@req_sqlite_dist
32 function req_sqlite_dist($addr, $port, $login, $pass, $db = '', $prefixe = '', $sqlite_version = ''){
33 static $last_connect = array();
35 // si provient de selectdb
36 // un code pour etre sur que l'on vient de select_db()
37 if (strpos($db, $code = '@selectdb@')!==false){
38 foreach (array('addr', 'port', 'login', 'pass', 'prefixe') as $a){
39 $
$a = $last_connect[$a];
41 $db = str_replace($code, '', $db);
45 * En sqlite, seule l'adresse du fichier est importante.
48 * (_DIR_DB si $addr est vide)
52 // determiner le dossier de la base : $addr ou _DIR_DB
54 if ($addr AND strpos($addr, '/')!==false)
55 $f = rtrim($addr, '/').'/';
57 // un nom de base demande et impossible d'obtenir la base, on s'en va :
58 // il faut que la base existe ou que le repertoire parent soit writable
59 if ($db AND !is_file($f .= $db.'.sqlite') AND !is_writable(dirname($f))){
60 spip_log("base $f non trouvee ou droits en ecriture manquants", 'sqlite.'._LOG_HS
);
64 // charger les modules sqlite au besoin
65 if (!_sqlite_charger_version($sqlite_version)){
66 spip_log("Impossible de trouver/charger le module SQLite ($sqlite_version)!", 'sqlite.'._LOG_HS
);
70 // chargement des constantes
71 // il ne faut pas definir les constantes avant d'avoir charge les modules sqlite
72 $define = "spip_sqlite".$sqlite_version."_constantes";
78 // base temporaire tant qu'on ne connait pas son vrai nom
79 // pour tester la connexion
80 $db = "_sqlite".$sqlite_version."_install";
81 $tmp = _DIR_DB
.$db.".sqlite";
82 if ($sqlite_version==3){
83 $ok = $link = new PDO("sqlite:$tmp");
85 $ok = $link = sqlite_open($tmp, _SQLITE_CHMOD
, $err);
88 // Ouvrir (eventuellement creer la base)
89 // si pas de version fourni, on essaie la 3, sinon la 2
90 if ($sqlite_version==3){
91 $ok = $link = new PDO("sqlite:$f");
93 $ok = $link = sqlite_open($f, _SQLITE_CHMOD
, $err);
98 $e = sqlite_last_error($db);
99 spip_log("Impossible d'ouvrir la base SQLite($sqlite_version) $f : $e", 'sqlite.'._LOG_HS
);
104 $last_connect = array(
110 'prefixe' => $prefixe,
112 // etre sur qu'on definit bien les fonctions a chaque nouvelle connexion
113 include_spip('req/sqlite_fonctions');
114 _sqlite_init_functions($link);
119 'prefixe' => $prefixe ?
$prefixe : $db,
126 * Fonction de requete generale, munie d'une trace a la demande
128 * @param string $query
130 * @param string $serveur
132 * @param bool $requeter
133 * Effectuer la requete ?
134 * - true pour executer
135 * - false pour retourner le texte de la requete
136 * @return bool|SQLiteResult|string
137 * Resultat de la requete
139 function spip_sqlite_query($query, $serveur = '', $requeter = true){
140 #spip_log("spip_sqlite_query() > $query",'sqlite.'._LOG_DEBUG);
141 #_sqlite_init(); // fait la premiere fois dans spip_sqlite
142 $query = spip_sqlite
::traduire_requete($query, $serveur);
143 if (!$requeter) return $query;
144 return spip_sqlite
::executer_requete($query, $serveur);
148 /* ordre alphabetique pour les autres */
150 // http://doc.spip.org/@spip_sqlite_alter
151 function spip_sqlite_alter($query, $serveur = '', $requeter = true){
153 $query = spip_sqlite_query("ALTER $query", $serveur, false);
154 // traduire la requete pour recuperer les bons noms de table
155 $query = spip_sqlite
::traduire_requete($query, $serveur);
158 * la il faut faire les transformations
159 * si ALTER TABLE x (DROP|CHANGE) y
161 * 1) recuperer "ALTER TABLE table "
162 * 2) spliter les sous requetes (,)
163 * 3) faire chaque requete independemment
167 if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
172 spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite.'._LOG_ERREUR
);
177 // il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
178 // tout en cassant "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"... en deux
179 // ou revoir l'api de sql_alter en creant un
180 // sql_alter_table($table,array($actions));
181 $todo = explode(',', $suite);
183 // on remet les morceaux dechires ensembles... que c'est laid !
187 while ($do = array_shift($todo)){
188 $todo2[$i] = isset($todo2[$i]) ?
$todo2[$i].",".$do : $do;
189 $o = (false!==strpos($do, "("));
190 $f = (false!==strpos($do, ")"));
191 if ($o AND !$f) $ouverte = true;
192 elseif ($f) $ouverte = false;
197 $resultats = array();
198 foreach ($todo2 as $do){
200 if (!preg_match('/(DROP PRIMARY KEY|DROP KEY|DROP INDEX|DROP COLUMN|DROP'
201 .'|CHANGE COLUMN|CHANGE|MODIFY|RENAME TO|RENAME'
202 .'|ADD PRIMARY KEY|ADD KEY|ADD INDEX|ADD UNIQUE KEY|ADD UNIQUE'
204 .')\s*([^\s]*)\s*(.*)?/i', $do, $matches)){
205 spip_log("SQLite : Probleme de ALTER TABLE, utilisation non reconnue dans : $do \n(requete d'origine : $query)", 'sqlite.'._LOG_ERREUR
);
209 $cle = strtoupper($matches[1]);
210 $colonne_origine = $matches[2];
211 $colonne_destination = '';
215 // eluder une eventuelle clause before|after|first inutilisable
216 $defr = rtrim(preg_replace('/(BEFORE|AFTER|FIRST)(.*)$/is', '', $def));
217 $defo = $defr; // garder la def d'origine pour certains cas
218 // remplacer les definitions venant de mysql
219 $defr = _sqlite_remplacements_definitions_table($defr);
221 // reinjecter dans le do
222 $do = str_replace($def, $defr, $do);
226 // suppression d'un index
229 $nom_index = $colonne_origine;
230 spip_sqlite_drop_index($nom_index, $table, $serveur);
233 // suppression d'une pk
234 case 'DROP PRIMARY KEY':
235 if (!_sqlite_modifier_table(
238 array('key' => array('PRIMARY KEY' => '')),
243 // suppression d'une colonne
246 if (!_sqlite_modifier_table(
248 array($colonne_origine => ""),
255 case 'CHANGE COLUMN':
257 // recuperer le nom de la future colonne
258 // on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
259 // en tenant compte de la cle primaire (ce qui est mieux)
261 $colonne_destination = substr($def, 0, strpos($def, ' '));
262 $def = substr($def, strlen($colonne_destination)+
1);
264 if (!_sqlite_modifier_table(
266 array($colonne_origine => $colonne_destination),
267 array('field' => array($colonne_destination => $def)),
274 // on reprend la def d'origine car _sqlite_modifier_table va refaire la translation
275 // en tenant compte de la cle primaire (ce qui est mieux)
276 if (!_sqlite_modifier_table(
279 array('field' => array($colonne_origine => $defo)),
285 // pas geres en sqlite2
287 $do = "RENAME TO".substr($do, 6);
289 if (_sqlite_is_version(3, '', $serveur)){
290 if (!spip_sqlite
::executer_requete("$debut $do", $serveur)){
291 spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR
);
294 // artillerie lourde pour sqlite2 !
296 $table_dest = trim(substr($do, 9));
297 if (!_sqlite_modifier_table(array($table => $table_dest), '', '', $serveur)){
298 spip_log("SQLite : Erreur ALTER TABLE / RENAME : $query", 'sqlite.'._LOG_ERREUR
);
305 case 'ADD PRIMARY KEY':
306 $pk = trim(substr($do, 16));
307 $pk = ($pk[0]=='(') ?
substr($pk, 1, -1) : $pk;
308 if (!_sqlite_modifier_table(
311 array('key' => array('PRIMARY KEY' => $pk)),
317 case 'ADD UNIQUE KEY':
322 // peut etre "(colonne)" ou "nom_index (colonnes)"
323 // bug potentiel si qqn met "(colonne, colonne)"
325 // nom_index (colonnes)
327 $colonnes = substr($def, 1, -1);
328 $nom_index = $colonne_origine;
332 if ($colonne_origine[0]=="("){
333 $colonnes = substr($colonne_origine, 1, -1);
334 if (false!==strpos(",", $colonnes)){
335 spip_log(_LOG_GRAVITE_ERREUR
, "SQLite : Erreur, impossible de creer un index sur plusieurs colonnes"
336 ." sans qu'il ait de nom ($table, ($colonnes))", 'sqlite');
339 $nom_index = $colonnes;
344 $nom_index = $colonnes = $colonne_origine;
347 spip_sqlite_create_index($nom_index, $table, $colonnes, $unique, $serveur);
350 // pas geres en sqlite2
352 $do = "ADD".substr($do, 10);
355 if (_sqlite_is_version(3, '', $serveur) AND !preg_match(',primary\s+key,i',$do)){
356 if (!spip_sqlite
::executer_requete("$debut $do", $serveur)){
357 spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR
);
363 // artillerie lourde pour sqlite2 !
364 // ou si la colonne est aussi primary key
365 // cas du add id_truc int primary key
366 // ajout d'une colonne qui passe en primary key directe
368 $def = trim(substr($do, 3));
369 $colonne_ajoutee = substr($def, 0, strpos($def, ' '));
370 $def = substr($def, strlen($colonne_ajoutee)+
1);
372 if (preg_match(',primary\s+key,i',$def)){
373 $opts['key'] = array('PRIMARY KEY' => $colonne_ajoutee);
374 $def = preg_replace(',primary\s+key,i','',$def);
376 $opts['field'] = array($colonne_ajoutee => $def);
377 if (!_sqlite_modifier_table($table, array($colonne_ajoutee), $opts, $serveur)){
378 spip_log("SQLite : Erreur ALTER TABLE / ADD : $query", 'sqlite.'._LOG_ERREUR
);
384 // tout est bon, ouf !
385 spip_log("SQLite ($serveur) : Changements OK : $debut $do", 'sqlite.'._LOG_INFO
);
388 spip_log("SQLite ($serveur) : fin ALTER TABLE OK !", 'sqlite.'._LOG_INFO
);
394 * Fonction de creation d'une table SQL nommee $nom
395 * http://doc.spip.org/@spip_sqlite_create
398 * @param array $champs
400 * @param bool $autoinc
401 * @param bool $temporary
402 * @param string $serveur
403 * @param bool $requeter
404 * @return bool|SQLiteResult|string
406 function spip_sqlite_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $serveur = '', $requeter = true){
407 $query = _sqlite_requete_create($nom, $champs, $cles, $autoinc, $temporary, $ifnotexists = true, $serveur, $requeter);
408 if (!$query) return false;
409 $res = spip_sqlite_query($query, $serveur, $requeter);
411 // SQLite ne cree pas les KEY sur les requetes CREATE TABLE
412 // il faut donc les faire creer ensuite
413 if (!$requeter) return $res;
415 $ok = $res ?
true : false;
417 foreach ($cles as $k => $v){
418 if (preg_match(',^(KEY|UNIQUE)\s,i',$k,$m)){
419 $index = trim(substr($k,strlen($m[1])));
420 $unique = (strlen($m[1])>3);
421 $ok &= spip_sqlite_create_index($index, $nom, $v, $unique, $serveur);
425 return $ok ?
true : false;
429 * Fonction pour creer une base de donnees SQLite
431 * @param string $nom le nom de la base (sans l'extension de fichier)
432 * @param string $serveur le nom de la connexion
433 * @param string $option options
435 * @return bool true si la base est creee.
437 function spip_sqlite_create_base($nom, $serveur = '', $option = true){
439 if (strpos($nom, "/")===false)
441 if (_sqlite_is_version(2, '', $serveur)){
442 $ok = sqlite_open($f, _SQLITE_CHMOD
, $err);
444 $ok = new PDO("sqlite:$f");
456 * Fonction de creation d'une vue SQL nommee $nom
457 * http://doc.spip.org/@spip_sqlite_create_view
460 * Nom de la vue a creer
461 * @param string $query_select
462 * Texte de la requete de selection servant de base a la vue
463 * @param string $serveur
465 * @param bool $requeter
466 * Effectuer la requete ?
467 * - true pour executer
468 * - false pour retourner le texte de la requete
469 * @return bool|SQLiteResult|string
470 * Resultat de la requete ou
471 * - false si erreur ou si la vue existe deja
472 * - string texte de la requete si $requeter vaut false
474 function spip_sqlite_create_view($nom, $query_select, $serveur = '', $requeter = true){
475 if (!$query_select) return false;
477 if (sql_showtable($nom, false, $serveur)){
478 spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", 'sqlite.'._LOG_ERREUR
);
482 $query = "CREATE VIEW $nom AS ".$query_select;
483 return spip_sqlite_query($query, $serveur, $requeter);
487 * Fonction de creation d'un INDEX
489 * @param string $nom : nom de l'index
490 * @param string $table : table sql de l'index
491 * @param string/array $champs : liste de champs sur lesquels s'applique l'index
492 * @param string $serveur : nom de la connexion sql utilisee
493 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
495 * @return bool ou requete
497 function spip_sqlite_create_index($nom, $table, $champs, $unique='', $serveur = '', $requeter = true){
498 if (!($nom OR $table OR $champs)){
499 spip_log("Champ manquant pour creer un index sqlite ($nom, $table, (".join(',', $champs)."))", 'sqlite.'._LOG_ERREUR
);
503 // SQLite ne differentie pas noms des index en fonction des tables
504 // il faut donc creer des noms uniques d'index pour une base sqlite
505 $nom = $table.'_'.$nom;
506 // enlever d'eventuelles parentheses deja presentes sur champs
507 if (!is_array($champs)){
508 if ($champs[0]=="(") $champs = substr($champs, 1, -1);
509 $champs = array($champs);
510 // supprimer l'info de longueur d'index mysql en fin de champ
511 $champs = preg_replace(",\(\d+\)$,","",$champs);
515 if (_sqlite_is_version(2, '', $serveur)){
516 /* simuler le IF EXISTS - version 2 */
517 $a = spip_sqlite_showtable($table, $serveur);
518 if (isset($a['key']['KEY '.$nom])) return true;
520 $ifnotexists = ' IF NOT EXISTS';
523 $query = "CREATE ".($unique?
"UNIQUE ":"")."INDEX$ifnotexists $nom ON $table (".join(',', $champs).")";
524 $res = spip_sqlite_query($query, $serveur, $requeter);
525 if (!$requeter) return $res;
533 * en PDO/sqlite3, il faut calculer le count par une requete count(*)
534 * pour les resultats de SELECT
535 * cela est fait sans spip_sqlite_query()
536 * http://doc.spip.org/@spip_sqlite_count
539 * @param string $serveur
540 * @param bool $requeter
543 function spip_sqlite_count($r, $serveur = '', $requeter = true){
546 if (_sqlite_is_version(3, '', $serveur)){
547 // select ou autre (insert, update,...) ?
549 // (link,requete) a compter
550 if (is_array($r->spipSqliteRowCount
)){
551 list($link,$query) = $r->spipSqliteRowCount
;
552 // amelioration possible a tester intensivement : pas de order by pour compter !
553 // $query = preg_replace(",ORDER BY .+(LIMIT\s|HAVING\s|GROUP BY\s|$),Uims","\\1",$query);
554 $query = "SELECT count(*) as zzzzsqlitecount FROM ($query)";
555 $l = $link->query($query);
557 if ($l AND $z = $l->fetch())
558 $i = $z['zzzzsqlitecount'];
559 $r->spipSqliteRowCount
= $i;
561 if (isset($r->spipSqliteRowCount
)){
562 // Ce compte est faux s'il y a des limit dans la requete :(
563 // il retourne le nombre d'enregistrements sans le limit
564 return $r->spipSqliteRowCount
;
566 return $r->rowCount();
569 return sqlite_num_rows($r);
574 // http://doc.spip.org/@spip_sqlite_countsel
575 function spip_sqlite_countsel($from = array(), $where = array(), $groupby = '', $having = array(), $serveur = '', $requeter = true){
576 $c = !$groupby ?
'*' : ('DISTINCT '.(is_string($groupby) ?
$groupby : join(',', $groupby)));
577 $r = spip_sqlite_select("COUNT($c)", $from, $where, '', '', '',
578 $having, $serveur, $requeter);
579 if ((is_resource($r) or is_object($r)) && $requeter){ // ressource : sqlite2, object : sqlite3
580 if (_sqlite_is_version(3, '', $serveur)){
581 list($r) = spip_sqlite_fetch($r, SPIP_SQLITE3_NUM
, $serveur);
583 list($r) = spip_sqlite_fetch($r, SPIP_SQLITE2_NUM
, $serveur);
591 // http://doc.spip.org/@spip_sqlite_delete
592 function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true){
593 $res = spip_sqlite_query(
594 _sqlite_calculer_expression('DELETE FROM', $table, ',')
595 ._sqlite_calculer_expression('WHERE', $where),
596 $serveur, $requeter);
598 // renvoyer la requete inerte si demandee
599 if (!$requeter) return $res;
602 $link = _sqlite_link($serveur);
603 if (_sqlite_is_version(3, $link)){
604 return $res->rowCount();
606 return sqlite_changes($link);
614 // http://doc.spip.org/@spip_sqlite_drop_table
615 function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter = true){
616 if ($exist) $exist = " IF EXISTS";
618 /* simuler le IF EXISTS - version 2 */
619 if ($exist && _sqlite_is_version(2, '', $serveur)){
620 $a = spip_sqlite_showtable($table, $serveur);
621 if (!$a) return true;
624 if (spip_sqlite_query("DROP TABLE$exist $table", $serveur, $requeter))
632 * http://doc.spip.org/@spip_sqlite_drop_view
635 * @param string $exist
636 * @param string $serveur
637 * @param bool $requeter
638 * @return bool|SQLiteResult|string
640 function spip_sqlite_drop_view($view, $exist = '', $serveur = '', $requeter = true){
641 if ($exist) $exist = " IF EXISTS";
643 /* simuler le IF EXISTS - version 2 */
644 if ($exist && _sqlite_is_version(2, '', $serveur)){
645 $a = spip_sqlite_showtable($view, $serveur);
646 if (!$a) return true;
650 return spip_sqlite_query("DROP VIEW$exist $view", $serveur, $requeter);
654 * Fonction de suppression d'un INDEX
656 * @param string $nom : nom de l'index
657 * @param string $table : table sql de l'index
658 * @param string $serveur : nom de la connexion sql utilisee
659 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
661 * @return bool ou requete
663 function spip_sqlite_drop_index($nom, $table, $serveur = '', $requeter = true){
664 if (!($nom OR $table)){
665 spip_log("Champ manquant pour supprimer un index sqlite ($nom, $table)", 'sqlite.'._LOG_ERREUR
);
669 // SQLite ne differentie pas noms des index en fonction des tables
670 // il faut donc creer des noms uniques d'index pour une base sqlite
671 $index = $table.'_'.$nom;
672 $exist = " IF EXISTS";
674 /* simuler le IF EXISTS - version 2 */
675 if (_sqlite_is_version(2, '', $serveur)){
676 $a = spip_sqlite_showtable($table, $serveur);
677 if (!isset($a['key']['KEY '.$nom])) return true;
681 $query = "DROP INDEX$exist $index";
682 return spip_sqlite_query($query, $serveur, $requeter);
686 * Retourne la dernière erreur generée
689 * nom de la connexion
693 function spip_sqlite_error($query = '', $serveur = ''){
694 $link = _sqlite_link($serveur);
696 if (_sqlite_is_version(3, $link)){
697 $errs = $link->errorInfo();
700 numero SQLState ('HY000' souvent lors d'une erreur)
701 http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
703 numéro d'erreur SQLite (souvent 1 lors d'une erreur)
704 http://www.sqlite.org/c3ref/c_abort.html
706 Le texte du message d'erreur
709 if (ltrim($errs[0],'0')) { // 00000 si pas d'erreur
713 $s = sqlite_error_string(sqlite_last_error($link));
715 $s = ": aucune ressource sqlite (link)";
717 if ($s) spip_log("$s - $query", 'sqlite.'._LOG_ERREUR
);
722 * Retourne le numero de la dernière erreur SQL
724 * Le numéro (en sqlite3/pdo) est un retour ODBC tel que (très souvent) HY000
725 * http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
727 * @param string $serveur
728 * nom de la connexion
731 * 1 ou autre erreur (en sqlite 2)
732 * 'HY000/1' : numéro de l'erreur SQLState / numéro d'erreur interne SQLite (en sqlite 3)
734 function spip_sqlite_errno($serveur = ''){
735 $link = _sqlite_link($serveur);
737 if (_sqlite_is_version(3, $link)){
738 $t = $link->errorInfo();
739 $s = ltrim($t[0],'0'); // 00000 si pas d'erreur
740 if ($s) $s .= ' / ' . $t[1]; // ajoute l'erreur du moteur SQLite
742 $s = sqlite_last_error($link);
744 $s = ": aucune ressource sqlite (link)";
747 if ($s) spip_log("Erreur sqlite $s", 'sqlite.'._LOG_ERREUR
);
753 // http://doc.spip.org/@spip_sqlite_explain
754 function spip_sqlite_explain($query, $serveur = '', $requeter = true){
755 if (strpos(ltrim($query), 'SELECT')!==0) return array();
757 $query = spip_sqlite
::traduire_requete($query, $serveur);
758 $query = 'EXPLAIN '.$query;
759 if (!$requeter) return $query;
760 // on ne trace pas ces requetes, sinon on obtient un tracage sans fin...
761 $r = spip_sqlite
::executer_requete($query, $serveur, false);
763 return $r ?
spip_sqlite_fetch($r, null, $serveur) : false; // hum ? etrange ca... a verifier
767 // http://doc.spip.org/@spip_sqlite_fetch
768 function spip_sqlite_fetch($r, $t = '', $serveur = '', $requeter = true){
770 $link = _sqlite_link($serveur);
771 $is_v3 = _sqlite_is_version(3, $link);
773 $t = ($is_v3 ? SPIP_SQLITE3_ASSOC
: SPIP_SQLITE2_ASSOC
);
777 $retour = ($is_v3 ?
$r->fetch($t) : sqlite_fetch_array($r, $t));
779 // les version 2 et 3 parfois renvoie des 'table.titre' au lieu de 'titre' tout court ! pff !
780 // suppression de 'table.' pour toutes les cles (c'est un peu violent !)
781 // c'est couteux : on ne verifie que la premiere ligne pour voir si on le fait ou non
783 AND strpos(implode('',array_keys($retour)),'.')!==false){
784 foreach ($retour as $cle => $val){
785 if (($pos = strpos($cle, '.'))!==false){
786 $retour[substr($cle, $pos+
1)] = &$retour[$cle];
787 unset($retour[$cle]);
796 function spip_sqlite_seek($r, $row_number, $serveur = '', $requeter = true){
798 $link = _sqlite_link($serveur);
799 if (_sqlite_is_version(3, $link)){
800 // encore un truc de bien fichu : PDO ne PEUT PAS faire de seek ou de rewind...
801 // je me demande si pour sqlite 3 il ne faudrait pas mieux utiliser
802 // les nouvelles fonctions sqlite3_xx (mais encore moins presentes...)
806 return sqlite_seek($r, $row_number);
812 // http://doc.spip.org/@spip_sqlite_free
813 function spip_sqlite_free(&$r, $serveur = '', $requeter = true){
816 //return sqlite_free_result($r);
820 // http://doc.spip.org/@spip_sqlite_get_charset
821 function spip_sqlite_get_charset($charset = array(), $serveur = '', $requeter = true){
822 //$c = !$charset ? '' : (" LIKE "._q($charset['charset']));
823 //return spip_sqlite_fetch(sqlite_query(_sqlite_link($serveur), "SHOW CHARACTER SET$c"), NULL, $serveur);
827 // http://doc.spip.org/@spip_sqlite_hex
828 function spip_sqlite_hex($v){
833 // http://doc.spip.org/@spip_sqlite_in
834 function spip_sqlite_in($val, $valeurs, $not = '', $serveur = '', $requeter = true){
837 while ($n = strpos($valeurs, ',', $n+
1)){
839 $in_sql .= "($val $not IN (".
840 substr($valeurs, 0, $n).
842 ($not ?
"AND\t" : "OR\t");
843 $valeurs = substr($valeurs, $n+
1);
847 $in_sql .= "($val $not IN ($valeurs))";
853 // http://doc.spip.org/@spip_sqlite_insert
854 function spip_sqlite_insert($table, $champs, $valeurs, $desc = '', $serveur = '', $requeter = true){
856 $query = "INSERT INTO $table ".($champs ?
"$champs VALUES $valeurs" : "DEFAULT VALUES");
857 if ($r = spip_sqlite_query($query, $serveur, $requeter)){
858 if (!$requeter) return $r;
859 $nb = spip_sqlite
::last_insert_id($serveur);
864 $err = spip_sqlite_error($query, $serveur);
865 // cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
866 return isset($_GET['var_profile']) ?
$r : $nb;
871 // http://doc.spip.org/@spip_sqlite_insertq
872 function spip_sqlite_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true){
873 if (!$desc) $desc = description_table($table, $serveur);
874 if (!$desc) die("$table insertion sans description");
875 $fields = isset($desc['field']) ?
$desc['field'] : array();
877 foreach ($couples as $champ => $val){
878 $couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
881 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
882 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
884 $cles = $valeurs = "";
885 if (count($couples)){
886 $cles = "(".join(',', array_keys($couples)).")";
887 $valeurs = "(".join(',', $couples).")";
890 return spip_sqlite_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
894 // http://doc.spip.org/@spip_sqlite_insertq_multi
895 function spip_sqlite_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true){
896 if (!$desc) $desc = description_table($table, $serveur);
897 if (!$desc) die("$table insertion sans description");
898 if (!isset($desc['field']))
899 $desc['field'] = array();
901 // recuperer les champs 'timestamp' pour mise a jour auto de ceux-ci
902 $maj = _sqlite_ajouter_champs_timestamp($table, array(), $desc, $serveur);
904 // seul le nom de la table est a traduire ici :
905 // le faire une seule fois au debut
906 $query_start = "INSERT INTO $table ";
907 $query_start = spip_sqlite
::traduire_requete($query_start,$serveur);
909 // ouvrir une transaction
911 spip_sqlite
::demarrer_transaction($serveur);
913 while ($couples = array_shift($tab_couples)){
914 foreach ($couples as $champ => $val){
915 $couples[$champ] = _sqlite_calculer_cite($val, $desc['field'][$champ]);
918 // inserer les champs timestamp par defaut
919 $couples = array_merge($maj,$couples);
921 $champs = $valeurs = "";
922 if (count($couples)){
923 $champs = "(".join(',', array_keys($couples)).")";
924 $valeurs = "(".join(',', $couples).")";
925 $query = $query_start."$champs VALUES $valeurs";
928 $query = $query_start."DEFAULT VALUES";
931 $retour = spip_sqlite
::executer_requete($query,$serveur);
933 // sur le dernier couple uniquement
934 if (!count($tab_couples)){
937 $nb = spip_sqlite
::last_insert_id($serveur);
942 $err = spip_sqlite_error($query, $serveur);
946 spip_sqlite
::finir_transaction($serveur);
948 // renvoie le dernier id d'autoincrement ajoute
949 // cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
950 return isset($_GET['var_profile']) ?
$retour : $nb;
955 * Retourne si le moteur SQL prefere utiliser des transactions.
958 * @return bool true / false
960 function spip_sqlite_preferer_transaction($serveur = '', $requeter = true) {
965 * Demarre une transaction.
966 * Pratique pour des sql_updateq() dans un foreach,
967 * parfois 100* plus rapide s'ils sont nombreux en sqlite !
970 function spip_sqlite_demarrer_transaction($serveur = '', $requeter = true) {
971 if (!$requeter) return "BEGIN TRANSACTION";
972 spip_sqlite
::demarrer_transaction($serveur);
977 * Cloture une transaction.
980 function spip_sqlite_terminer_transaction($serveur = '', $requeter = true) {
981 if (!$requeter) return "COMMIT";
982 spip_sqlite
::finir_transaction($serveur);
987 // http://doc.spip.org/@spip_sqlite_listdbs
988 function spip_sqlite_listdbs($serveur = '', $requeter = true){
991 if (!is_dir($d = substr(_DIR_DB
, 0, -1))){
995 include_spip('inc/flock');
996 $bases = preg_files($d, $pattern = '(.*)\.sqlite$');
999 foreach ($bases as $b){
1000 // pas de bases commencant pas sqlite
1001 // (on s'en sert pour l'installation pour simuler la presence d'un serveur)
1002 // les bases sont de la forme _sqliteX_tmp_spip_install.sqlite
1003 if (strpos($b, '_sqlite')) continue;
1004 $bds[] = preg_replace(";.*/$pattern;iS", '$1', $b);
1011 // http://doc.spip.org/@spip_sqlite_multi
1012 function spip_sqlite_multi($objet, $lang){
1013 $r = "EXTRAIRE_MULTI(" . $objet . ", '" . $lang . "') AS multi";
1019 * Optimise une table SQL
1020 * Note: Sqlite optimise TOUTE un fichier sinon rien.
1021 * On evite donc 2 traitements sur la meme base dans un hit.
1023 * @param $table nom de la table a optimiser
1024 * @param $serveur nom de la connexion
1025 * @param $requeter effectuer la requete ? sinon retourner son code
1026 * @return bool|string true / false / requete
1028 // http://doc.spip.org/@spip_sqlite_optimize
1029 function spip_sqlite_optimize($table, $serveur = '', $requeter = true){
1031 if ($requeter and $do){
1037 return spip_sqlite_query("VACUUM", $serveur, $requeter);
1043 * echapper une valeur selon son type ou au mieux
1044 * comme le fait _q() mais pour sqlite avec ses specificites
1046 * @param string|array|number $v
1047 * @param string $type
1048 * @return string|number
1050 function spip_sqlite_quote($v, $type = ''){
1052 return _sqlite_calculer_cite($v,$type);
1053 // si c'est un tableau, le parcourir en propageant le type
1054 foreach($v as $k=>$r)
1055 $v[$k] = spip_sqlite_quote($r, $type);
1056 return join(",", $v);
1061 * Tester si une date est proche de la valeur d'un champ
1063 * @param string $champ le nom du champ a tester
1064 * @param int $interval valeur de l'interval : -1, 4, ...
1065 * @param string $unite utite utilisee (DAY, MONTH, YEAR, ...)
1066 * @return string expression SQL
1068 function spip_sqlite_date_proche($champ, $interval, $unite){
1069 $op = (($interval <= 0) ?
'>' : '<');
1070 return "($champ $op datetime('".date("Y-m-d H:i:s")."', '$interval $unite'))";
1074 // http://doc.spip.org/@spip_sqlite_replace
1075 function spip_sqlite_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true){
1076 if (!$desc) $desc = description_table($table, $serveur);
1077 if (!$desc) die("$table insertion sans description");
1078 $fields = isset($desc['field']) ?
$desc['field'] : array();
1080 foreach ($couples as $champ => $val){
1081 $couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
1084 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1085 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
1087 return spip_sqlite_query("REPLACE INTO $table (".join(',', array_keys($couples)).') VALUES ('.join(',', $couples).')', $serveur);
1091 // http://doc.spip.org/@spip_sqlite_replace_multi
1092 function spip_sqlite_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true){
1094 // boucler pour trainter chaque requete independemment
1095 foreach ($tab_couples as $couples){
1096 $retour = spip_sqlite_replace($table, $couples, $desc, $serveur, $requeter);
1098 // renvoie le dernier id
1103 // http://doc.spip.org/@spip_sqlite_select
1104 function spip_sqlite_select($select, $from, $where = '', $groupby = '', $orderby = '', $limit = '', $having = '', $serveur = '', $requeter = true){
1106 // version() n'est pas connu de sqlite
1107 $select = str_replace('version()', 'sqlite_version()', $select);
1110 $from = (!is_array($from) ?
$from : _sqlite_calculer_select_as($from));
1113 _sqlite_calculer_expression('SELECT', $select, ', ')
1114 ._sqlite_calculer_expression('FROM', $from, ', ')
1115 ._sqlite_calculer_expression('WHERE', $where)
1116 ._sqlite_calculer_expression('GROUP BY', $groupby, ',')
1117 ._sqlite_calculer_expression('HAVING', $having)
1118 .($orderby ?
("\nORDER BY "._sqlite_calculer_order($orderby)) : '')
1119 .($limit ?
"\nLIMIT $limit" : '');
1121 // dans un select, on doit renvoyer la requête en cas d'erreur
1122 $res = spip_sqlite_query($query, $serveur, $requeter);
1123 // texte de la requete demande ?
1124 if (!$requeter) return $res;
1125 // erreur survenue ?
1126 if ($res === false) {
1127 return spip_sqlite
::traduire_requete($query, $serveur);
1134 * Selectionne un fichier de base de donnees
1136 * @param string $nom
1137 * Nom de la base a utiliser
1138 * @param string $serveur
1140 * @param bool $requeter
1143 * @return bool|string
1144 * Nom de la base en cas de success.
1145 * False en cas d'erreur.
1147 function spip_sqlite_selectdb($db, $serveur = '', $requeter = true){
1150 // interdire la creation d'une nouvelle base,
1151 // sauf si on est dans l'installation
1152 if (!is_file($f = _DIR_DB
.$db.'.sqlite')
1153 && (!defined('_ECRIRE_INSTALL') ||
!_ECRIRE_INSTALL
)){
1154 spip_log("Il est interdit de creer la base $db", 'sqlite.'._LOG_HS
);
1158 // se connecter a la base indiquee
1159 // avec les identifiants connus
1160 $index = $serveur ?
$serveur : 0;
1162 if ($link = spip_connect_db('', '', '', '', '@selectdb@'.$db, $serveur, '', '')){
1163 if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
1166 spip_log("Impossible de selectionner la base $db", 'sqlite.'._LOG_HS
);
1173 // http://doc.spip.org/@spip_sqlite_set_charset
1174 function spip_sqlite_set_charset($charset, $serveur = '', $requeter = true){
1175 # spip_log("Gestion charset sql a ecrire : "."SET NAMES "._q($charset), 'sqlite.'._LOG_ERREUR);
1176 # return spip_sqlite_query("SET NAMES ". spip_sqlite_quote($charset), $serveur); //<-- Passe pas !
1181 * Retourne une ressource de la liste des tables de la base de données
1183 * @param string $match
1184 * Filtre sur tables à récupérer
1185 * @param string $serveur
1186 * Connecteur de la base
1187 * @param bool $requeter
1188 * true pour éxecuter la requête
1189 * false pour retourner le texte de la requête.
1191 * Ressource à utiliser avec sql_fetch()
1193 function spip_sqlite_showbase($match, $serveur = '', $requeter = true){
1194 // type est le type d'entrée : table / index / view
1195 // on ne retourne que les tables (?) et non les vues...
1196 # ESCAPE non supporte par les versions sqlite <3
1197 # return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name LIKE "._q($match)." ESCAPE '\'", $serveur, $requeter);
1198 $match = preg_quote($match);
1199 $match = str_replace("\\\_", "[[TIRETBAS]]", $match);
1200 $match = str_replace("\\\%", "[[POURCENT]]", $match);
1201 $match = str_replace("_", ".", $match);
1202 $match = str_replace("%", ".*", $match);
1203 $match = str_replace("[[TIRETBAS]]", "_", $match);
1204 $match = str_replace("[[POURCENT]]", "%", $match);
1205 $match = "^$match$";
1206 return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name REGEXP "._q($match), $serveur, $requeter);
1210 // http://doc.spip.org/@spip_sqlite_showtable
1211 function spip_sqlite_showtable($nom_table, $serveur = '', $requeter = true){
1213 'SELECT sql, type FROM'
1214 .' (SELECT * FROM sqlite_master UNION ALL'
1215 .' SELECT * FROM sqlite_temp_master)'
1216 ." WHERE tbl_name LIKE '$nom_table'"
1217 ." AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
1218 .' ORDER BY substr(type,2,1), name';
1220 $a = spip_sqlite_query($query, $serveur, $requeter);
1222 if (!$requeter) return $a;
1223 if (!($a = spip_sqlite_fetch($a, null, $serveur))) return "";
1224 $vue = ($a['type']=='view'); // table | vue
1227 // il faut parser le create
1229 if (!preg_match("/^[^(),]*\((([^()]*(\([^()]*\))?[^()]*)*)\)[^()]*$/", array_shift($a), $r))
1233 // extraction d'une KEY éventuelle en prenant garde de ne pas
1234 // relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
1235 if (preg_match("/^(.*?),([^,]*KEY[ (].*)$/s", $desc, $r)){
1245 // enlever les contenus des valeurs DEFAULT 'xxx' qui pourraient perturber
1246 // par exemple s'il contiennent une virgule.
1247 // /!\ cela peut aussi echapper le nom des champs si la table a eu des operations avec SQLite Manager !
1248 list($desc, $echaps) = query_echappe_textes($desc);
1250 // separer toutes les descriptions de champs, separes par des virgules
1251 # /!\ explode peut exploser aussi DECIMAL(10,2) !
1252 $k_precedent = null;
1253 foreach (explode(",", $desc) as $v){
1255 preg_match("/^\s*([^\s]+)\s+(.*)/", $v, $r);
1256 // Les cles de champs peuvent etre entourees
1257 // de guillements doubles " , simples ', graves ` ou de crochets [ ], ou rien.
1258 // http://www.sqlite.org/lang_keywords.html
1259 $k = strtolower(query_reinjecte_textes($r[1], $echaps)); // champ, "champ", [champ]...
1260 if ($char = strpbrk($k[0], '\'"[`')) {
1261 $k = trim($k, $char);
1262 if ($char == '[') $k = rtrim($k, ']');
1264 $def = query_reinjecte_textes($r[2], $echaps); // valeur du champ
1266 # rustine pour DECIMAL(10,2)
1267 if (false !== strpos($k, ')')) {
1268 $fields[$k_precedent] .= ',' . $k . ' ' . $def;
1275 // la primary key peut etre dans une des descriptions de champs
1276 // et non en fin de table, cas encore decouvert avec Sqlite Manager
1277 if (stripos($r[2], 'PRIMARY KEY') !== false) {
1278 $keys['PRIMARY KEY'] = $k;
1281 // key inclues dans la requete
1282 foreach (preg_split('/\)\s*,?/', $namedkeys) as $v){
1283 if (preg_match("/^\s*([^(]*)\((.*)$/", $v, $r)){
1284 $k = str_replace("`", '', trim($r[1]));
1285 $t = trim(strtolower(str_replace("`", '', $r[2])), '"');
1286 if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
1289 // sinon ajouter les key index
1291 'SELECT name,sql FROM'
1292 .' (SELECT * FROM sqlite_master UNION ALL'
1293 .' SELECT * FROM sqlite_temp_master)'
1294 ." WHERE tbl_name LIKE '$nom_table'"
1295 ." AND type='index' AND name NOT LIKE 'sqlite_%'"
1296 .'ORDER BY substr(type,2,1), name';
1297 $a = spip_sqlite_query($query, $serveur, $requeter);
1298 while ($r = spip_sqlite_fetch($a, null, $serveur)){
1299 $key = str_replace($nom_table.'_', '', $r['name']); // enlever le nom de la table ajoute a l'index
1300 $colonnes = preg_replace(',.*\((.*)\).*,', '$1', $r['sql']);
1301 $keys['KEY '.$key] = $colonnes;
1304 // c'est une vue, on liste les champs disponibles simplement
1306 if ($res = sql_fetsel('*', $nom_table, '', '', '', '1', '', $serveur)){ // limit 1
1308 foreach ($res as $c => $v) $fields[$c] = '';
1314 return array('field' => $fields, 'key' => $keys);
1319 // http://doc.spip.org/@spip_sqlite_update
1320 function spip_sqlite_update($table, $champs, $where = '', $desc = '', $serveur = '', $requeter = true){
1321 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1322 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1325 foreach ($champs as $champ => $val)
1326 $set[] = $champ."=$val";
1328 return spip_sqlite_query(
1329 _sqlite_calculer_expression('UPDATE', $table, ',')
1330 ._sqlite_calculer_expression('SET', $set, ',')
1331 ._sqlite_calculer_expression('WHERE', $where),
1332 $serveur, $requeter);
1336 // http://doc.spip.org/@spip_sqlite_updateq
1337 function spip_sqlite_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true){
1339 if (!$champs) return;
1340 if (!$desc) $desc = description_table($table, $serveur);
1341 if (!$desc) die("$table insertion sans description");
1342 $fields = $desc['field'];
1344 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1345 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1348 foreach ($champs as $champ => $val){
1349 $set[] = $champ.'='._sqlite_calculer_cite($val, $fields[$champ]);
1351 return spip_sqlite_query(
1352 _sqlite_calculer_expression('UPDATE', $table, ',')
1353 ._sqlite_calculer_expression('SET', $set, ',')
1354 ._sqlite_calculer_expression('WHERE', $where),
1355 $serveur, $requeter);
1361 * Ensuite les fonctions non abstraites
1362 * crees pour l'occasion de sqlite
1368 * fonction pour la premiere connexion a un serveur SQLite
1369 * http://doc.spip.org/@_sqlite_init
1373 function _sqlite_init(){
1374 if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC
.'bases/');
1375 if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD
);
1377 if (!is_dir($d = _DIR_DB
)){
1378 include_spip('inc/flock');
1379 sous_repertoire($d);
1385 * teste la version sqlite du link en cours
1386 * http://doc.spip.org/@_sqlite_is_version
1388 * @param string $version
1389 * @param string $link
1390 * @param string $serveur
1391 * @param bool $requeter
1394 function _sqlite_is_version($version = '', $link = '', $serveur = '', $requeter = true){
1395 if ($link==='') $link = _sqlite_link($serveur);
1396 if (!$link) return false;
1397 if ($link instanceof PDO
){
1403 if (!$version) return $v;
1404 return ($version==$v);
1410 * http://doc.spip.org/@_sqlite_link
1412 * @param string $serveur
1415 function _sqlite_link($serveur = ''){
1416 $link = &$GLOBALS['connexions'][$serveur ?
$serveur : 0]['link'];
1421 /* ordre alphabetique pour les autres */
1425 * renvoie les bons echappements (pas sur les fonctions now())
1426 * http://doc.spip.org/@_sqlite_calculer_cite
1428 * @param string|array|number $v
1429 * @param string $type
1430 * @return string|array|number
1432 function _sqlite_calculer_cite($v, $type){
1435 AND stripos($type,"NOT NULL")===false) return 'NULL'; // null php se traduit en NULL SQL
1437 if (sql_test_date($type) AND preg_match('/^\w+\(/', $v))
1439 if (sql_test_int($type)){
1442 elseif (ctype_xdigit(substr($v, 2)) AND strncmp($v, '0x', 2)==0)
1443 return hexdec(substr($v, 2));
1449 // si on ne connait pas le type on le deduit de $v autant que possible
1454 if (function_exists('sqlite_escape_string')){
1455 return "'".sqlite_escape_string($v)."'";
1458 // trouver un link sqlite3 pour faire l'echappement
1459 foreach ($GLOBALS['connexions'] as $s){
1460 if (_sqlite_is_version(3, $l = $s['link'])){
1461 return $l->quote($v);
1465 // echapper les ' en ''
1466 spip_log("Pas de methode sqlite_escape_string ni ->quote pour echapper","sqlite."._LOG_INFO_IMPORTANTE
);
1467 return ("'" . str_replace("'","''",$v) . "'");
1472 * renvoie grosso modo "$expression join($join, $v)"
1473 * http://doc.spip.org/@_sqlite_calculer_expression
1475 * @param $expression
1477 * @param string $join
1480 function _sqlite_calculer_expression($expression, $v, $join = 'AND'){
1484 $exp = "\n$expression ";
1489 if (strtoupper($join)==='AND')
1490 return $exp.join("\n\t$join ", array_map('_sqlite_calculer_where', $v));
1492 return $exp.join($join, $v);
1498 * pour conversion 0+x ? (pas la peine en sqlite)
1499 * http://doc.spip.org/@_sqlite_calculer_order
1504 function _sqlite_calculer_order($orderby){
1505 return (is_array($orderby)) ?
join(", ", $orderby) : $orderby;
1509 // renvoie des 'nom AS alias'
1510 // http://doc.spip.org/@_sqlite_calculer_select_as
1511 function _sqlite_calculer_select_as($args){
1513 foreach ($args as $k => $v){
1514 if (substr($k, -1)=='@'){
1515 // c'est une jointure qui se refere au from precedent
1520 if (!is_numeric($k)){
1521 $p = strpos($v, " ");
1523 $v = substr($v, 0, $p)." AS '$k'".substr($v, $p);
1524 else $v .= " AS '$k'";
1529 return substr($res, 2);
1534 * renvoie les bonnes parentheses pour des where imbriquees
1535 * http://doc.spip.org/@_sqlite_calculer_where
1538 * @return array|mixed|string
1540 function _sqlite_calculer_where($v){
1544 $op = array_shift($v);
1545 if (!($n = count($v)))
1548 $arg = _sqlite_calculer_where(array_shift($v));
1552 $arg2 = _sqlite_calculer_where(array_shift($v));
1554 return "($arg $op $arg2)";
1555 } else return "($arg $op ($arg2) : $v[0])";
1562 * Charger les modules sqlite (si possible) (juste la version demandee),
1563 * ou, si aucune version, renvoie les versions sqlite dispo
1564 * sur ce serveur dans un array
1566 * http://doc.spip.org/@_sqlite_charger_version
1568 * @param string $version
1569 * @return array|bool
1571 function _sqlite_charger_version($version = ''){
1572 $versions = array();
1575 if (!$version ||
$version==2){
1576 if (charger_php_extension('sqlite')){
1582 if (!$version ||
$version==3){
1583 if (charger_php_extension('pdo') && charger_php_extension('pdo_sqlite')){
1587 if ($version) return in_array($version, $versions);
1593 * Gestion des requetes ALTER non reconnues de SQLite :
1594 * ALTER TABLE table DROP column
1595 * ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
1596 * ALTER TABLE table MODIFY column definition
1597 * ALTER TABLE table ADD|DROP PRIMARY KEY
1599 * (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
1601 * 1) creer une table B avec le nouveau format souhaite
1602 * 2) copier la table d'origine A vers B
1603 * 3) supprimer la table A
1604 * 4) renommer la table B en A
1605 * 5) remettre les index (qui sont supprimes avec la table A)
1607 * http://doc.spip.org/@_sqlite_modifier_table
1609 * @param string/array $table : nom_table, array(nom_table=>nom_futur)
1610 * @param string/array $col : nom_colonne, array(nom_colonne=>nom_futur)
1611 * @param array $opt : options comme les tables spip, qui sera merge a la table creee : array('field'=>array('nom'=>'syntaxe', ...), 'key'=>array('KEY nom'=>'colonne', ...))
1612 * @param string $serveur : nom de la connexion sql en cours
1615 function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = ''){
1617 if (is_array($table)){
1619 list($table_origine,$table_destination) = each($table);
1621 $table_origine = $table_destination = $table;
1623 // ne prend actuellement qu'un changement
1624 // mais pourra etre adapte pour changer plus qu'une colonne a la fois
1625 if (is_array($colonne)){
1627 list($colonne_origine,$colonne_destination) = each($colonne);
1629 $colonne_origine = $colonne_destination = $colonne;
1631 if (!isset($opt['field'])) $opt['field'] = array();
1632 if (!isset($opt['key'])) $opt['key'] = array();
1634 // si les noms de tables sont differents, pas besoin de table temporaire
1635 // on prendra directement le nom de la future table
1636 $meme_table = ($table_origine==$table_destination);
1638 $def_origine = sql_showtable($table_origine, false, $serveur);
1639 if (!$def_origine OR !isset($def_origine['field'])){
1640 spip_log("Alter table impossible sur $table_origine : table non trouvee",'sqlite'._LOG_ERREUR
);
1645 $table_tmp = $table_origine.'_tmp';
1647 // 1) creer une table temporaire avec les modifications
1648 // - DROP : suppression de la colonne
1649 // - CHANGE : modification de la colonne
1650 // (foreach pour conserver l'ordre des champs)
1654 // pour le INSERT INTO plus loin
1655 // stocker la correspondance nouvelles->anciennes colonnes
1656 $fields_correspondances = array();
1657 foreach ($def_origine['field'] as $c => $d){
1659 if ($colonne_origine && ($c==$colonne_origine)){
1661 if ($colonne_destination){
1662 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1663 $fields_correspondances[$colonne_destination] = $c;
1667 $fields_correspondances[$c] = $c;
1670 // cas de ADD sqlite2 (ajout du champ en fin de table):
1671 if (!$colonne_origine && $colonne_destination){
1672 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1677 foreach ($def_origine['key'] as $c => $d){
1678 $c = str_replace($colonne_origine, $colonne_destination, $c);
1679 $d = str_replace($colonne_origine, $colonne_destination, $d);
1680 // seulement si on ne supprime pas la colonne !
1685 // autres keys, on merge
1686 $keys = array_merge($keys, $opt['key']);
1689 // copier dans destination (si differente de origine), sinon tmp
1690 $table_copie = ($meme_table) ?
$table_tmp : $table_destination;
1691 $autoinc = (isset($keys['PRIMARY KEY'])
1692 AND stripos($keys['PRIMARY KEY'],',')===false
1693 AND stripos($fields[$keys['PRIMARY KEY']],'default')===false);
1695 if ($q = _sqlite_requete_create(
1701 $ifnotexists = true,
1707 // 2) y copier les champs qui vont bien
1708 $champs_dest = join(', ', array_keys($fields_correspondances));
1709 $champs_ori = join(', ', $fields_correspondances);
1710 $queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
1712 // 3) supprimer la table d'origine
1713 $queries[] = "DROP TABLE $table_origine";
1715 // 4) renommer la table temporaire
1716 // avec le nom de la table destination
1719 if (_sqlite_is_version(3, '', $serveur)){
1720 $queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
1722 $queries[] = _sqlite_requete_create(
1728 $ifnotexists = false, // la table existe puisqu'on est dans une transaction
1730 $queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
1731 $queries[] = "DROP TABLE $table_copie";
1735 // 5) remettre les index !
1736 foreach ($keys as $k => $v){
1737 if ($k=='PRIMARY KEY'){
1742 $queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)";
1747 if (count($queries)){
1748 spip_sqlite
::demarrer_transaction($serveur);
1749 // il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
1750 foreach ($queries as $q){
1751 if (!spip_sqlite
::executer_requete($q, $serveur)){
1752 spip_log(_LOG_GRAVITE_ERREUR
, "SQLite : ALTER TABLE table :"
1753 ." Erreur a l'execution de la requete : $q", 'sqlite');
1754 spip_sqlite
::annuler_transaction($serveur);
1758 spip_sqlite
::finir_transaction($serveur);
1767 * http://doc.spip.org/@_sqlite_ref_fonctions
1771 function _sqlite_ref_fonctions(){
1773 'alter' => 'spip_sqlite_alter',
1774 'count' => 'spip_sqlite_count',
1775 'countsel' => 'spip_sqlite_countsel',
1776 'create' => 'spip_sqlite_create',
1777 'create_base' => 'spip_sqlite_create_base',
1778 'create_view' => 'spip_sqlite_create_view',
1779 'date_proche' => 'spip_sqlite_date_proche',
1780 'delete' => 'spip_sqlite_delete',
1781 'drop_table' => 'spip_sqlite_drop_table',
1782 'drop_view' => 'spip_sqlite_drop_view',
1783 'errno' => 'spip_sqlite_errno',
1784 'error' => 'spip_sqlite_error',
1785 'explain' => 'spip_sqlite_explain',
1786 'fetch' => 'spip_sqlite_fetch',
1787 'seek' => 'spip_sqlite_seek',
1788 'free' => 'spip_sqlite_free',
1789 'hex' => 'spip_sqlite_hex',
1790 'in' => 'spip_sqlite_in',
1791 'insert' => 'spip_sqlite_insert',
1792 'insertq' => 'spip_sqlite_insertq',
1793 'insertq_multi' => 'spip_sqlite_insertq_multi',
1794 'listdbs' => 'spip_sqlite_listdbs',
1795 'multi' => 'spip_sqlite_multi',
1796 'optimize' => 'spip_sqlite_optimize',
1797 'query' => 'spip_sqlite_query',
1798 'quote' => 'spip_sqlite_quote',
1799 'replace' => 'spip_sqlite_replace',
1800 'replace_multi' => 'spip_sqlite_replace_multi',
1801 'select' => 'spip_sqlite_select',
1802 'selectdb' => 'spip_sqlite_selectdb',
1803 'set_charset' => 'spip_sqlite_set_charset',
1804 'get_charset' => 'spip_sqlite_get_charset',
1805 'showbase' => 'spip_sqlite_showbase',
1806 'showtable' => 'spip_sqlite_showtable',
1807 'update' => 'spip_sqlite_update',
1808 'updateq' => 'spip_sqlite_updateq',
1809 'preferer_transaction' => 'spip_sqlite_preferer_transaction',
1810 'demarrer_transaction' => 'spip_sqlite_demarrer_transaction',
1811 'terminer_transaction' => 'spip_sqlite_terminer_transaction',
1814 // association de chaque nom http d'un charset aux couples sqlite
1815 // SQLite supporte utf-8 et utf-16 uniquement.
1817 'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci'),
1818 //'utf-16be'=>array('charset'=>'utf16be','collation'=>'UTF-16BE'),// aucune idee de quoi il faut remplir dans es champs la
1819 //'utf-16le'=>array('charset'=>'utf16le','collation'=>'UTF-16LE')
1822 $fonctions['charsets'] = $charsets;
1829 * $query est une requete ou une liste de champs
1830 * http://doc.spip.org/@_sqlite_remplacements_definitions_table
1833 * @param bool $autoinc
1836 function _sqlite_remplacements_definitions_table($query, $autoinc = false){
1837 // quelques remplacements
1838 $num = "(\s*\([0-9]*\))?";
1839 $enum = "(\s*\([^\)]*\))?";
1842 '/enum'.$enum.'/is' => 'VARCHAR(255)',
1843 '/COLLATE \w+_bin/is' => 'COLLATE BINARY',
1844 '/COLLATE \w+_ci/is' => 'COLLATE NOCASE',
1845 '/auto_increment/is' => '',
1846 '/(timestamp .* )ON .*$/is' => '\\1',
1847 '/character set \w+/is' => '',
1848 '/((big|small|medium|tiny)?int(eger)?)'.$num.'\s*unsigned/is' => '\\1 UNSIGNED',
1849 '/(text\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1850 '/((char|varchar)'.$num.'\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1851 '/(datetime\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00 00:00:00'",
1852 '/(date\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00'",
1855 // pour l'autoincrement, il faut des INTEGER NOT NULL PRIMARY KEY
1856 $remplace_autocinc = array(
1857 '/(big|small|medium|tiny)?int(eger)?'.$num.'/is' => 'INTEGER'
1859 // pour les int non autoincrement, il faut un DEFAULT
1860 $remplace_nonautocinc = array(
1861 '/((big|small|medium|tiny)?int(eger)?'.$num.'\s+not\s+null)\s*$/is' => "\\1 DEFAULT 0",
1864 if (is_string($query)){
1865 $query = preg_replace(array_keys($remplace), $remplace, $query);
1866 if ($autoinc OR preg_match(',AUTO_INCREMENT,is',$query))
1867 $query = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query);
1869 $query = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query);
1870 $query = _sqlite_collate_ci($query);
1873 elseif(is_array($query)){
1874 foreach($query as $k=>$q) {
1875 $ai = ($autoinc?
$k==$autoinc:preg_match(',AUTO_INCREMENT,is',$q));
1876 $query[$k] = preg_replace(array_keys($remplace), $remplace, $query[$k]);
1878 $query[$k] = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query[$k]);
1880 $query[$k] = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query[$k]);
1881 $query[$k] = _sqlite_collate_ci($query[$k]);
1889 * Definir la collation d'un champ en fonction de si une collation est deja explicite
1890 * et du par defaut que l'on veut NOCASE
1891 * @param string $champ
1894 function _sqlite_collate_ci($champ){
1895 if (stripos($champ,"COLLATE")!==false)
1897 if (stripos($champ,"BINARY")!==false)
1898 return str_ireplace("BINARY","COLLATE BINARY",$champ);
1899 if (preg_match(",^(char|varchar|(long|small|medium|tiny)?text),i",$champ))
1900 return $champ . " COLLATE NOCASE";
1907 * Creer la requete pour la creation d'une table
1908 * retourne la requete pour utilisation par sql_create() et sql_alter()
1910 * http://doc.spip.org/@_sqlite_requete_create
1915 * @param bool $autoinc
1916 * @param bool $temporary
1917 * @param bool $_ifnotexists
1918 * @param string $serveur
1919 * @param bool $requeter
1920 * @return bool|string
1922 function _sqlite_requete_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $_ifnotexists = true, $serveur = '', $requeter = true){
1923 $query = $keys = $s = $p = '';
1925 // certains plugins declarent les tables (permet leur inclusion dans le dump)
1926 // sans les renseigner (laisse le compilo recuperer la description)
1927 if (!is_array($champs) ||
!is_array($cles))
1930 // sqlite ne gere pas KEY tout court dans une requete CREATE TABLE
1931 // il faut passer par des create index
1932 // Il gere par contre primary key !
1933 // Soit la PK est definie dans les cles, soit dans un champs
1934 $c = ""; // le champ de cle primaire
1935 if (!isset($cles[$pk = "PRIMARY KEY"]) OR !$c = $cles[$pk]){
1936 foreach ($champs as $k => $v){
1937 if (false!==stripos($v, $pk)){
1939 // on n'en a plus besoin dans field, vu que defini dans key
1940 $champs[$k] = preg_replace("/$pk/is", '', $champs[$k]);
1945 if ($c) $keys = "\n\t\t$pk ($c)";
1946 // Pas de DEFAULT 0 sur les cles primaires en auto-increment
1947 if (isset($champs[$c])
1948 AND stripos($champs[$c],"default 0")!==false){
1949 $champs[$c] = trim(str_ireplace("default 0","",$champs[$c]));
1952 $champs = _sqlite_remplacements_definitions_table($champs, $autoinc?
$c:false);
1953 foreach ($champs as $k => $v){
1954 $query .= "$s\n\t\t$k $v";
1960 // simuler le IF NOT EXISTS - version 2
1961 if (_sqlite_is_version(2, '', $serveur)){
1962 $a = spip_sqlite_showtable($nom, $serveur);
1963 if ($a) return false;
1965 // sinon l'ajouter en version 3
1967 $ifnotexists = ' IF NOT EXISTS';
1971 $temporary = $temporary ?
' TEMPORARY' : '';
1972 $q = "CREATE$temporary TABLE$ifnotexists $nom ($query".($keys ?
",$keys" : '').")\n";
1979 * Retrouver les champs 'timestamp'
1980 * pour les ajouter aux 'insert' ou 'replace'
1981 * afin de simuler le fonctionnement de mysql
1983 * stocke le resultat pour ne pas faire
1984 * de requetes showtable intempestives
1986 * http://doc.spip.org/@_sqlite_ajouter_champs_timestamp
1990 * @param string $desc
1991 * @param string $serveur
1994 function _sqlite_ajouter_champs_timestamp($table, $couples, $desc = '', $serveur = ''){
1995 static $tables = array();
1997 if (!isset($tables[$table])){
2000 $trouver_table = charger_fonction('trouver_table', 'base');
2001 $desc = $trouver_table($table, $serveur);
2002 // si pas de description, on ne fait rien, ou on die() ?
2003 if (!$desc) return $couples;
2006 // recherche des champs avec simplement 'TIMESTAMP'
2007 // cependant, il faudra peut etre etendre
2008 // avec la gestion de DEFAULT et ON UPDATE
2009 // mais ceux-ci ne sont pas utilises dans le core
2010 $tables[$table] = array();
2012 foreach ($desc['field'] as $k => $v){
2013 if (strpos(strtolower(ltrim($v)), 'timestamp')===0)
2014 $tables[$table][$k] = "datetime('now')";
2018 // ajout des champs type 'timestamp' absents
2019 return array_merge($tables[$table],$couples);
2024 * renvoyer la liste des versions sqlite disponibles
2026 * http://doc.spip.org/@spip_versions_sqlite
2028 * @return array|bool
2030 function spip_versions_sqlite(){
2031 return _sqlite_charger_version();
2036 static $requeteurs = array();
2037 static $transaction_en_cours = array();
2039 function spip_sqlite(){}
2042 * Retourne une unique instance du requêteur
2044 * Retourne une instance unique du requêteur pour une connexion SQLite
2047 * @param string $serveur
2049 * @return sqlite_requeteur
2050 * Instance unique du requêteur
2052 static function requeteur($serveur){
2053 if (!isset(spip_sqlite
::$requeteurs[$serveur]))
2054 spip_sqlite
::$requeteurs[$serveur] = new sqlite_requeteur($serveur);
2055 return spip_sqlite
::$requeteurs[$serveur];
2058 static function traduire_requete($query, $serveur){
2059 $requeteur = spip_sqlite
::requeteur($serveur);
2060 $traducteur = new sqlite_traducteur($query, $requeteur->prefixe
,$requeteur->sqlite_version
);
2061 return $traducteur->traduire_requete();
2064 static function demarrer_transaction($serveur){
2065 spip_sqlite
::executer_requete("BEGIN TRANSACTION",$serveur);
2066 spip_sqlite
::$transaction_en_cours[$serveur] = true;
2069 static function executer_requete($query, $serveur, $tracer=null){
2070 $requeteur = spip_sqlite
::requeteur($serveur);
2071 return $requeteur->executer_requete($query, $tracer);
2074 static function last_insert_id($serveur){
2075 $requeteur = spip_sqlite
::requeteur($serveur);
2076 return $requeteur->last_insert_id($serveur);
2079 static function annuler_transaction($serveur){
2080 spip_sqlite
::executer_requete("ROLLBACK",$serveur);
2081 spip_sqlite
::$transaction_en_cours[$serveur] = false;
2084 static function finir_transaction($serveur){
2085 // si pas de transaction en cours, ne rien faire et le dire
2086 if (!isset (spip_sqlite
::$transaction_en_cours[$serveur])
2087 OR spip_sqlite
::$transaction_en_cours[$serveur]==false)
2089 // sinon fermer la transaction et retourner true
2090 spip_sqlite
::executer_requete("COMMIT",$serveur);
2091 spip_sqlite
::$transaction_en_cours[$serveur] = false;
2097 * Classe pour partager les lancements de requete
2098 * instanciee une fois par $serveur
2099 * - peut corriger la syntaxe des requetes pour la conformite a sqlite
2100 * - peut tracer les requetes
2103 class sqlite_requeteur
{
2104 var $query = ''; // la requete
2105 var $serveur = ''; // le serveur
2106 var $link = ''; // le link (ressource) sqlite
2107 var $prefixe = ''; // le prefixe des tables
2108 var $db = ''; // le nom de la base
2109 var $tracer = false; // doit-on tracer les requetes (var_profile)
2111 var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2115 * http://doc.spip.org/@sqlite_traiter_requete
2118 * @param string $serveur
2121 function sqlite_requeteur($serveur = ''){
2123 $this->serveur
= strtolower($serveur);
2125 if (!($this->link
= _sqlite_link($this->serveur
)) && (!defined('_ECRIRE_INSTALL') ||
!_ECRIRE_INSTALL
)){
2126 spip_log("Aucune connexion sqlite (link)", 'sqlite.'._LOG_ERREUR
);
2130 $this->sqlite_version
= _sqlite_is_version('', $this->link
);
2132 $this->prefixe
= $GLOBALS['connexions'][$this->serveur ?
$this->serveur
: 0]['prefixe'];
2133 $this->db
= $GLOBALS['connexions'][$this->serveur ?
$this->serveur
: 0]['db'];
2135 // tracage des requetes ?
2136 $this->tracer
= (isset($_GET['var_profile']) && $_GET['var_profile']);
2140 * lancer la requete $query,
2141 * faire le tracage si demande
2142 * http://doc.spip.org/@executer_requete
2144 * @return bool|SQLiteResult
2146 function executer_requete($query, $tracer=null){
2147 if (is_null($tracer))
2148 $tracer = $this->tracer
;
2152 include_spip('public/tracer');
2153 $t = trace_query_start();
2156 # spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ?
2158 // memoriser la derniere erreur PHP vue
2159 $e = (function_exists('error_get_last')?
error_get_last():"");
2160 // sauver la derniere requete
2161 $GLOBALS['connexions'][$this->serveur ?
$this->serveur
: 0]['last'] = $query;
2163 if ($this->sqlite_version
==3){
2164 $r = $this->link
->query($query);
2165 // sauvegarde de la requete (elle y est deja dans $r->queryString)
2166 # $r->spipQueryString = $query;
2168 // comptage : oblige de compter le nombre d'entrees retournees
2169 // par une requete SELECT
2170 // aucune autre solution ne donne le nombre attendu :( !
2171 // particulierement s'il y a des LIMIT dans la requete.
2172 if (strtoupper(substr(ltrim($query), 0, 6))=='SELECT'){
2174 // noter le link et la query pour faire le comptage *si* on en a besoin
2175 $r->spipSqliteRowCount
= array($this->link
,$query);
2177 elseif ($r instanceof PDOStatement
) {
2178 $r->spipSqliteRowCount
= 0;
2183 $r = sqlite_query($this->link
, $query);
2186 // loger les warnings/erreurs eventuels de sqlite remontant dans PHP
2187 if ($err = (function_exists('error_get_last')?
error_get_last():"") AND $err!=$e){
2188 $err = strip_tags($err['message'])." in ".$err['file']." line ".$err['line'];
2189 spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR
);
2198 if (spip_sqlite_errno($this->serveur
))
2199 $err .= spip_sqlite_error($query, $this->serveur
);
2200 return $t ?
trace_query_end($query, $t, $r, $err, $this->serveur
) : $r;
2203 function last_insert_id(){
2204 if ($this->sqlite_version
==3)
2205 return $this->link
->lastInsertId();
2207 return sqlite_last_insert_rowid($this->link
);
2213 * Cette classe est presente essentiellement pour un preg_replace_callback
2214 * avec des parametres dans la fonction appelee que l'on souhaite incrementer
2215 * (fonction pour proteger les textes)
2217 class sqlite_traducteur
{
2219 var $prefixe = ''; // le prefixe des tables
2220 var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2222 // Pour les corrections a effectuer sur les requetes :
2223 var $textes = array(); // array(code=>'texte') trouvé
2225 function sqlite_traducteur($query, $prefixe, $sqlite_version){
2226 $this->query
= $query;
2227 $this->prefixe
= $prefixe;
2228 $this->sqlite_version
= $sqlite_version;
2232 * transformer la requete pour sqlite
2233 * enleve les textes, transforme la requete pour quelle soit
2234 * bien interpretee par sqlite, puis remet les textes
2235 * la fonction affecte $this->query
2236 * http://doc.spip.org/@traduire_requete
2240 function traduire_requete(){
2242 // 1) Protection des textes en les remplacant par des codes
2244 // enlever les 'textes' et initialiser avec
2245 list($this->query
, $textes) = query_echappe_textes($this->query
);
2248 // 2) Corrections de la requete
2250 // Correction Create Database
2251 // Create Database -> requete ignoree
2252 if (strpos($this->query
, 'CREATE DATABASE')===0){
2253 spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.'._LOG_AVERTISSEMENT
);
2254 $this->query
= "SELECT 1";
2257 // Correction Insert Ignore
2258 // INSERT IGNORE -> insert (tout court et pas 'insert or replace')
2259 if (strpos($this->query
, 'INSERT IGNORE')===0){
2260 spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.'._LOG_DEBUG
);
2261 $this->query
= 'INSERT '.substr($this->query
, '13');
2264 // Correction des dates avec INTERVAL
2265 // utiliser sql_date_proche() de preference
2266 if (strpos($this->query
, 'INTERVAL')!==false){
2267 $this->query
= preg_replace_callback("/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U",
2268 array(&$this, '_remplacerDateParTime'),
2272 if (strpos($this->query
, 'LEFT(')!==false){
2273 $this->query
= str_replace('LEFT(','_LEFT(',$this->query
);
2277 // USING (non reconnu en sqlite2)
2278 // problematique car la jointure ne se fait pas du coup.
2279 if (($this->sqlite_version
==2) && (strpos($this->query
, "USING")!==false)){
2280 spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", 'sqlite.'._LOG_ERREUR
);
2281 $this->query
= preg_replace('/USING\s*\([^\)]*\)/', '', $this->query
);
2285 // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2286 if (strpos($this->query
, 'FIELD')!==false){
2287 $this->query
= preg_replace_callback('/FIELD\s*\(([^\)]*)\)/',
2288 array(&$this, '_remplacerFieldParCase'),
2292 // Correction des noms de tables FROM
2293 // mettre les bons noms de table dans from, update, insert, replace...
2294 if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query
, $regs)){
2295 $suite = strstr($this->query
, $regs[0]);
2296 $this->query
= substr($this->query
, 0, -strlen($suite));
2300 $pref = ($this->prefixe
) ?
$this->prefixe
."_" : "";
2301 $this->query
= preg_replace('/([,\s])spip_/S', '\1'.$pref, $this->query
).$suite;
2303 // Correction zero AS x
2304 // pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
2305 // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
2306 // il dit que x ne doit pas être un integer dans le order by !
2307 // on remplace du coup x par vide() dans ce cas uniquement
2309 // rien que pour public/vertebrer.php ?
2310 if ((strpos($this->query
, "0 AS")!==false)){
2311 // on ne remplace que dans ORDER BY ou GROUP BY
2312 if (preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query
, $regs)){
2313 $suite = strstr($this->query
, $regs[0]);
2314 $this->query
= substr($this->query
, 0, -strlen($suite));
2316 // on cherche les noms des x dans 0 AS x
2317 // on remplace dans $suite le nom par vide()
2318 preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query
, $matches, PREG_PATTERN_ORDER
);
2319 foreach ($matches[1] as $m){
2320 $suite = str_replace($m, 'VIDE()', $suite);
2322 $this->query
.= $suite;
2326 // Correction possible des divisions entieres
2327 // Le standard SQL (lequel? ou?) semble indiquer que
2328 // a/b=c doit donner c entier si a et b sont entiers 4/3=1.
2329 // C'est ce que retournent effectivement SQL Server et SQLite
2330 // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
2332 // On peut forcer la conversion en multipliant par 1.0 avant la division
2333 // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
2334 // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
2335 // http://www.sqlite.org/cvstrac/tktview?tn=3202
2336 // (4*1.0/3) n'est pas rendu dans ce cas !
2337 # $this->query = str_replace('/','* 1.00 / ',$this->query);
2340 // Correction critere REGEXP, non reconnu en sqlite2
2341 if (($this->sqlite_version
==2) && (strpos($this->query
, 'REGEXP')!==false)){
2342 $this->query
= preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query
);
2346 // 3) Remise en place des textes d'origine
2348 // Correction Antiquotes et echappements
2350 if (strpos($this->query
,'`')!==false)
2351 $this->query
= str_replace('`','', $this->query
);
2353 $this->query
= query_reinjecte_textes($this->query
, $textes);
2355 return $this->query
;
2361 * remplacer DATE_ / INTERVAL par DATE...strtotime
2362 * http://doc.spip.org/@_remplacerDateParTime
2367 function _remplacerDateParTime($matches){
2368 $op = strtoupper($matches[1]=='ADD') ?
'+' : '-';
2369 return "datetime$matches[2] '$op$matches[3] $matches[4]')";
2373 * callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2374 * http://doc.spip.org/@_remplacerFieldParCase
2379 function _remplacerFieldParCase($matches){
2380 $fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
2381 $t = explode(',', $fields);
2382 $index = array_shift($t);
2388 $res .= "\nWHEN $index=$v THEN $n";
2390 return "CASE $res ELSE 0 END ";