[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / req / sqlite_generique.php
1 <?php
2
3 /* *************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
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 \***************************************************************************/
12
13 /**
14 * Ce fichier contient les fonctions gerant
15 * les instructions SQL pour Sqlite
16 *
17 * @package SPIP\SQL\SQLite
18 */
19
20 if (!defined('_ECRIRE_INC_VERSION')) return;
21
22 // TODO: get/set_caracteres ?
23
24
25 /*
26 *
27 * regroupe le maximum de fonctions qui peuvent cohabiter
28 * D'abord les fonctions d'abstractions de SPIP
29 *
30 */
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();
34
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];
40 }
41 $db = str_replace($code, '', $db);
42 }
43
44 /*
45 * En sqlite, seule l'adresse du fichier est importante.
46 * Ce sera $db le nom,
47 * le path est $addr
48 * (_DIR_DB si $addr est vide)
49 */
50 _sqlite_init();
51
52 // determiner le dossier de la base : $addr ou _DIR_DB
53 $f = _DIR_DB;
54 if ($addr AND strpos($addr, '/')!==false)
55 $f = rtrim($addr, '/').'/';
56
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);
61 return false;
62 }
63
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);
67 return false;
68 }
69
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";
73 $define();
74
75 $ok = false;
76 if (!$db){
77 // si pas de db ->
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");
84 } else {
85 $ok = $link = sqlite_open($tmp, _SQLITE_CHMOD, $err);
86 }
87 } else {
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");
92 } else {
93 $ok = $link = sqlite_open($f, _SQLITE_CHMOD, $err);
94 }
95 }
96
97 if (!$ok){
98 $e = sqlite_last_error($db);
99 spip_log("Impossible d'ouvrir la base SQLite($sqlite_version) $f : $e", 'sqlite.'._LOG_HS);
100 return false;
101 }
102
103 if ($link){
104 $last_connect = array(
105 'addr' => $addr,
106 'port' => $port,
107 'login' => $login,
108 'pass' => $pass,
109 'db' => $db,
110 'prefixe' => $prefixe,
111 );
112 // etre sur qu'on definit bien les fonctions a chaque nouvelle connexion
113 include_spip('req/sqlite_fonctions');
114 _sqlite_init_functions($link);
115 }
116
117 return array(
118 'db' => $db,
119 'prefixe' => $prefixe ? $prefixe : $db,
120 'link' => $link,
121 );
122 }
123
124
125 /**
126 * Fonction de requete generale, munie d'une trace a la demande
127 *
128 * @param string $query
129 * Requete a executer
130 * @param string $serveur
131 * Nom du connecteur
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
138 */
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);
145 }
146
147
148 /* ordre alphabetique pour les autres */
149
150 // http://doc.spip.org/@spip_sqlite_alter
151 function spip_sqlite_alter($query, $serveur = '', $requeter = true){
152
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);
156
157 /*
158 * la il faut faire les transformations
159 * si ALTER TABLE x (DROP|CHANGE) y
160 *
161 * 1) recuperer "ALTER TABLE table "
162 * 2) spliter les sous requetes (,)
163 * 3) faire chaque requete independemment
164 */
165
166 // 1
167 if (preg_match("/\s*(ALTER(\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
168 $debut = $regs[1];
169 $table = $regs[3];
170 $suite = $regs[4];
171 } else {
172 spip_log("SQLite : Probleme de ALTER TABLE mal forme dans $query", 'sqlite.'._LOG_ERREUR);
173 return false;
174 }
175
176 // 2
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);
182
183 // on remet les morceaux dechires ensembles... que c'est laid !
184 $todo2 = array();
185 $i = 0;
186 $ouverte = false;
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;
193 if (!$ouverte) $i++;
194 }
195
196 // 3
197 $resultats = array();
198 foreach ($todo2 as $do){
199 $do = trim($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'
203 .'|ADD COLUMN|ADD'
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);
206 return false;
207 }
208
209 $cle = strtoupper($matches[1]);
210 $colonne_origine = $matches[2];
211 $colonne_destination = '';
212
213 $def = $matches[3];
214
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);
220
221 // reinjecter dans le do
222 $do = str_replace($def, $defr, $do);
223 $def = $defr;
224
225 switch ($cle) {
226 // suppression d'un index
227 case 'DROP KEY':
228 case 'DROP INDEX':
229 $nom_index = $colonne_origine;
230 spip_sqlite_drop_index($nom_index, $table, $serveur);
231 break;
232
233 // suppression d'une pk
234 case 'DROP PRIMARY KEY':
235 if (!_sqlite_modifier_table(
236 $table,
237 $colonne_origine,
238 array('key' => array('PRIMARY KEY' => '')),
239 $serveur)){
240 return false;
241 }
242 break;
243 // suppression d'une colonne
244 case 'DROP COLUMN':
245 case 'DROP':
246 if (!_sqlite_modifier_table(
247 $table,
248 array($colonne_origine => ""),
249 '',
250 $serveur)){
251 return false;
252 }
253 break;
254
255 case 'CHANGE COLUMN':
256 case 'CHANGE':
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)
260 $def = trim($defo);
261 $colonne_destination = substr($def, 0, strpos($def, ' '));
262 $def = substr($def, strlen($colonne_destination)+1);
263
264 if (!_sqlite_modifier_table(
265 $table,
266 array($colonne_origine => $colonne_destination),
267 array('field' => array($colonne_destination => $def)),
268 $serveur)){
269 return false;
270 }
271 break;
272
273 case 'MODIFY':
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(
277 $table,
278 $colonne_origine,
279 array('field' => array($colonne_origine => $defo)),
280 $serveur)){
281 return false;
282 }
283 break;
284
285 // pas geres en sqlite2
286 case 'RENAME':
287 $do = "RENAME TO".substr($do, 6);
288 case 'RENAME TO':
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);
292 return false;
293 }
294 // artillerie lourde pour sqlite2 !
295 } else {
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);
299 return false;
300 }
301 }
302 break;
303
304 // ajout d'une pk
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(
309 $table,
310 $colonne_origine,
311 array('key' => array('PRIMARY KEY' => $pk)),
312 $serveur)){
313 return false;
314 }
315 break;
316 // ajout d'un index
317 case 'ADD UNIQUE KEY':
318 case 'ADD UNIQUE':
319 $unique=true;
320 case 'ADD INDEX':
321 case 'ADD KEY':
322 // peut etre "(colonne)" ou "nom_index (colonnes)"
323 // bug potentiel si qqn met "(colonne, colonne)"
324 //
325 // nom_index (colonnes)
326 if ($def){
327 $colonnes = substr($def, 1, -1);
328 $nom_index = $colonne_origine;
329 }
330 else {
331 // (colonne)
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');
337 break;
338 } else {
339 $nom_index = $colonnes;
340 }
341 }
342 // nom_index
343 else {
344 $nom_index = $colonnes = $colonne_origine;
345 }
346 }
347 spip_sqlite_create_index($nom_index, $table, $colonnes, $unique, $serveur);
348 break;
349
350 // pas geres en sqlite2
351 case 'ADD COLUMN':
352 $do = "ADD".substr($do, 10);
353 case 'ADD':
354 default:
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);
358 return false;
359 }
360 break;
361
362 }
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
367 else {
368 $def = trim(substr($do, 3));
369 $colonne_ajoutee = substr($def, 0, strpos($def, ' '));
370 $def = substr($def, strlen($colonne_ajoutee)+1);
371 $opts = array();
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);
375 }
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);
379 return false;
380 }
381 }
382 break;
383 }
384 // tout est bon, ouf !
385 spip_log("SQLite ($serveur) : Changements OK : $debut $do", 'sqlite.'._LOG_INFO);
386 }
387
388 spip_log("SQLite ($serveur) : fin ALTER TABLE OK !", 'sqlite.'._LOG_INFO);
389 return true;
390 }
391
392
393 /**
394 * Fonction de creation d'une table SQL nommee $nom
395 * http://doc.spip.org/@spip_sqlite_create
396 *
397 * @param string $nom
398 * @param array $champs
399 * @param array $cles
400 * @param bool $autoinc
401 * @param bool $temporary
402 * @param string $serveur
403 * @param bool $requeter
404 * @return bool|SQLiteResult|string
405 */
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);
410
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;
414
415 $ok = $res ? true : false;
416 if ($ok){
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);
422 }
423 }
424 }
425 return $ok ? true : false;
426 }
427
428 /**
429 * Fonction pour creer une base de donnees SQLite
430 *
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
434 *
435 * @return bool true si la base est creee.
436 **/
437 function spip_sqlite_create_base($nom, $serveur = '', $option = true){
438 $f = $nom.'.sqlite';
439 if (strpos($nom, "/")===false)
440 $f = _DIR_DB.$f;
441 if (_sqlite_is_version(2, '', $serveur)){
442 $ok = sqlite_open($f, _SQLITE_CHMOD, $err);
443 } else {
444 $ok = new PDO("sqlite:$f");
445 }
446 if ($ok){
447 unset($ok);
448 return true;
449 }
450 unset($ok);
451 return false;
452 }
453
454
455 /**
456 * Fonction de creation d'une vue SQL nommee $nom
457 * http://doc.spip.org/@spip_sqlite_create_view
458 *
459 * @param string $nom
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
464 * Nom du connecteur
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
473 */
474 function spip_sqlite_create_view($nom, $query_select, $serveur = '', $requeter = true){
475 if (!$query_select) return false;
476 // vue deja presente
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);
479 return false;
480 }
481
482 $query = "CREATE VIEW $nom AS ".$query_select;
483 return spip_sqlite_query($query, $serveur, $requeter);
484 }
485
486 /**
487 * Fonction de creation d'un INDEX
488 *
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
494 *
495 * @return bool ou requete
496 */
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);
500 return false;
501 }
502
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);
512 }
513
514 $ifnotexists = "";
515 $version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
516 if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
517
518 if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
519 $ifnotexists = ' IF NOT EXISTS';
520 } else {
521 /* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
522 $a = spip_sqlite_showtable($table, $serveur);
523 if (isset($a['key']['KEY '.$nom])) return true;
524 }
525
526 $query = "CREATE ".($unique?"UNIQUE ":"")."INDEX$ifnotexists $nom ON $table (".join(',', $champs).")";
527 $res = spip_sqlite_query($query, $serveur, $requeter);
528 if (!$requeter) return $res;
529 if ($res)
530 return true;
531 else
532 return false;
533 }
534
535 /**
536 * en PDO/sqlite3, il faut calculer le count par une requete count(*)
537 * pour les resultats de SELECT
538 * cela est fait sans spip_sqlite_query()
539 * http://doc.spip.org/@spip_sqlite_count
540 *
541 * @param $r
542 * @param string $serveur
543 * @param bool $requeter
544 * @return int
545 */
546 function spip_sqlite_count($r, $serveur = '', $requeter = true){
547 if (!$r) return 0;
548
549 if (_sqlite_is_version(3, '', $serveur)){
550 // select ou autre (insert, update,...) ?
551
552 // (link,requete) a compter
553 if (is_array($r->spipSqliteRowCount)){
554 list($link,$query) = $r->spipSqliteRowCount;
555 // amelioration possible a tester intensivement : pas de order by pour compter !
556 // $query = preg_replace(",ORDER BY .+(LIMIT\s|HAVING\s|GROUP BY\s|$),Uims","\\1",$query);
557 $query = "SELECT count(*) as zzzzsqlitecount FROM ($query)";
558 $l = $link->query($query);
559 $i = 0;
560 if ($l AND $z = $l->fetch())
561 $i = $z['zzzzsqlitecount'];
562 $r->spipSqliteRowCount = $i;
563 }
564 if (isset($r->spipSqliteRowCount)){
565 // Ce compte est faux s'il y a des limit dans la requete :(
566 // il retourne le nombre d'enregistrements sans le limit
567 return $r->spipSqliteRowCount;
568 } else {
569 return $r->rowCount();
570 }
571 } else {
572 return sqlite_num_rows($r);
573 }
574 }
575
576
577 // http://doc.spip.org/@spip_sqlite_countsel
578 function spip_sqlite_countsel($from = array(), $where = array(), $groupby = '', $having = array(), $serveur = '', $requeter = true){
579 $c = !$groupby ? '*' : ('DISTINCT '.(is_string($groupby) ? $groupby : join(',', $groupby)));
580 $r = spip_sqlite_select("COUNT($c)", $from, $where, '', '', '',
581 $having, $serveur, $requeter);
582 if ((is_resource($r) or is_object($r)) && $requeter){ // ressource : sqlite2, object : sqlite3
583 if (_sqlite_is_version(3, '', $serveur)){
584 list($r) = spip_sqlite_fetch($r, SPIP_SQLITE3_NUM, $serveur);
585 } else {
586 list($r) = spip_sqlite_fetch($r, SPIP_SQLITE2_NUM, $serveur);
587 }
588
589 }
590 return $r;
591 }
592
593
594 // http://doc.spip.org/@spip_sqlite_delete
595 function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true){
596 $res = spip_sqlite_query(
597 _sqlite_calculer_expression('DELETE FROM', $table, ',')
598 ._sqlite_calculer_expression('WHERE', $where),
599 $serveur, $requeter);
600
601 // renvoyer la requete inerte si demandee
602 if (!$requeter) return $res;
603
604 if ($res){
605 $link = _sqlite_link($serveur);
606 if (_sqlite_is_version(3, $link)){
607 return $res->rowCount();
608 } else {
609 return sqlite_changes($link);
610 }
611 }
612 else
613 return false;
614 }
615
616
617 // http://doc.spip.org/@spip_sqlite_drop_table
618 function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter = true){
619 if ($exist) $exist = " IF EXISTS";
620
621 /* simuler le IF EXISTS - version 2 */
622 if ($exist && _sqlite_is_version(2, '', $serveur)){
623 $a = spip_sqlite_showtable($table, $serveur);
624 if (!$a) return true;
625 $exist = '';
626 }
627 if (spip_sqlite_query("DROP TABLE$exist $table", $serveur, $requeter))
628 return true;
629 else
630 return false;
631 }
632
633 /**
634 * supprime une vue
635 * http://doc.spip.org/@spip_sqlite_drop_view
636 *
637 * @param $view
638 * @param string $exist
639 * @param string $serveur
640 * @param bool $requeter
641 * @return bool|SQLiteResult|string
642 */
643 function spip_sqlite_drop_view($view, $exist = '', $serveur = '', $requeter = true){
644 if ($exist) $exist = " IF EXISTS";
645
646 /* simuler le IF EXISTS - version 2 */
647 if ($exist && _sqlite_is_version(2, '', $serveur)){
648 $a = spip_sqlite_showtable($view, $serveur);
649 if (!$a) return true;
650 $exist = '';
651 }
652
653 return spip_sqlite_query("DROP VIEW$exist $view", $serveur, $requeter);
654 }
655
656 /**
657 * Fonction de suppression d'un INDEX
658 *
659 * @param string $nom : nom de l'index
660 * @param string $table : table sql de l'index
661 * @param string $serveur : nom de la connexion sql utilisee
662 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
663 *
664 * @return bool ou requete
665 */
666 function spip_sqlite_drop_index($nom, $table, $serveur = '', $requeter = true){
667 if (!($nom OR $table)){
668 spip_log("Champ manquant pour supprimer un index sqlite ($nom, $table)", 'sqlite.'._LOG_ERREUR);
669 return false;
670 }
671
672 // SQLite ne differentie pas noms des index en fonction des tables
673 // il faut donc creer des noms uniques d'index pour une base sqlite
674 $index = $table.'_'.$nom;
675 $exist = " IF EXISTS";
676
677 /* simuler le IF EXISTS - version 2 */
678 if (_sqlite_is_version(2, '', $serveur)){
679 $a = spip_sqlite_showtable($table, $serveur);
680 if (!isset($a['key']['KEY '.$nom])) return true;
681 $exist = '';
682 }
683
684 $query = "DROP INDEX$exist $index";
685 return spip_sqlite_query($query, $serveur, $requeter);
686 }
687
688 /**
689 * Retourne la derniÚre erreur generée
690 *
691 * @param $serveur
692 * nom de la connexion
693 * @return string
694 * erreur eventuelle
695 **/
696 function spip_sqlite_error($query = '', $serveur = ''){
697 $link = _sqlite_link($serveur);
698
699 if (_sqlite_is_version(3, $link)){
700 $errs = $link->errorInfo();
701 /*
702 $errs[0]
703 numero SQLState ('HY000' souvent lors d'une erreur)
704 http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
705 $errs[1]
706 numéro d'erreur SQLite (souvent 1 lors d'une erreur)
707 http://www.sqlite.org/c3ref/c_abort.html
708 $errs[2]
709 Le texte du message d'erreur
710 */
711 $s = '';
712 if (ltrim($errs[0],'0')) { // 00000 si pas d'erreur
713 $s = "$errs[2]";
714 }
715 } elseif ($link) {
716 $s = sqlite_error_string(sqlite_last_error($link));
717 } else {
718 $s = ": aucune ressource sqlite (link)";
719 }
720 if ($s) spip_log("$s - $query", 'sqlite.'._LOG_ERREUR);
721 return $s;
722 }
723
724 /**
725 * Retourne le numero de la derniĂšre erreur SQL
726 *
727 * Le numéro (en sqlite3/pdo) est un retour ODBC tel que (trÚs souvent) HY000
728 * http://www.easysoft.com/developer/interfaces/odbc/sqlstate_status_return_codes.html
729 *
730 * @param string $serveur
731 * nom de la connexion
732 * @return int|string
733 * 0 pas d'erreur
734 * 1 ou autre erreur (en sqlite 2)
735 * 'HY000/1' : numéro de l'erreur SQLState / numéro d'erreur interne SQLite (en sqlite 3)
736 **/
737 function spip_sqlite_errno($serveur = ''){
738 $link = _sqlite_link($serveur);
739
740 if (_sqlite_is_version(3, $link)){
741 $t = $link->errorInfo();
742 $s = ltrim($t[0],'0'); // 00000 si pas d'erreur
743 if ($s) $s .= ' / ' . $t[1]; // ajoute l'erreur du moteur SQLite
744 } elseif ($link) {
745 $s = sqlite_last_error($link);
746 } else {
747 $s = ": aucune ressource sqlite (link)";
748 }
749
750 if ($s) spip_log("Erreur sqlite $s", 'sqlite.'._LOG_ERREUR);
751
752 return $s ? $s : 0;
753 }
754
755
756 // http://doc.spip.org/@spip_sqlite_explain
757 function spip_sqlite_explain($query, $serveur = '', $requeter = true){
758 if (strpos(ltrim($query), 'SELECT')!==0) return array();
759
760 $query = spip_sqlite::traduire_requete($query, $serveur);
761 $query = 'EXPLAIN '.$query;
762 if (!$requeter) return $query;
763 // on ne trace pas ces requetes, sinon on obtient un tracage sans fin...
764 $r = spip_sqlite::executer_requete($query, $serveur, false);
765
766 return $r ? spip_sqlite_fetch($r, null, $serveur) : false; // hum ? etrange ca... a verifier
767 }
768
769
770 // http://doc.spip.org/@spip_sqlite_fetch
771 function spip_sqlite_fetch($r, $t = '', $serveur = '', $requeter = true){
772
773 $link = _sqlite_link($serveur);
774 $is_v3 = _sqlite_is_version(3, $link);
775 if (!$t)
776 $t = ($is_v3 ? SPIP_SQLITE3_ASSOC : SPIP_SQLITE2_ASSOC);
777
778 $retour = false;
779 if ($r)
780 $retour = ($is_v3 ? $r->fetch($t) : sqlite_fetch_array($r, $t));
781
782 // les version 2 et 3 parfois renvoie des 'table.titre' au lieu de 'titre' tout court ! pff !
783 // suppression de 'table.' pour toutes les cles (c'est un peu violent !)
784 // c'est couteux : on ne verifie que la premiere ligne pour voir si on le fait ou non
785 if ($retour
786 AND strpos(implode('',array_keys($retour)),'.')!==false){
787 foreach ($retour as $cle => $val){
788 if (($pos = strpos($cle, '.'))!==false){
789 $retour[substr($cle, $pos+1)] = &$retour[$cle];
790 unset($retour[$cle]);
791 }
792 }
793 }
794
795 return $retour;
796 }
797
798
799 function spip_sqlite_seek($r, $row_number, $serveur = '', $requeter = true){
800 if ($r){
801 $link = _sqlite_link($serveur);
802 if (_sqlite_is_version(3, $link)){
803 // encore un truc de bien fichu : PDO ne PEUT PAS faire de seek ou de rewind...
804 // je me demande si pour sqlite 3 il ne faudrait pas mieux utiliser
805 // les nouvelles fonctions sqlite3_xx (mais encore moins presentes...)
806 return false;
807 }
808 else {
809 return sqlite_seek($r, $row_number);
810 }
811 }
812 }
813
814
815 // http://doc.spip.org/@spip_sqlite_free
816 function spip_sqlite_free(&$r, $serveur = '', $requeter = true){
817 unset($r);
818 return true;
819 //return sqlite_free_result($r);
820 }
821
822
823 // http://doc.spip.org/@spip_sqlite_get_charset
824 function spip_sqlite_get_charset($charset = array(), $serveur = '', $requeter = true){
825 //$c = !$charset ? '' : (" LIKE "._q($charset['charset']));
826 //return spip_sqlite_fetch(sqlite_query(_sqlite_link($serveur), "SHOW CHARACTER SET$c"), NULL, $serveur);
827 }
828
829
830 // http://doc.spip.org/@spip_sqlite_hex
831 function spip_sqlite_hex($v){
832 return hexdec($v);
833 }
834
835
836 // http://doc.spip.org/@spip_sqlite_in
837 function spip_sqlite_in($val, $valeurs, $not = '', $serveur = '', $requeter = true){
838 $n = $i = 0;
839 $in_sql = "";
840 while ($n = strpos($valeurs, ',', $n+1)){
841 if ((++$i)>=255){
842 $in_sql .= "($val $not IN (".
843 substr($valeurs, 0, $n).
844 "))\n".
845 ($not ? "AND\t" : "OR\t");
846 $valeurs = substr($valeurs, $n+1);
847 $i = $n = 0;
848 }
849 }
850 $in_sql .= "($val $not IN ($valeurs))";
851
852 return "($in_sql)";
853 }
854
855
856 // http://doc.spip.org/@spip_sqlite_insert
857 function spip_sqlite_insert($table, $champs, $valeurs, $desc = '', $serveur = '', $requeter = true){
858
859 $query = "INSERT INTO $table ".($champs ? "$champs VALUES $valeurs" : "DEFAULT VALUES");
860 if ($r = spip_sqlite_query($query, $serveur, $requeter)){
861 if (!$requeter) return $r;
862 $nb = spip_sqlite::last_insert_id($serveur);
863 }
864 else
865 $nb = 0;
866
867 $err = spip_sqlite_error($query, $serveur);
868 // cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
869 return isset($_GET['var_profile']) ? $r : $nb;
870
871 }
872
873
874 // http://doc.spip.org/@spip_sqlite_insertq
875 function spip_sqlite_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true){
876 if (!$desc) $desc = description_table($table, $serveur);
877 if (!$desc) die("$table insertion sans description");
878 $fields = isset($desc['field']) ? $desc['field'] : array();
879
880 foreach ($couples as $champ => $val){
881 $couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
882 }
883
884 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
885 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
886
887 $cles = $valeurs = "";
888 if (count($couples)){
889 $cles = "(".join(',', array_keys($couples)).")";
890 $valeurs = "(".join(',', $couples).")";
891 }
892
893 return spip_sqlite_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
894 }
895
896
897 // http://doc.spip.org/@spip_sqlite_insertq_multi
898 function spip_sqlite_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true){
899 if (!$desc) $desc = description_table($table, $serveur);
900 if (!$desc) die("$table insertion sans description");
901 if (!isset($desc['field']))
902 $desc['field'] = array();
903
904 // recuperer les champs 'timestamp' pour mise a jour auto de ceux-ci
905 $maj = _sqlite_ajouter_champs_timestamp($table, array(), $desc, $serveur);
906
907 // seul le nom de la table est a traduire ici :
908 // le faire une seule fois au debut
909 $query_start = "INSERT INTO $table ";
910 $query_start = spip_sqlite::traduire_requete($query_start,$serveur);
911
912 // ouvrir une transaction
913 if ($requeter)
914 spip_sqlite::demarrer_transaction($serveur);
915
916 while ($couples = array_shift($tab_couples)){
917 foreach ($couples as $champ => $val){
918 $couples[$champ] = _sqlite_calculer_cite($val, $desc['field'][$champ]);
919 }
920
921 // inserer les champs timestamp par defaut
922 $couples = array_merge($maj,$couples);
923
924 $champs = $valeurs = "";
925 if (count($couples)){
926 $champs = "(".join(',', array_keys($couples)).")";
927 $valeurs = "(".join(',', $couples).")";
928 $query = $query_start."$champs VALUES $valeurs";
929 }
930 else
931 $query = $query_start."DEFAULT VALUES";
932
933 if ($requeter)
934 $retour = spip_sqlite::executer_requete($query,$serveur);
935
936 // sur le dernier couple uniquement
937 if (!count($tab_couples)){
938 $nb = 0;
939 if ($requeter)
940 $nb = spip_sqlite::last_insert_id($serveur);
941 else
942 return $query;
943 }
944
945 $err = spip_sqlite_error($query, $serveur);
946 }
947
948 if ($requeter)
949 spip_sqlite::finir_transaction($serveur);
950
951 // renvoie le dernier id d'autoincrement ajoute
952 // cas particulier : ne pas substituer la reponse spip_sqlite_query si on est en profilage
953 return isset($_GET['var_profile']) ? $retour : $nb;
954 }
955
956
957 /**
958 * Retourne si le moteur SQL prefere utiliser des transactions.
959 *
960 * @param
961 * @return bool true / false
962 **/
963 function spip_sqlite_preferer_transaction($serveur = '', $requeter = true) {
964 return true;
965 }
966
967 /**
968 * Demarre une transaction.
969 * Pratique pour des sql_updateq() dans un foreach,
970 * parfois 100* plus rapide s'ils sont nombreux en sqlite !
971 *
972 **/
973 function spip_sqlite_demarrer_transaction($serveur = '', $requeter = true) {
974 if (!$requeter) return "BEGIN TRANSACTION";
975 spip_sqlite::demarrer_transaction($serveur);
976 return true;
977 }
978
979 /**
980 * Cloture une transaction.
981 *
982 **/
983 function spip_sqlite_terminer_transaction($serveur = '', $requeter = true) {
984 if (!$requeter) return "COMMIT";
985 spip_sqlite::finir_transaction($serveur);
986 return true;
987 }
988
989
990 // http://doc.spip.org/@spip_sqlite_listdbs
991 function spip_sqlite_listdbs($serveur = '', $requeter = true){
992 _sqlite_init();
993
994 if (!is_dir($d = substr(_DIR_DB, 0, -1))){
995 return array();
996 }
997
998 include_spip('inc/flock');
999 $bases = preg_files($d, $pattern = '(.*)\.sqlite$');
1000 $bds = array();
1001
1002 foreach ($bases as $b){
1003 // pas de bases commencant pas sqlite
1004 // (on s'en sert pour l'installation pour simuler la presence d'un serveur)
1005 // les bases sont de la forme _sqliteX_tmp_spip_install.sqlite
1006 if (strpos($b, '_sqlite')) continue;
1007 $bds[] = preg_replace(";.*/$pattern;iS", '$1', $b);
1008 }
1009
1010 return $bds;
1011 }
1012
1013
1014 // http://doc.spip.org/@spip_sqlite_multi
1015 function spip_sqlite_multi($objet, $lang){
1016 $r = "EXTRAIRE_MULTI(" . $objet . ", '" . $lang . "') AS multi";
1017 return $r;
1018 }
1019
1020
1021 /**
1022 * Optimise une table SQL
1023 * Note: Sqlite optimise TOUTE un fichier sinon rien.
1024 * On evite donc 2 traitements sur la meme base dans un hit.
1025 *
1026 * @param $table nom de la table a optimiser
1027 * @param $serveur nom de la connexion
1028 * @param $requeter effectuer la requete ? sinon retourner son code
1029 * @return bool|string true / false / requete
1030 **/
1031 // http://doc.spip.org/@spip_sqlite_optimize
1032 function spip_sqlite_optimize($table, $serveur = '', $requeter = true){
1033 static $do = false;
1034 if ($requeter and $do){
1035 return true;
1036 }
1037 if ($requeter){
1038 $do = true;
1039 }
1040 return spip_sqlite_query("VACUUM", $serveur, $requeter);
1041 }
1042
1043
1044 //
1045 /**
1046 * echapper une valeur selon son type ou au mieux
1047 * comme le fait _q() mais pour sqlite avec ses specificites
1048 *
1049 * @param string|array|number $v
1050 * @param string $type
1051 * @return string|number
1052 */
1053 function spip_sqlite_quote($v, $type = ''){
1054 if (!is_array($v))
1055 return _sqlite_calculer_cite($v,$type);
1056 // si c'est un tableau, le parcourir en propageant le type
1057 foreach($v as $k=>$r)
1058 $v[$k] = spip_sqlite_quote($r, $type);
1059 return join(",", $v);
1060 }
1061
1062
1063 /**
1064 * Tester si une date est proche de la valeur d'un champ
1065 *
1066 * @param string $champ le nom du champ a tester
1067 * @param int $interval valeur de l'interval : -1, 4, ...
1068 * @param string $unite utite utilisee (DAY, MONTH, YEAR, ...)
1069 * @return string expression SQL
1070 **/
1071 function spip_sqlite_date_proche($champ, $interval, $unite){
1072 $op = (($interval <= 0) ? '>' : '<');
1073 return "($champ $op datetime('".date("Y-m-d H:i:s")."', '$interval $unite'))";
1074 }
1075
1076
1077 // http://doc.spip.org/@spip_sqlite_replace
1078 function spip_sqlite_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true){
1079 if (!$desc) $desc = description_table($table, $serveur);
1080 if (!$desc) die("$table insertion sans description");
1081 $fields = isset($desc['field']) ? $desc['field'] : array();
1082
1083 foreach ($couples as $champ => $val){
1084 $couples[$champ] = _sqlite_calculer_cite($val, $fields[$champ]);
1085 }
1086
1087 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1088 $couples = _sqlite_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
1089
1090 return spip_sqlite_query("REPLACE INTO $table (".join(',', array_keys($couples)).') VALUES ('.join(',', $couples).')', $serveur);
1091 }
1092
1093
1094 // http://doc.spip.org/@spip_sqlite_replace_multi
1095 function spip_sqlite_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true){
1096
1097 // boucler pour trainter chaque requete independemment
1098 foreach ($tab_couples as $couples){
1099 $retour = spip_sqlite_replace($table, $couples, $desc, $serveur, $requeter);
1100 }
1101 // renvoie le dernier id
1102 return $retour;
1103 }
1104
1105
1106 // http://doc.spip.org/@spip_sqlite_select
1107 function spip_sqlite_select($select, $from, $where = '', $groupby = '', $orderby = '', $limit = '', $having = '', $serveur = '', $requeter = true){
1108
1109 // version() n'est pas connu de sqlite
1110 $select = str_replace('version()', 'sqlite_version()', $select);
1111
1112 // recomposer from
1113 $from = (!is_array($from) ? $from : _sqlite_calculer_select_as($from));
1114
1115 $query =
1116 _sqlite_calculer_expression('SELECT', $select, ', ')
1117 ._sqlite_calculer_expression('FROM', $from, ', ')
1118 ._sqlite_calculer_expression('WHERE', $where)
1119 ._sqlite_calculer_expression('GROUP BY', $groupby, ',')
1120 ._sqlite_calculer_expression('HAVING', $having)
1121 .($orderby ? ("\nORDER BY "._sqlite_calculer_order($orderby)) : '')
1122 .($limit ? "\nLIMIT $limit" : '');
1123
1124 // dans un select, on doit renvoyer la requĂȘte en cas d'erreur
1125 $res = spip_sqlite_query($query, $serveur, $requeter);
1126 // texte de la requete demande ?
1127 if (!$requeter) return $res;
1128 // erreur survenue ?
1129 if ($res === false) {
1130 return spip_sqlite::traduire_requete($query, $serveur);
1131 }
1132 return $res;
1133 }
1134
1135
1136 /**
1137 * Selectionne un fichier de base de donnees
1138 *
1139 * @param string $nom
1140 * Nom de la base a utiliser
1141 * @param string $serveur
1142 * Nom du connecteur
1143 * @param bool $requeter
1144 * Inutilise
1145 *
1146 * @return bool|string
1147 * Nom de la base en cas de success.
1148 * False en cas d'erreur.
1149 **/
1150 function spip_sqlite_selectdb($db, $serveur = '', $requeter = true){
1151 _sqlite_init();
1152
1153 // interdire la creation d'une nouvelle base,
1154 // sauf si on est dans l'installation
1155 if (!is_file($f = _DIR_DB.$db.'.sqlite')
1156 && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
1157 spip_log("Il est interdit de creer la base $db", 'sqlite.'._LOG_HS);
1158 return false;
1159 }
1160
1161 // se connecter a la base indiquee
1162 // avec les identifiants connus
1163 $index = $serveur ? $serveur : 0;
1164
1165 if ($link = spip_connect_db('', '', '', '', '@selectdb@'.$db, $serveur, '', '')){
1166 if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
1167 return $db;
1168 } else {
1169 spip_log("Impossible de selectionner la base $db", 'sqlite.'._LOG_HS);
1170 return false;
1171 }
1172
1173 }
1174
1175
1176 // http://doc.spip.org/@spip_sqlite_set_charset
1177 function spip_sqlite_set_charset($charset, $serveur = '', $requeter = true){
1178 # spip_log("Gestion charset sql a ecrire : "."SET NAMES "._q($charset), 'sqlite.'._LOG_ERREUR);
1179 # return spip_sqlite_query("SET NAMES ". spip_sqlite_quote($charset), $serveur); //<-- Passe pas !
1180 }
1181
1182
1183 /**
1184 * Retourne une ressource de la liste des tables de la base de données
1185 *
1186 * @param string $match
1187 * Filtre sur tables à récupérer
1188 * @param string $serveur
1189 * Connecteur de la base
1190 * @param bool $requeter
1191 * true pour Ă©xecuter la requĂȘte
1192 * false pour retourner le texte de la requĂȘte.
1193 * @return ressource
1194 * Ressource Ă  utiliser avec sql_fetch()
1195 **/
1196 function spip_sqlite_showbase($match, $serveur = '', $requeter = true){
1197 // type est le type d'entrée : table / index / view
1198 // on ne retourne que les tables (?) et non les vues...
1199 # ESCAPE non supporte par les versions sqlite <3
1200 # return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name LIKE "._q($match)." ESCAPE '\'", $serveur, $requeter);
1201 $match = preg_quote($match);
1202 $match = str_replace("\\\_", "[[TIRETBAS]]", $match);
1203 $match = str_replace("\\\%", "[[POURCENT]]", $match);
1204 $match = str_replace("_", ".", $match);
1205 $match = str_replace("%", ".*", $match);
1206 $match = str_replace("[[TIRETBAS]]", "_", $match);
1207 $match = str_replace("[[POURCENT]]", "%", $match);
1208 $match = "^$match$";
1209 return spip_sqlite_query("SELECT name FROM sqlite_master WHERE type='table' AND tbl_name REGEXP "._q($match), $serveur, $requeter);
1210 }
1211
1212 define('_SQLITE_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
1213 // http://doc.spip.org/@spip_sqlite_showtable
1214 function spip_sqlite_showtable($nom_table, $serveur = '', $requeter = true){
1215 $query =
1216 'SELECT sql, type FROM'
1217 .' (SELECT * FROM sqlite_master UNION ALL'
1218 .' SELECT * FROM sqlite_temp_master)'
1219 ." WHERE tbl_name LIKE '$nom_table'"
1220 ." AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'"
1221 .' ORDER BY substr(type,2,1), name';
1222
1223 $a = spip_sqlite_query($query, $serveur, $requeter);
1224 if (!$a) return "";
1225 if (!$requeter) return $a;
1226 if (!($a = spip_sqlite_fetch($a, null, $serveur))) return "";
1227 $vue = ($a['type']=='view'); // table | vue
1228
1229 // c'est une table
1230 // il faut parser le create
1231 if (!$vue){
1232 if (!preg_match(_SQLITE_RE_SHOW_TABLE, array_shift($a), $r)){
1233 return "";
1234 } else {
1235 $desc = $r[1];
1236 // extraction d'une KEY Ă©ventuelle en prenant garde de ne pas
1237 // relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
1238 if (preg_match("/^(.*?),([^,]*\sKEY[ (].*)$/s", $desc, $r)){
1239 $namedkeys = $r[2];
1240 $desc = $r[1];
1241 }
1242 else
1243 $namedkeys = "";
1244
1245 $fields = array();
1246 $keys = array();
1247
1248 // enlever les contenus des valeurs DEFAULT 'xxx' qui pourraient perturber
1249 // par exemple s'il contiennent une virgule.
1250 // /!\ cela peut aussi echapper le nom des champs si la table a eu des operations avec SQLite Manager !
1251 list($desc, $echaps) = query_echappe_textes($desc);
1252
1253 // separer toutes les descriptions de champs, separes par des virgules
1254 # /!\ explode peut exploser aussi DECIMAL(10,2) !
1255 $k_precedent = null;
1256 foreach (explode(",", $desc) as $v){
1257
1258 preg_match("/^\s*([^\s]+)\s+(.*)/", $v, $r);
1259 // Les cles de champs peuvent etre entourees
1260 // de guillements doubles " , simples ', graves ` ou de crochets [ ], ou rien.
1261 // http://www.sqlite.org/lang_keywords.html
1262 $k = strtolower(query_reinjecte_textes($r[1], $echaps)); // champ, "champ", [champ]...
1263 if ($char = strpbrk($k[0], '\'"[`')) {
1264 $k = trim($k, $char);
1265 if ($char == '[') $k = rtrim($k, ']');
1266 }
1267 $def = query_reinjecte_textes($r[2], $echaps); // valeur du champ
1268
1269 # rustine pour DECIMAL(10,2)
1270 if (false !== strpos($k, ')')) {
1271 $fields[$k_precedent] .= ',' . $k . ' ' . $def;
1272 continue;
1273 }
1274
1275 $fields[$k] = $def;
1276 $k_precedent = $k;
1277
1278 // la primary key peut etre dans une des descriptions de champs
1279 // et non en fin de table, cas encore decouvert avec Sqlite Manager
1280 if (stripos($r[2], 'PRIMARY KEY') !== false) {
1281 $keys['PRIMARY KEY'] = $k;
1282 }
1283 }
1284 // key inclues dans la requete
1285 foreach(preg_split('/\)\s*(,|$)/',$namedkeys) as $v) {
1286 if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/",$v,$r)) {
1287 $k = str_replace("`", '', trim($r[1]));
1288 $t = trim(strtolower(str_replace("`", '', $r[2])), '"');
1289 if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
1290 }
1291 }
1292 // sinon ajouter les key index
1293 $query =
1294 'SELECT name,sql FROM'
1295 .' (SELECT * FROM sqlite_master UNION ALL'
1296 .' SELECT * FROM sqlite_temp_master)'
1297 ." WHERE tbl_name LIKE '$nom_table'"
1298 ." AND type='index' AND name NOT LIKE 'sqlite_%'"
1299 .'ORDER BY substr(type,2,1), name';
1300 $a = spip_sqlite_query($query, $serveur, $requeter);
1301 while ($r = spip_sqlite_fetch($a, null, $serveur)){
1302 $key = str_replace($nom_table.'_', '', $r['name']); // enlever le nom de la table ajoute a l'index
1303 $colonnes = preg_replace(',.*\((.*)\).*,', '$1', $r['sql']);
1304 $keys['KEY '.$key] = $colonnes;
1305 }
1306 }
1307 }
1308 // c'est une vue, on liste les champs disponibles simplement
1309 else {
1310 if ($res = sql_fetsel('*', $nom_table, '', '', '', '1', '', $serveur)){ // limit 1
1311 $fields = array();
1312 foreach ($res as $c => $v) $fields[$c] = '';
1313 $keys = array();
1314 } else {
1315 return "";
1316 }
1317 }
1318 return array('field' => $fields, 'key' => $keys);
1319
1320 }
1321
1322
1323 // http://doc.spip.org/@spip_sqlite_update
1324 function spip_sqlite_update($table, $champs, $where = '', $desc = '', $serveur = '', $requeter = true){
1325 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1326 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1327
1328 $set = array();
1329 foreach ($champs as $champ => $val)
1330 $set[] = $champ."=$val";
1331 if (!empty($set))
1332 return spip_sqlite_query(
1333 _sqlite_calculer_expression('UPDATE', $table, ',')
1334 ._sqlite_calculer_expression('SET', $set, ',')
1335 ._sqlite_calculer_expression('WHERE', $where),
1336 $serveur, $requeter);
1337 }
1338
1339
1340 // http://doc.spip.org/@spip_sqlite_updateq
1341 function spip_sqlite_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true){
1342
1343 if (!$champs) return;
1344 if (!$desc) $desc = description_table($table, $serveur);
1345 if (!$desc) die("$table insertion sans description");
1346 $fields = $desc['field'];
1347
1348 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
1349 $champs = _sqlite_ajouter_champs_timestamp($table, $champs, $desc, $serveur);
1350
1351 $set = array();
1352 foreach ($champs as $champ => $val){
1353 $set[] = $champ.'='._sqlite_calculer_cite($val, $fields[$champ]);
1354 }
1355 return spip_sqlite_query(
1356 _sqlite_calculer_expression('UPDATE', $table, ',')
1357 ._sqlite_calculer_expression('SET', $set, ',')
1358 ._sqlite_calculer_expression('WHERE', $where),
1359 $serveur, $requeter);
1360 }
1361
1362
1363 /*
1364 *
1365 * Ensuite les fonctions non abstraites
1366 * crees pour l'occasion de sqlite
1367 *
1368 */
1369
1370
1371 /**
1372 * fonction pour la premiere connexion a un serveur SQLite
1373 * http://doc.spip.org/@_sqlite_init
1374 *
1375 * @return void
1376 */
1377 function _sqlite_init(){
1378 if (!defined('_DIR_DB')) define('_DIR_DB', _DIR_ETC.'bases/');
1379 if (!defined('_SQLITE_CHMOD')) define('_SQLITE_CHMOD', _SPIP_CHMOD);
1380
1381 if (!is_dir($d = _DIR_DB)){
1382 include_spip('inc/flock');
1383 sous_repertoire($d);
1384 }
1385 }
1386
1387
1388 /**
1389 * teste la version sqlite du link en cours
1390 * http://doc.spip.org/@_sqlite_is_version
1391 *
1392 * @param string $version
1393 * @param string $link
1394 * @param string $serveur
1395 * @param bool $requeter
1396 * @return bool|int
1397 */
1398 function _sqlite_is_version($version = '', $link = '', $serveur = '', $requeter = true){
1399 if ($link==='') $link = _sqlite_link($serveur);
1400 if (!$link) return false;
1401 if ($link instanceof PDO){
1402 $v = 3;
1403 } else {
1404 $v = 2;
1405 }
1406
1407 if (!$version) return $v;
1408 return ($version==$v);
1409 }
1410
1411
1412 /**
1413 * retrouver un link
1414 * http://doc.spip.org/@_sqlite_link
1415 *
1416 * @param string $serveur
1417 * @return
1418 */
1419 function _sqlite_link($serveur = ''){
1420 $link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1421 return $link;
1422 }
1423
1424
1425 /* ordre alphabetique pour les autres */
1426
1427
1428 /**
1429 * renvoie les bons echappements (pas sur les fonctions now())
1430 * http://doc.spip.org/@_sqlite_calculer_cite
1431 *
1432 * @param string|array|number $v
1433 * @param string $type
1434 * @return string|array|number
1435 */
1436 function _sqlite_calculer_cite($v, $type){
1437 if ($type){
1438 if(is_null($v)
1439 AND stripos($type,"NOT NULL")===false) return 'NULL'; // null php se traduit en NULL SQL
1440
1441 if (sql_test_date($type) AND preg_match('/^\w+\(/', $v))
1442 return $v;
1443 if (sql_test_int($type)){
1444 if (is_numeric($v))
1445 return $v;
1446 elseif (ctype_xdigit(substr($v, 2)) AND strncmp($v, '0x', 2)==0)
1447 return hexdec(substr($v, 2));
1448 else
1449 return intval($v);
1450 }
1451 }
1452 else {
1453 // si on ne connait pas le type on le deduit de $v autant que possible
1454 if (is_numeric($v))
1455 return strval($v);
1456 }
1457
1458 if (function_exists('sqlite_escape_string')){
1459 return "'".sqlite_escape_string($v)."'";
1460 }
1461
1462 // trouver un link sqlite3 pour faire l'echappement
1463 foreach ($GLOBALS['connexions'] as $s){
1464 if (_sqlite_is_version(3, $l = $s['link'])){
1465 return $l->quote($v);
1466 }
1467 }
1468
1469 // echapper les ' en ''
1470 spip_log("Pas de methode sqlite_escape_string ni ->quote pour echapper","sqlite."._LOG_INFO_IMPORTANTE);
1471 return ("'" . str_replace("'","''",$v) . "'");
1472 }
1473
1474
1475 /**
1476 * renvoie grosso modo "$expression join($join, $v)"
1477 * http://doc.spip.org/@_sqlite_calculer_expression
1478 *
1479 * @param $expression
1480 * @param $v
1481 * @param string $join
1482 * @return string
1483 */
1484 function _sqlite_calculer_expression($expression, $v, $join = 'AND'){
1485 if (empty($v))
1486 return '';
1487
1488 $exp = "\n$expression ";
1489
1490 if (!is_array($v)){
1491 return $exp.$v;
1492 } else {
1493 if (strtoupper($join)==='AND')
1494 return $exp.join("\n\t$join ", array_map('_sqlite_calculer_where', $v));
1495 else
1496 return $exp.join($join, $v);
1497 }
1498 }
1499
1500
1501 /**
1502 * pour conversion 0+x ? (pas la peine en sqlite)
1503 * http://doc.spip.org/@_sqlite_calculer_order
1504 *
1505 * @param $orderby
1506 * @return string
1507 */
1508 function _sqlite_calculer_order($orderby){
1509 return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
1510 }
1511
1512
1513 // renvoie des 'nom AS alias'
1514 // http://doc.spip.org/@_sqlite_calculer_select_as
1515 function _sqlite_calculer_select_as($args){
1516 $res = '';
1517 foreach ($args as $k => $v){
1518 if (substr($k, -1)=='@'){
1519 // c'est une jointure qui se refere au from precedent
1520 // pas de virgule
1521 $res .= ' '.$v;
1522 }
1523 else {
1524 if (!is_numeric($k)){
1525 $p = strpos($v, " ");
1526 if ($p)
1527 $v = substr($v, 0, $p)." AS '$k'".substr($v, $p);
1528 else $v .= " AS '$k'";
1529 }
1530 $res .= ', '.$v;
1531 }
1532 }
1533 return substr($res, 2);
1534 }
1535
1536
1537 /**
1538 * renvoie les bonnes parentheses pour des where imbriquees
1539 * http://doc.spip.org/@_sqlite_calculer_where
1540 *
1541 * @param $v
1542 * @return array|mixed|string
1543 */
1544 function _sqlite_calculer_where($v){
1545 if (!is_array($v))
1546 return $v;
1547
1548 $op = array_shift($v);
1549 if (!($n = count($v)))
1550 return $op;
1551 else {
1552 $arg = _sqlite_calculer_where(array_shift($v));
1553 if ($n==1){
1554 return "$op($arg)";
1555 } else {
1556 $arg2 = _sqlite_calculer_where(array_shift($v));
1557 if ($n==2){
1558 return "($arg $op $arg2)";
1559 } else return "($arg $op ($arg2) : $v[0])";
1560 }
1561 }
1562 }
1563
1564
1565 /**
1566 * Charger les modules sqlite (si possible) (juste la version demandee),
1567 * ou, si aucune version, renvoie les versions sqlite dispo
1568 * sur ce serveur dans un array
1569 *
1570 * http://doc.spip.org/@_sqlite_charger_version
1571 *
1572 * @param string $version
1573 * @return array|bool
1574 */
1575 function _sqlite_charger_version($version = ''){
1576 $versions = array();
1577
1578 // version 2
1579 if (!$version || $version==2){
1580 if (charger_php_extension('sqlite')){
1581 $versions[] = 2;
1582 }
1583 }
1584
1585 // version 3
1586 if (!$version || $version==3){
1587 if (charger_php_extension('pdo') && charger_php_extension('pdo_sqlite')){
1588 $versions[] = 3;
1589 }
1590 }
1591 if ($version) return in_array($version, $versions);
1592 return $versions;
1593 }
1594
1595
1596 /**
1597 * Gestion des requetes ALTER non reconnues de SQLite :
1598 * ALTER TABLE table DROP column
1599 * ALTER TABLE table CHANGE [COLUMN] columnA columnB definition
1600 * ALTER TABLE table MODIFY column definition
1601 * ALTER TABLE table ADD|DROP PRIMARY KEY
1602 *
1603 * (MODIFY transforme en CHANGE columnA columnA) par spip_sqlite_alter()
1604 *
1605 * 1) creer une table B avec le nouveau format souhaite
1606 * 2) copier la table d'origine A vers B
1607 * 3) supprimer la table A
1608 * 4) renommer la table B en A
1609 * 5) remettre les index (qui sont supprimes avec la table A)
1610 *
1611 * http://doc.spip.org/@_sqlite_modifier_table
1612 *
1613 * @param string/array $table : nom_table, array(nom_table=>nom_futur)
1614 * @param string/array $col : nom_colonne, array(nom_colonne=>nom_futur)
1615 * @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', ...))
1616 * @param string $serveur : nom de la connexion sql en cours
1617 *
1618 */
1619 function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = ''){
1620
1621 if (is_array($table)){
1622 reset($table);
1623 list($table_origine,$table_destination) = each($table);
1624 } else {
1625 $table_origine = $table_destination = $table;
1626 }
1627 // ne prend actuellement qu'un changement
1628 // mais pourra etre adapte pour changer plus qu'une colonne a la fois
1629 if (is_array($colonne)){
1630 reset($colonne);
1631 list($colonne_origine,$colonne_destination) = each($colonne);
1632 } else {
1633 $colonne_origine = $colonne_destination = $colonne;
1634 }
1635 if (!isset($opt['field'])) $opt['field'] = array();
1636 if (!isset($opt['key'])) $opt['key'] = array();
1637
1638 // si les noms de tables sont differents, pas besoin de table temporaire
1639 // on prendra directement le nom de la future table
1640 $meme_table = ($table_origine==$table_destination);
1641
1642 $def_origine = sql_showtable($table_origine, false, $serveur);
1643 if (!$def_origine OR !isset($def_origine['field'])){
1644 spip_log("Alter table impossible sur $table_origine : table non trouvee",'sqlite'._LOG_ERREUR);
1645 return false;
1646 }
1647
1648
1649 $table_tmp = $table_origine.'_tmp';
1650
1651 // 1) creer une table temporaire avec les modifications
1652 // - DROP : suppression de la colonne
1653 // - CHANGE : modification de la colonne
1654 // (foreach pour conserver l'ordre des champs)
1655
1656 // field
1657 $fields = array();
1658 // pour le INSERT INTO plus loin
1659 // stocker la correspondance nouvelles->anciennes colonnes
1660 $fields_correspondances = array();
1661 foreach ($def_origine['field'] as $c => $d){
1662
1663 if ($colonne_origine && ($c==$colonne_origine)){
1664 // si pas DROP
1665 if ($colonne_destination){
1666 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1667 $fields_correspondances[$colonne_destination] = $c;
1668 }
1669 } else {
1670 $fields[$c] = $d;
1671 $fields_correspondances[$c] = $c;
1672 }
1673 }
1674 // cas de ADD sqlite2 (ajout du champ en fin de table):
1675 if (!$colonne_origine && $colonne_destination){
1676 $fields[$colonne_destination] = $opt['field'][$colonne_destination];
1677 }
1678
1679 // key...
1680 $keys = array();
1681 foreach ($def_origine['key'] as $c => $d){
1682 $c = str_replace($colonne_origine, $colonne_destination, $c);
1683 $d = str_replace($colonne_origine, $colonne_destination, $d);
1684 // seulement si on ne supprime pas la colonne !
1685 if ($d)
1686 $keys[$c] = $d;
1687 }
1688
1689 // autres keys, on merge
1690 $keys = array_merge($keys, $opt['key']);
1691 $queries = array();
1692
1693 // copier dans destination (si differente de origine), sinon tmp
1694 $table_copie = ($meme_table) ? $table_tmp : $table_destination;
1695 $autoinc = (isset($keys['PRIMARY KEY'])
1696 AND stripos($keys['PRIMARY KEY'],',')===false
1697 AND stripos($fields[$keys['PRIMARY KEY']],'default')===false);
1698
1699 if ($q = _sqlite_requete_create(
1700 $table_copie,
1701 $fields,
1702 $keys,
1703 $autoinc,
1704 $temporary = false,
1705 $ifnotexists = true,
1706 $serveur)){
1707 $queries[] = $q;
1708 }
1709
1710
1711 // 2) y copier les champs qui vont bien
1712 $champs_dest = join(', ', array_keys($fields_correspondances));
1713 $champs_ori = join(', ', $fields_correspondances);
1714 $queries[] = "INSERT INTO $table_copie ($champs_dest) SELECT $champs_ori FROM $table_origine";
1715
1716 // 3) supprimer la table d'origine
1717 $queries[] = "DROP TABLE $table_origine";
1718
1719 // 4) renommer la table temporaire
1720 // avec le nom de la table destination
1721 // si necessaire
1722 if ($meme_table){
1723 if (_sqlite_is_version(3, '', $serveur)){
1724 $queries[] = "ALTER TABLE $table_copie RENAME TO $table_destination";
1725 } else {
1726 $queries[] = _sqlite_requete_create(
1727 $table_destination,
1728 $fields,
1729 $keys,
1730 $autoinc,
1731 $temporary = false,
1732 $ifnotexists = false, // la table existe puisqu'on est dans une transaction
1733 $serveur);
1734 $queries[] = "INSERT INTO $table_destination SELECT * FROM $table_copie";
1735 $queries[] = "DROP TABLE $table_copie";
1736 }
1737 }
1738
1739 // 5) remettre les index !
1740 foreach ($keys as $k => $v){
1741 if ($k=='PRIMARY KEY'){
1742 }
1743 else {
1744 // enlever KEY
1745 $k = substr($k, 4);
1746 $queries[] = "CREATE INDEX $table_destination"."_$k ON $table_destination ($v)";
1747 }
1748 }
1749
1750
1751 if (count($queries)){
1752 spip_sqlite::demarrer_transaction($serveur);
1753 // il faut les faire une par une car $query = join('; ', $queries).";"; ne fonctionne pas
1754 foreach ($queries as $q){
1755 if (!spip_sqlite::executer_requete($q, $serveur)){
1756 spip_log(_LOG_GRAVITE_ERREUR, "SQLite : ALTER TABLE table :"
1757 ." Erreur a l'execution de la requete : $q", 'sqlite');
1758 spip_sqlite::annuler_transaction($serveur);
1759 return false;
1760 }
1761 }
1762 spip_sqlite::finir_transaction($serveur);
1763 }
1764
1765 return true;
1766 }
1767
1768
1769 /**
1770 * Nom des fonctions
1771 * http://doc.spip.org/@_sqlite_ref_fonctions
1772 *
1773 * @return array
1774 */
1775 function _sqlite_ref_fonctions(){
1776 $fonctions = array(
1777 'alter' => 'spip_sqlite_alter',
1778 'count' => 'spip_sqlite_count',
1779 'countsel' => 'spip_sqlite_countsel',
1780 'create' => 'spip_sqlite_create',
1781 'create_base' => 'spip_sqlite_create_base',
1782 'create_view' => 'spip_sqlite_create_view',
1783 'date_proche' => 'spip_sqlite_date_proche',
1784 'delete' => 'spip_sqlite_delete',
1785 'drop_table' => 'spip_sqlite_drop_table',
1786 'drop_view' => 'spip_sqlite_drop_view',
1787 'errno' => 'spip_sqlite_errno',
1788 'error' => 'spip_sqlite_error',
1789 'explain' => 'spip_sqlite_explain',
1790 'fetch' => 'spip_sqlite_fetch',
1791 'seek' => 'spip_sqlite_seek',
1792 'free' => 'spip_sqlite_free',
1793 'hex' => 'spip_sqlite_hex',
1794 'in' => 'spip_sqlite_in',
1795 'insert' => 'spip_sqlite_insert',
1796 'insertq' => 'spip_sqlite_insertq',
1797 'insertq_multi' => 'spip_sqlite_insertq_multi',
1798 'listdbs' => 'spip_sqlite_listdbs',
1799 'multi' => 'spip_sqlite_multi',
1800 'optimize' => 'spip_sqlite_optimize',
1801 'query' => 'spip_sqlite_query',
1802 'quote' => 'spip_sqlite_quote',
1803 'replace' => 'spip_sqlite_replace',
1804 'replace_multi' => 'spip_sqlite_replace_multi',
1805 'select' => 'spip_sqlite_select',
1806 'selectdb' => 'spip_sqlite_selectdb',
1807 'set_charset' => 'spip_sqlite_set_charset',
1808 'get_charset' => 'spip_sqlite_get_charset',
1809 'showbase' => 'spip_sqlite_showbase',
1810 'showtable' => 'spip_sqlite_showtable',
1811 'update' => 'spip_sqlite_update',
1812 'updateq' => 'spip_sqlite_updateq',
1813 'preferer_transaction' => 'spip_sqlite_preferer_transaction',
1814 'demarrer_transaction' => 'spip_sqlite_demarrer_transaction',
1815 'terminer_transaction' => 'spip_sqlite_terminer_transaction',
1816 );
1817
1818 // association de chaque nom http d'un charset aux couples sqlite
1819 // SQLite supporte utf-8 et utf-16 uniquement.
1820 $charsets = array(
1821 'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci'),
1822 //'utf-16be'=>array('charset'=>'utf16be','collation'=>'UTF-16BE'),// aucune idee de quoi il faut remplir dans es champs la
1823 //'utf-16le'=>array('charset'=>'utf16le','collation'=>'UTF-16LE')
1824 );
1825
1826 $fonctions['charsets'] = $charsets;
1827
1828 return $fonctions;
1829 }
1830
1831
1832 /**
1833 * $query est une requete ou une liste de champs
1834 * http://doc.spip.org/@_sqlite_remplacements_definitions_table
1835 *
1836 * @param $query
1837 * @param bool $autoinc
1838 * @return mixed
1839 */
1840 function _sqlite_remplacements_definitions_table($query, $autoinc = false){
1841 // quelques remplacements
1842 $num = "(\s*\([0-9]*\))?";
1843 $enum = "(\s*\([^\)]*\))?";
1844
1845 $remplace = array(
1846 '/enum'.$enum.'/is' => 'VARCHAR(255)',
1847 '/COLLATE \w+_bin/is' => 'COLLATE BINARY',
1848 '/COLLATE \w+_ci/is' => 'COLLATE NOCASE',
1849 '/auto_increment/is' => '',
1850 '/(timestamp .* )ON .*$/is' => '\\1',
1851 '/character set \w+/is' => '',
1852 '/((big|small|medium|tiny)?int(eger)?)'.$num.'\s*unsigned/is' => '\\1 UNSIGNED',
1853 '/(text\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1854 '/((char|varchar)'.$num.'\s+not\s+null(\s+collate\s+\w+)?)\s*$/is' => "\\1 DEFAULT ''",
1855 '/(datetime\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00 00:00:00'",
1856 '/(date\s+not\s+null)\s*$/is' => "\\1 DEFAULT '0000-00-00'",
1857 );
1858
1859 // pour l'autoincrement, il faut des INTEGER NOT NULL PRIMARY KEY
1860 $remplace_autocinc = array(
1861 '/(big|small|medium|tiny)?int(eger)?'.$num.'/is' => 'INTEGER'
1862 );
1863 // pour les int non autoincrement, il faut un DEFAULT
1864 $remplace_nonautocinc = array(
1865 '/((big|small|medium|tiny)?int(eger)?'.$num.'\s+not\s+null)\s*$/is' => "\\1 DEFAULT 0",
1866 );
1867
1868 if (is_string($query)){
1869 $query = preg_replace(array_keys($remplace), $remplace, $query);
1870 if ($autoinc OR preg_match(',AUTO_INCREMENT,is',$query))
1871 $query = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query);
1872 else{
1873 $query = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query);
1874 $query = _sqlite_collate_ci($query);
1875 }
1876 }
1877 elseif(is_array($query)){
1878 foreach($query as $k=>$q) {
1879 $ai = ($autoinc?$k==$autoinc:preg_match(',AUTO_INCREMENT,is',$q));
1880 $query[$k] = preg_replace(array_keys($remplace), $remplace, $query[$k]);
1881 if ($ai)
1882 $query[$k] = preg_replace(array_keys($remplace_autocinc), $remplace_autocinc, $query[$k]);
1883 else{
1884 $query[$k] = preg_replace(array_keys($remplace_nonautocinc), $remplace_nonautocinc, $query[$k]);
1885 $query[$k] = _sqlite_collate_ci($query[$k]);
1886 }
1887 }
1888 }
1889 return $query;
1890 }
1891
1892 /**
1893 * Definir la collation d'un champ en fonction de si une collation est deja explicite
1894 * et du par defaut que l'on veut NOCASE
1895 * @param string $champ
1896 * @return string
1897 */
1898 function _sqlite_collate_ci($champ){
1899 if (stripos($champ,"COLLATE")!==false)
1900 return $champ;
1901 if (stripos($champ,"BINARY")!==false)
1902 return str_ireplace("BINARY","COLLATE BINARY",$champ);
1903 if (preg_match(",^(char|varchar|(long|small|medium|tiny)?text),i",$champ))
1904 return $champ . " COLLATE NOCASE";
1905
1906 return $champ;
1907 }
1908
1909
1910 /**
1911 * Creer la requete pour la creation d'une table
1912 * retourne la requete pour utilisation par sql_create() et sql_alter()
1913 *
1914 * http://doc.spip.org/@_sqlite_requete_create
1915 *
1916 * @param $nom
1917 * @param $champs
1918 * @param $cles
1919 * @param bool $autoinc
1920 * @param bool $temporary
1921 * @param bool $_ifnotexists
1922 * @param string $serveur
1923 * @param bool $requeter
1924 * @return bool|string
1925 */
1926 function _sqlite_requete_create($nom, $champs, $cles, $autoinc = false, $temporary = false, $_ifnotexists = true, $serveur = '', $requeter = true){
1927 $query = $keys = $s = $p = '';
1928
1929 // certains plugins declarent les tables (permet leur inclusion dans le dump)
1930 // sans les renseigner (laisse le compilo recuperer la description)
1931 if (!is_array($champs) || !is_array($cles))
1932 return;
1933
1934 // sqlite ne gere pas KEY tout court dans une requete CREATE TABLE
1935 // il faut passer par des create index
1936 // Il gere par contre primary key !
1937 // Soit la PK est definie dans les cles, soit dans un champs
1938 $c = ""; // le champ de cle primaire
1939 if (!isset($cles[$pk = "PRIMARY KEY"]) OR !$c = $cles[$pk]){
1940 foreach ($champs as $k => $v){
1941 if (false!==stripos($v, $pk)){
1942 $c = $k;
1943 // on n'en a plus besoin dans field, vu que defini dans key
1944 $champs[$k] = preg_replace("/$pk/is", '', $champs[$k]);
1945 break;
1946 }
1947 }
1948 }
1949 if ($c) $keys = "\n\t\t$pk ($c)";
1950 // Pas de DEFAULT 0 sur les cles primaires en auto-increment
1951 if (isset($champs[$c])
1952 AND stripos($champs[$c],"default 0")!==false){
1953 $champs[$c] = trim(str_ireplace("default 0","",$champs[$c]));
1954 }
1955
1956 $champs = _sqlite_remplacements_definitions_table($champs, $autoinc?$c:false);
1957 foreach ($champs as $k => $v){
1958 $query .= "$s\n\t\t$k $v";
1959 $s = ",";
1960 }
1961
1962 $ifnotexists = "";
1963 if ($_ifnotexists){
1964
1965 $version = spip_sqlite_fetch(spip_sqlite_query("select sqlite_version() AS sqlite_version",$serveur),'',$serveur);
1966 if (!function_exists('spip_version_compare')) include_spip('plugins/installer');
1967
1968 if ($version AND spip_version_compare($version['sqlite_version'],'3.3.0','>=')) {
1969 $ifnotexists = ' IF NOT EXISTS';
1970 } else {
1971 /* simuler le IF EXISTS - version 2 et sqlite < 3.3a */
1972 $a = spip_sqlite_showtable($table, $serveur);
1973 if (isset($a['key']['KEY '.$nom])) return true;
1974 }
1975
1976 }
1977
1978 $temporary = $temporary ? ' TEMPORARY' : '';
1979 $q = "CREATE$temporary TABLE$ifnotexists $nom ($query".($keys ? ",$keys" : '').")\n";
1980
1981 return $q;
1982 }
1983
1984
1985 /**
1986 * Retrouver les champs 'timestamp'
1987 * pour les ajouter aux 'insert' ou 'replace'
1988 * afin de simuler le fonctionnement de mysql
1989 *
1990 * stocke le resultat pour ne pas faire
1991 * de requetes showtable intempestives
1992 *
1993 * http://doc.spip.org/@_sqlite_ajouter_champs_timestamp
1994 *
1995 * @param $table
1996 * @param $couples
1997 * @param string $desc
1998 * @param string $serveur
1999 * @return
2000 */
2001 function _sqlite_ajouter_champs_timestamp($table, $couples, $desc = '', $serveur = ''){
2002 static $tables = array();
2003
2004 if (!isset($tables[$table])){
2005
2006 if (!$desc){
2007 $trouver_table = charger_fonction('trouver_table', 'base');
2008 $desc = $trouver_table($table, $serveur);
2009 // si pas de description, on ne fait rien, ou on die() ?
2010 if (!$desc) return $couples;
2011 }
2012
2013 // recherche des champs avec simplement 'TIMESTAMP'
2014 // cependant, il faudra peut etre etendre
2015 // avec la gestion de DEFAULT et ON UPDATE
2016 // mais ceux-ci ne sont pas utilises dans le core
2017 $tables[$table] = array();
2018
2019 foreach ($desc['field'] as $k => $v){
2020 if (strpos(strtolower(ltrim($v)), 'timestamp')===0)
2021 $tables[$table][$k] = "datetime('now')";
2022 }
2023 }
2024
2025 // ajout des champs type 'timestamp' absents
2026 return array_merge($tables[$table],$couples);
2027 }
2028
2029
2030 /**
2031 * renvoyer la liste des versions sqlite disponibles
2032 * sur le serveur
2033 * http://doc.spip.org/@spip_versions_sqlite
2034 *
2035 * @return array|bool
2036 */
2037 function spip_versions_sqlite(){
2038 return _sqlite_charger_version();
2039 }
2040
2041
2042 class spip_sqlite {
2043 static $requeteurs = array();
2044 static $transaction_en_cours = array();
2045
2046 function spip_sqlite(){}
2047
2048 /**
2049 * Retourne une unique instance du requĂȘteur
2050 *
2051 * Retourne une instance unique du requĂȘteur pour une connexion SQLite
2052 * donnée
2053 *
2054 * @param string $serveur
2055 * Nom du connecteur
2056 * @return sqlite_requeteur
2057 * Instance unique du requĂȘteur
2058 **/
2059 static function requeteur($serveur){
2060 if (!isset(spip_sqlite::$requeteurs[$serveur]))
2061 spip_sqlite::$requeteurs[$serveur] = new sqlite_requeteur($serveur);
2062 return spip_sqlite::$requeteurs[$serveur];
2063 }
2064
2065 static function traduire_requete($query, $serveur){
2066 $requeteur = spip_sqlite::requeteur($serveur);
2067 $traducteur = new sqlite_traducteur($query, $requeteur->prefixe,$requeteur->sqlite_version);
2068 return $traducteur->traduire_requete();
2069 }
2070
2071 static function demarrer_transaction($serveur){
2072 spip_sqlite::executer_requete("BEGIN TRANSACTION",$serveur);
2073 spip_sqlite::$transaction_en_cours[$serveur] = true;
2074 }
2075
2076 static function executer_requete($query, $serveur, $tracer=null){
2077 $requeteur = spip_sqlite::requeteur($serveur);
2078 return $requeteur->executer_requete($query, $tracer);
2079 }
2080
2081 static function last_insert_id($serveur){
2082 $requeteur = spip_sqlite::requeteur($serveur);
2083 return $requeteur->last_insert_id($serveur);
2084 }
2085
2086 static function annuler_transaction($serveur){
2087 spip_sqlite::executer_requete("ROLLBACK",$serveur);
2088 spip_sqlite::$transaction_en_cours[$serveur] = false;
2089 }
2090
2091 static function finir_transaction($serveur){
2092 // si pas de transaction en cours, ne rien faire et le dire
2093 if (!isset (spip_sqlite::$transaction_en_cours[$serveur])
2094 OR spip_sqlite::$transaction_en_cours[$serveur]==false)
2095 return false;
2096 // sinon fermer la transaction et retourner true
2097 spip_sqlite::executer_requete("COMMIT",$serveur);
2098 spip_sqlite::$transaction_en_cours[$serveur] = false;
2099 return true;
2100 }
2101 }
2102
2103 /*
2104 * Classe pour partager les lancements de requete
2105 * instanciee une fois par $serveur
2106 * - peut corriger la syntaxe des requetes pour la conformite a sqlite
2107 * - peut tracer les requetes
2108 *
2109 */
2110 class sqlite_requeteur {
2111 var $query = ''; // la requete
2112 var $serveur = ''; // le serveur
2113 var $link = ''; // le link (ressource) sqlite
2114 var $prefixe = ''; // le prefixe des tables
2115 var $db = ''; // le nom de la base
2116 var $tracer = false; // doit-on tracer les requetes (var_profile)
2117
2118 var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2119
2120 /**
2121 * constructeur
2122 * http://doc.spip.org/@sqlite_traiter_requete
2123 *
2124 * @param $query
2125 * @param string $serveur
2126 * @return bool
2127 */
2128 function sqlite_requeteur($serveur = ''){
2129 _sqlite_init();
2130 $this->serveur = strtolower($serveur);
2131
2132 if (!($this->link = _sqlite_link($this->serveur)) && (!defined('_ECRIRE_INSTALL') || !_ECRIRE_INSTALL)){
2133 spip_log("Aucune connexion sqlite (link)", 'sqlite.'._LOG_ERREUR);
2134 return false;
2135 }
2136
2137 $this->sqlite_version = _sqlite_is_version('', $this->link);
2138
2139 $this->prefixe = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['prefixe'];
2140 $this->db = $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['db'];
2141
2142 // tracage des requetes ?
2143 $this->tracer = (isset($_GET['var_profile']) && $_GET['var_profile']);
2144 }
2145
2146 /**
2147 * lancer la requete $query,
2148 * faire le tracage si demande
2149 * http://doc.spip.org/@executer_requete
2150 *
2151 * @return bool|SQLiteResult
2152 */
2153 function executer_requete($query, $tracer=null){
2154 if (is_null($tracer))
2155 $tracer = $this->tracer;
2156 $err = "";
2157 $t = 0;
2158 if ($tracer){
2159 include_spip('public/tracer');
2160 $t = trace_query_start();
2161 }
2162
2163 # spip_log("requete: $this->serveur >> $query",'sqlite.'._LOG_DEBUG); // boum ? pourquoi ?
2164 if ($this->link){
2165 // memoriser la derniere erreur PHP vue
2166 $e = (function_exists('error_get_last')?error_get_last():"");
2167 // sauver la derniere requete
2168 $GLOBALS['connexions'][$this->serveur ? $this->serveur : 0]['last'] = $query;
2169
2170 if ($this->sqlite_version==3){
2171 $r = $this->link->query($query);
2172 // sauvegarde de la requete (elle y est deja dans $r->queryString)
2173 # $r->spipQueryString = $query;
2174
2175 // comptage : oblige de compter le nombre d'entrees retournees
2176 // par une requete SELECT
2177 // aucune autre solution ne donne le nombre attendu :( !
2178 // particulierement s'il y a des LIMIT dans la requete.
2179 if (strtoupper(substr(ltrim($query), 0, 6))=='SELECT'){
2180 if ($r){
2181 // noter le link et la query pour faire le comptage *si* on en a besoin
2182 $r->spipSqliteRowCount = array($this->link,$query);
2183 }
2184 elseif ($r instanceof PDOStatement) {
2185 $r->spipSqliteRowCount = 0;
2186 }
2187 }
2188 }
2189 else {
2190 $r = sqlite_query($this->link, $query);
2191 }
2192
2193 // loger les warnings/erreurs eventuels de sqlite remontant dans PHP
2194 if ($err = (function_exists('error_get_last')?error_get_last():"") AND $err!=$e){
2195 $err = strip_tags($err['message'])." in ".$err['file']." line ".$err['line'];
2196 spip_log("$err - ".$query, 'sqlite.'._LOG_ERREUR);
2197 }
2198 else $err = "";
2199
2200 }
2201 else {
2202 $r = false;
2203 }
2204
2205 if (spip_sqlite_errno($this->serveur))
2206 $err .= spip_sqlite_error($query, $this->serveur);
2207 return $t ? trace_query_end($query, $t, $r, $err, $this->serveur) : $r;
2208 }
2209
2210 function last_insert_id(){
2211 if ($this->sqlite_version==3)
2212 return $this->link->lastInsertId();
2213 else
2214 return sqlite_last_insert_rowid($this->link);
2215 }
2216 }
2217
2218
2219 /**
2220 * Cette classe est presente essentiellement pour un preg_replace_callback
2221 * avec des parametres dans la fonction appelee que l'on souhaite incrementer
2222 * (fonction pour proteger les textes)
2223 */
2224 class sqlite_traducteur {
2225 var $query = '';
2226 var $prefixe = ''; // le prefixe des tables
2227 var $sqlite_version = ''; // Version de sqlite (2 ou 3)
2228
2229 // Pour les corrections a effectuer sur les requetes :
2230 var $textes = array(); // array(code=>'texte') trouvé
2231
2232 function sqlite_traducteur($query, $prefixe, $sqlite_version){
2233 $this->query = $query;
2234 $this->prefixe = $prefixe;
2235 $this->sqlite_version = $sqlite_version;
2236 }
2237
2238 /**
2239 * transformer la requete pour sqlite
2240 * enleve les textes, transforme la requete pour quelle soit
2241 * bien interpretee par sqlite, puis remet les textes
2242 * la fonction affecte $this->query
2243 * http://doc.spip.org/@traduire_requete
2244 *
2245 * @return void
2246 */
2247 function traduire_requete(){
2248 //
2249 // 1) Protection des textes en les remplacant par des codes
2250 //
2251 // enlever les 'textes' et initialiser avec
2252 list($this->query, $textes) = query_echappe_textes($this->query);
2253
2254 //
2255 // 2) Corrections de la requete
2256 //
2257 // Correction Create Database
2258 // Create Database -> requete ignoree
2259 if (strpos($this->query, 'CREATE DATABASE')===0){
2260 spip_log("Sqlite : requete non executee -> $this->query", 'sqlite.'._LOG_AVERTISSEMENT);
2261 $this->query = "SELECT 1";
2262 }
2263
2264 // Correction Insert Ignore
2265 // INSERT IGNORE -> insert (tout court et pas 'insert or replace')
2266 if (strpos($this->query, 'INSERT IGNORE')===0){
2267 spip_log("Sqlite : requete transformee -> $this->query", 'sqlite.'._LOG_DEBUG);
2268 $this->query = 'INSERT '.substr($this->query, '13');
2269 }
2270
2271 // Correction des dates avec INTERVAL
2272 // utiliser sql_date_proche() de preference
2273 if (strpos($this->query, 'INTERVAL')!==false){
2274 $this->query = preg_replace_callback("/DATE_(ADD|SUB)(.*)INTERVAL\s+(\d+)\s+([a-zA-Z]+)\)/U",
2275 array(&$this, '_remplacerDateParTime'),
2276 $this->query);
2277 }
2278
2279 if (strpos($this->query, 'LEFT(')!==false){
2280 $this->query = str_replace('LEFT(','_LEFT(',$this->query);
2281 }
2282
2283 if (strpos($this->query, 'TIMESTAMPDIFF(')!==false){
2284 $this->query = preg_replace('/TIMESTAMPDIFF\(\s*([^,]*)\s*,/Uims',"TIMESTAMPDIFF('\\1',",$this->query);
2285 }
2286
2287
2288 // Correction Using
2289 // USING (non reconnu en sqlite2)
2290 // problematique car la jointure ne se fait pas du coup.
2291 if (($this->sqlite_version==2) && (strpos($this->query, "USING")!==false)){
2292 spip_log("'USING (champ)' n'est pas reconnu en SQLite 2. Utilisez 'ON table1.champ = table2.champ'", 'sqlite.'._LOG_ERREUR);
2293 $this->query = preg_replace('/USING\s*\([^\)]*\)/', '', $this->query);
2294 }
2295
2296 // Correction Field
2297 // remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2298 if (strpos($this->query, 'FIELD')!==false){
2299 $this->query = preg_replace_callback('/FIELD\s*\(([^\)]*)\)/',
2300 array(&$this, '_remplacerFieldParCase'),
2301 $this->query);
2302 }
2303
2304 // Correction des noms de tables FROM
2305 // mettre les bons noms de table dans from, update, insert, replace...
2306 if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/iS', $this->query, $regs)){
2307 $suite = strstr($this->query, $regs[0]);
2308 $this->query = substr($this->query, 0, -strlen($suite));
2309 }
2310 else
2311 $suite = '';
2312 $pref = ($this->prefixe) ? $this->prefixe."_" : "";
2313 $this->query = preg_replace('/([,\s])spip_/S', '\1'.$pref, $this->query).$suite;
2314
2315 // Correction zero AS x
2316 // pg n'aime pas 0+x AS alias, sqlite, dans le meme style,
2317 // n'apprecie pas du tout SELECT 0 as x ... ORDER BY x
2318 // il dit que x ne doit pas ĂȘtre un integer dans le order by !
2319 // on remplace du coup x par vide() dans ce cas uniquement
2320 //
2321 // rien que pour public/vertebrer.php ?
2322 if ((strpos($this->query, "0 AS")!==false)){
2323 // on ne remplace que dans ORDER BY ou GROUP BY
2324 if (preg_match('/\s(ORDER|GROUP) BY\s/i', $this->query, $regs)){
2325 $suite = strstr($this->query, $regs[0]);
2326 $this->query = substr($this->query, 0, -strlen($suite));
2327
2328 // on cherche les noms des x dans 0 AS x
2329 // on remplace dans $suite le nom par vide()
2330 preg_match_all('/\b0 AS\s*([^\s,]+)/', $this->query, $matches, PREG_PATTERN_ORDER);
2331 foreach ($matches[1] as $m){
2332 $suite = str_replace($m, 'VIDE()', $suite);
2333 }
2334 $this->query .= $suite;
2335 }
2336 }
2337
2338 // Correction possible des divisions entieres
2339 // Le standard SQL (lequel? ou?) semble indiquer que
2340 // a/b=c doit donner c entier si a et b sont entiers 4/3=1.
2341 // C'est ce que retournent effectivement SQL Server et SQLite
2342 // Ce n'est pas ce qu'applique MySQL qui retourne un reel : 4/3=1.333...
2343 //
2344 // On peut forcer la conversion en multipliant par 1.0 avant la division
2345 // /!\ SQLite 3.5.9 Debian/Ubuntu est victime d'un bug en plus !
2346 // cf. https://bugs.launchpad.net/ubuntu/+source/sqlite3/+bug/254228
2347 // http://www.sqlite.org/cvstrac/tktview?tn=3202
2348 // (4*1.0/3) n'est pas rendu dans ce cas !
2349 # $this->query = str_replace('/','* 1.00 / ',$this->query);
2350
2351
2352 // Correction critere REGEXP, non reconnu en sqlite2
2353 if (($this->sqlite_version==2) && (strpos($this->query, 'REGEXP')!==false)){
2354 $this->query = preg_replace('/([^\s\(]*)(\s*)REGEXP(\s*)([^\s\)]*)/', 'REGEXP($4, $1)', $this->query);
2355 }
2356
2357 //
2358 // 3) Remise en place des textes d'origine
2359 //
2360 // Correction Antiquotes et echappements
2361 // ` => rien
2362 if (strpos($this->query,'`')!==false)
2363 $this->query = str_replace('`','', $this->query);
2364
2365 $this->query = query_reinjecte_textes($this->query, $textes);
2366
2367 return $this->query;
2368 }
2369
2370
2371 /**
2372 * les callbacks
2373 * remplacer DATE_ / INTERVAL par DATE...strtotime
2374 * http://doc.spip.org/@_remplacerDateParTime
2375 *
2376 * @param $matches
2377 * @return string
2378 */
2379 function _remplacerDateParTime($matches){
2380 $op = strtoupper($matches[1]=='ADD') ? '+' : '-';
2381 return "datetime$matches[2] '$op$matches[3] $matches[4]')";
2382 }
2383
2384 /**
2385 * callback ou l'on remplace FIELD(table,i,j,k...) par CASE WHEN table=i THEN n ... ELSE 0 END
2386 * http://doc.spip.org/@_remplacerFieldParCase
2387 *
2388 * @param $matches
2389 * @return string
2390 */
2391 function _remplacerFieldParCase($matches){
2392 $fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
2393 $t = explode(',', $fields);
2394 $index = array_shift($t);
2395
2396 $res = '';
2397 $n = 0;
2398 foreach ($t as $v){
2399 $n++;
2400 $res .= "\nWHEN $index=$v THEN $n";
2401 }
2402 return "CASE $res ELSE 0 END ";
2403 }
2404
2405 }
2406
2407 ?>