b485cd870d91c7efffc6c341ec3fa98ecad3aec1
[ptitvelo/web/www.git] / www / ecrire / req / mysql.php
1 <?php
2
3 /* *************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2012 *
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 MySQL
16 *
17 * @package SPIP\SQL\MySQL
18 */
19
20 if (!defined('_ECRIRE_INC_VERSION')) return;
21
22 // fonction pour la premiere connexion a un serveur MySQL
23
24 // http://doc.spip.org/@req_mysql_dist
25 /**
26 * @param $host
27 * @param $port
28 * @param $login
29 * @param $pass
30 * @param string $db
31 * @param string $prefixe
32 * @return array|bool
33 */
34
35 function req_mysql_dist($host, $port, $login, $pass, $db='', $prefixe='') {
36 if (!charger_php_extension('mysql')) return false;
37 if ($port > 0) $host = "$host:$port";
38 $link = @mysql_connect($host, $login, $pass, true);
39 if (!$link) {
40 spip_log('Echec mysql_connect. Erreur : ' . mysql_error(),'mysql.'._LOG_HS);
41 return false;
42 }
43 $last = '';
44 if (!$db) {
45 $ok = $link;
46 $db = 'spip';
47 } else {
48 $ok = spip_mysql_selectdb($db);
49 if (defined('_MYSQL_SET_SQL_MODE')
50 OR defined('_MYSQL_SQL_MODE_TEXT_NOT_NULL') // compatibilite
51 )
52 mysql_query($last = "set sql_mode=''");
53 }
54 spip_log("Connexion vers $host, base $db, prefixe $prefixe " . ($ok ? "operationnelle sur $link" : 'impossible'), _LOG_DEBUG);
55
56 return !$ok ? false : array(
57 'db' => $db,
58 'last' => $last,
59 'prefixe' => $prefixe ? $prefixe : $db,
60 'link' => $GLOBALS['mysql_rappel_connexion'] ? $link : false,
61 );
62 }
63
64 $GLOBALS['spip_mysql_functions_1'] = array(
65 'alter' => 'spip_mysql_alter',
66 'count' => 'spip_mysql_count',
67 'countsel' => 'spip_mysql_countsel',
68 'create' => 'spip_mysql_create',
69 'create_base' => 'spip_mysql_create_base',
70 'create_view' => 'spip_mysql_create_view',
71 'date_proche' => 'spip_mysql_date_proche',
72 'delete' => 'spip_mysql_delete',
73 'drop_table' => 'spip_mysql_drop_table',
74 'drop_view' => 'spip_mysql_drop_view',
75 'errno' => 'spip_mysql_errno',
76 'error' => 'spip_mysql_error',
77 'explain' => 'spip_mysql_explain',
78 'fetch' => 'spip_mysql_fetch',
79 'seek' => 'spip_mysql_seek',
80 'free' => 'spip_mysql_free',
81 'hex' => 'spip_mysql_hex',
82 'in' => 'spip_mysql_in',
83 'insert' => 'spip_mysql_insert',
84 'insertq' => 'spip_mysql_insertq',
85 'insertq_multi' => 'spip_mysql_insertq_multi',
86 'listdbs' => 'spip_mysql_listdbs',
87 'multi' => 'spip_mysql_multi',
88 'optimize' => 'spip_mysql_optimize',
89 'query' => 'spip_mysql_query',
90 'quote' => 'spip_mysql_quote',
91 'replace' => 'spip_mysql_replace',
92 'replace_multi' => 'spip_mysql_replace_multi',
93 'repair' => 'spip_mysql_repair',
94 'select' => 'spip_mysql_select',
95 'selectdb' => 'spip_mysql_selectdb',
96 'set_charset' => 'spip_mysql_set_charset',
97 'get_charset' => 'spip_mysql_get_charset',
98 'showbase' => 'spip_mysql_showbase',
99 'showtable' => 'spip_mysql_showtable',
100 'update' => 'spip_mysql_update',
101 'updateq' => 'spip_mysql_updateq',
102
103 // association de chaque nom http d'un charset aux couples MySQL
104 'charsets' => array(
105 'cp1250'=>array('charset'=>'cp1250','collation'=>'cp1250_general_ci'),
106 'cp1251'=>array('charset'=>'cp1251','collation'=>'cp1251_general_ci'),
107 'cp1256'=>array('charset'=>'cp1256','collation'=>'cp1256_general_ci'),
108 'iso-8859-1'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
109 //'iso-8859-6'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
110 'iso-8859-9'=>array('charset'=>'latin5','collation'=>'latin5_turkish_ci'),
111 //'iso-8859-15'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
112 'utf-8'=>array('charset'=>'utf8','collation'=>'utf8_general_ci'))
113 );
114
115 // http://doc.spip.org/@spip_mysql_set_charset
116 /**
117 * @param $charset
118 * @param string $serveur
119 * @param bool $requeter
120 * @param bool $requeter
121 * @return resource
122 */
123 function spip_mysql_set_charset($charset, $serveur='',$requeter=true,$requeter=true){
124 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
125 spip_log("changement de charset sql : "."SET NAMES "._q($charset), _LOG_DEBUG);
126 return mysql_query($connexion['last'] = "SET NAMES "._q($charset));
127 }
128
129 // http://doc.spip.org/@spip_mysql_get_charset
130 /**
131
132 * @param array $charset
133 * @param string $serveur
134 * @param bool $requeter
135 * @return array
136 *
137 */
138 function spip_mysql_get_charset($charset=array(), $serveur='',$requeter=true){
139 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
140 $connexion['last'] = $c = "SHOW CHARACTER SET"
141 . (!$charset ? '' : (" LIKE "._q($charset['charset'])));
142
143 return spip_mysql_fetch(mysql_query($c), NULL, $serveur);
144 }
145
146 // obsolete, ne plus utiliser
147 // http://doc.spip.org/@spip_query_db
148 function spip_query_db($query, $serveur='',$requeter=true) {
149 return spip_mysql_query($query, $serveur, $requeter);
150 }
151
152 // Fonction de requete generale, munie d'une trace a la demande
153
154 // http://doc.spip.org/@spip_mysql_query
155 /**
156
157 * @param $query
158 * @param string $serveur
159 * @param bool $requeter
160 * @return array|null|resource|string
161 *
162 */
163 function spip_mysql_query($query, $serveur='',$requeter=true) {
164
165 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
166 $prefixe = $connexion['prefixe'];
167 $link = $connexion['link'];
168 $db = $connexion['db'];
169
170 $query = traite_query($query, $db, $prefixe);
171
172 // renvoyer la requete inerte si demandee
173 if (!$requeter) return $query;
174
175 if (isset($_GET['var_profile'])) {
176 include_spip('public/tracer');
177 $t = trace_query_start();
178 } else $t = 0 ;
179
180 $connexion['last'] = $query;
181 $r = $link ? mysql_query($query, $link) : mysql_query($query);
182
183 if ($e = spip_mysql_errno($serveur)) // Log de l'erreur eventuelle
184 $e .= spip_mysql_error($query, $serveur); // et du fautif
185 return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
186 }
187
188 // http://doc.spip.org/@spip_mysql_alter
189 /**
190 * @param $query
191 * @param string $serveur
192 * @param bool $requeter
193 * @return array|null|resource|string
194 */
195 function spip_mysql_alter($query, $serveur='',$requeter=true){
196 // ici on supprime les ` entourant le nom de table pour permettre
197 // la transposition du prefixe, compte tenu que les plugins ont la mauvaise habitude
198 // d'utiliser ceux-ci, copie-colle de phpmyadmin
199 $query = preg_replace(",^TABLE\s*`([^`]*)`,i","TABLE \\1",$query);
200 return spip_mysql_query("ALTER ".$query, $serveur, $requeter); # i.e. que PG se debrouille
201 }
202
203 // http://doc.spip.org/@spip_mysql_optimize
204 /**
205 * @param $table
206 * @param string $serveur
207 * @param bool $requeter
208 * @return bool
209 */
210 function spip_mysql_optimize($table, $serveur='',$requeter=true){
211 spip_mysql_query("OPTIMIZE TABLE ". $table);
212 return true;
213 }
214
215 // http://doc.spip.org/@spip_mysql_explain
216 /**
217 * @param $query
218 * @param string $serveur
219 * @param bool $requeter
220 * @return array
221 */
222 function spip_mysql_explain($query, $serveur='',$requeter=true){
223 if (strpos(ltrim($query), 'SELECT') !== 0) return array();
224 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
225 $prefixe = $connexion['prefixe'];
226 $link = $connexion['link'];
227 $db = $connexion['db'];
228
229 $query = 'EXPLAIN ' . traite_query($query, $db, $prefixe);
230 $r = $link ? mysql_query($query, $link) : mysql_query($query);
231 return spip_mysql_fetch($r, NULL, $serveur);
232 }
233 // fonction instance de sql_select, voir ses specs dans abstract.php
234 // traite_query pourrait y etre fait d'avance ce serait moins cher
235 // Les \n et \t sont utiles au debusqueur.
236
237
238 // http://doc.spip.org/@spip_mysql_select
239 /**
240 * @param $select
241 * @param $from
242 * @param string $where
243 * @param string $groupby
244 * @param string $orderby
245 * @param string $limit
246 * @param string $having
247 * @param string $serveur
248 * @param bool $requeter
249 * @return array|null|resource|string
250 */
251 function spip_mysql_select($select, $from, $where='',
252 $groupby='', $orderby='', $limit='', $having='',
253 $serveur='',$requeter=true) {
254
255
256 $from = (!is_array($from) ? $from : spip_mysql_select_as($from));
257 $query =
258 calculer_mysql_expression('SELECT', $select, ', ')
259 . calculer_mysql_expression('FROM', $from, ', ')
260 . calculer_mysql_expression('WHERE', $where)
261 . calculer_mysql_expression('GROUP BY', $groupby, ',')
262 . calculer_mysql_expression('HAVING', $having)
263 . ($orderby ? ("\nORDER BY " . spip_mysql_order($orderby)) :'')
264 . ($limit ? "\nLIMIT $limit" : '');
265
266 // renvoyer la requete inerte si demandee
267 if ($requeter === false) return $query;
268 $r = spip_mysql_query($query, $serveur, $requeter);
269 return $r ? $r : $query;
270 }
271
272 // 0+x avec un champ x commencant par des chiffres est converti par MySQL
273 // en le nombre qui commence x.
274 // Pas portable malheureusement, on laisse pour le moment.
275
276 // http://doc.spip.org/@spip_mysql_order
277 /**
278 * @param $orderby
279 * @return string
280 */
281 function spip_mysql_order($orderby)
282 {
283 return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
284 }
285
286
287 // http://doc.spip.org/@calculer_mysql_where
288 /**
289 * @param $v
290 * @return array|mixed|string
291 */
292 function calculer_mysql_where($v)
293 {
294 if (!is_array($v))
295 return $v ;
296
297 $op = array_shift($v);
298 if (!($n=count($v)))
299 return $op;
300 else {
301 $arg = calculer_mysql_where(array_shift($v));
302 if ($n==1) {
303 return "$op($arg)";
304 } else {
305 $arg2 = calculer_mysql_where(array_shift($v));
306 if ($n==2) {
307 return "($arg $op $arg2)";
308 } else return "($arg $op ($arg2) : $v[0])";
309 }
310 }
311 }
312
313 // http://doc.spip.org/@calculer_mysql_expression
314 /**
315 * @param $expression
316 * @param $v
317 * @param string $join
318 * @return string
319 */
320 function calculer_mysql_expression($expression, $v, $join = 'AND'){
321 if (empty($v))
322 return '';
323
324 $exp = "\n$expression ";
325
326 if (!is_array($v)) {
327 return $exp . $v;
328 } else {
329 if (strtoupper($join) === 'AND')
330 return $exp . join("\n\t$join ", array_map('calculer_mysql_where', $v));
331 else
332 return $exp . join($join, $v);
333 }
334 }
335
336 // http://doc.spip.org/@spip_mysql_select_as
337 /**
338 * @param $args
339 * @return string
340 */
341 function spip_mysql_select_as($args)
342 {
343 $res = '';
344 foreach($args as $k => $v) {
345 if (substr($k,-1)=='@') {
346 // c'est une jointure qui se refere au from precedent
347 // pas de virgule
348 $res .= ' ' . $v ;
349 }
350 else {
351 if (!is_numeric($k)) {
352 $p = strpos($v, " ");
353 if ($p)
354 $v = substr($v,0,$p) . " AS `$k`" . substr($v,$p);
355 else $v .= " AS `$k`";
356 }
357
358 $res .= ', ' . $v ;
359 }
360 }
361 return substr($res,2);
362 }
363
364 //
365 // Changer les noms des tables ($table_prefix)
366 // Quand tous les appels SQL seront abstraits on pourra l'ameliorer
367
368 define('_SQL_PREFIXE_TABLE', '/([,\s])spip_/S');
369
370 // http://doc.spip.org/@traite_query
371 /**
372 * @param $query
373 * @param string $db
374 * @param string $prefixe
375 * @return array|null|string
376 */
377 function traite_query($query, $db='', $prefixe='') {
378
379 if ($GLOBALS['mysql_rappel_nom_base'] AND $db)
380 $pref = '`'. $db.'`.';
381 else $pref = '';
382
383 if ($prefixe)
384 $pref .= $prefixe . "_";
385
386 if (!preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
387 $suite ='';
388 } else {
389 $suite = strstr($query, $regs[0]);
390 $query = substr($query, 0, -strlen($suite));
391 // propager le prefixe en cas de requete imbriquee
392 // il faut alors echapper les chaine avant de le faire, pour ne pas risquer de
393 // modifier une requete qui est en fait juste du texte dans un champ
394 if (stripos($suite,"SELECT")!==false) {
395 list($suite,$textes) = query_echappe_textes($suite);
396 if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite, $r))
397 $suite = $r[1] . traite_query($r[2], $db, $prefixe);
398 $suite = query_reinjecte_textes($suite, $textes);
399 }
400 }
401 $r = preg_replace(_SQL_PREFIXE_TABLE, '\1'.$pref, $query) . $suite;
402
403 #spip_log("traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
404 return $r;
405 }
406
407 /**
408 * Selectionne une base de donnees
409 *
410 * @param string $nom
411 * Nom de la base a utiliser
412 *
413 * @return bool
414 * True cas de success.
415 * False en cas d'erreur.
416 **/
417 function spip_mysql_selectdb($db) {
418 $ok = mysql_select_db($db);
419 if (!$ok)
420 spip_log('Echec mysql_selectdb. Erreur : ' . mysql_error(),'mysql.'._LOG_CRITIQUE);
421 return $ok;
422 }
423
424
425 /**
426 * Retourne les bases de donnees accessibles
427 *
428 * Retourne un tableau du nom de toutes les bases de donnees
429 * accessibles avec les permissions de l'utilisateur SQL
430 * de cette connexion.
431 * Attention on n'a pas toujours les droits !
432 *
433 * @param string $serveur
434 * Nom du connecteur
435 * @param bool $requeter
436 * Inutilise
437 * @return array
438 * Liste de noms de bases de donnees
439 **/
440 function spip_mysql_listdbs($serveur='',$requeter=true) {
441 $dbs = array();
442 if ($res = spip_mysql_query("SHOW DATABASES")){
443 while($row = mysql_fetch_assoc($res))
444 $dbs[] = $row['Database'];
445 }
446 return $dbs;
447 }
448
449 // Fonction de creation d'une table SQL nommee $nom
450 // a partir de 2 tableaux PHP :
451 // champs: champ => type
452 // cles: type-de-cle => champ(s)
453 // si $autoinc, c'est une auto-increment (i.e. serial) sur la Primary Key
454 // Le nom des caches doit etre inferieur a 64 caracteres
455
456 // http://doc.spip.org/@spip_mysql_create
457 /**
458 * @param $nom
459 * @param $champs
460 * @param $cles
461 * @param bool $autoinc
462 * @param bool $temporary
463 * @param string $serveur
464 * @param bool $requeter
465 * @return array|null|resource|string
466 */
467 function spip_mysql_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='',$requeter=true) {
468
469 $query = ''; $keys = ''; $s = ''; $p='';
470
471 // certains plugins declarent les tables (permet leur inclusion dans le dump)
472 // sans les renseigner (laisse le compilo recuperer la description)
473 if (!is_array($champs) || !is_array($cles))
474 return;
475
476 $res = spip_mysql_query("SELECT version() as v");
477 if ($row = mysql_fetch_array($res)
478 && (version_compare($row['v'],'5.0','>=')))
479 spip_mysql_query("SET sql_mode=''");
480
481 foreach($cles as $k => $v) {
482 $keys .= "$s\n\t\t$k ($v)";
483 if ($k == "PRIMARY KEY")
484 $p = $v;
485 $s = ",";
486 }
487 $s = '';
488
489 $character_set = "";
490 if (@$GLOBALS['meta']['charset_sql_base'])
491 $character_set .= " CHARACTER SET ".$GLOBALS['meta']['charset_sql_base'];
492 if (@$GLOBALS['meta']['charset_collation_sql_base'])
493 $character_set .= " COLLATE ".$GLOBALS['meta']['charset_collation_sql_base'];
494
495 foreach($champs as $k => $v) {
496 $v = _mysql_remplacements_definitions_table($v);
497 if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i',$v,$defs)){
498 if (preg_match(',(char|text),i',$defs[1])
499 AND !preg_match(',(binary|CHARACTER|COLLATE),i',$v) ){
500 $v = $defs[1] . $character_set . ' ' . substr($v,strlen($defs[1]));
501 }
502 }
503
504 $query .= "$s\n\t\t$k $v"
505 . (($autoinc && ($p == $k) && preg_match(',\b(big|small|medium)?int\b,i', $v))
506 ? " auto_increment"
507 : ''
508 );
509 $s = ",";
510 }
511 $temporary = $temporary ? 'TEMPORARY':'';
512 $q = "CREATE $temporary TABLE IF NOT EXISTS $nom ($query" . ($keys ? ",$keys" : '') . ")".
513 ($character_set?" DEFAULT $character_set":"")
514 ."\n";
515 return spip_mysql_query($q, $serveur);
516 }
517
518
519 /**
520 * Adapte pour Mysql la declaration SQL d'une colonne d'une table
521 *
522 * @param string $query
523 * Definition SQL d'un champ de table
524 * @return string
525 * Definition SQL adaptee pour MySQL d'un champ de table
526 */
527 function _mysql_remplacements_definitions_table($query){
528 // quelques remplacements
529 $num = "(\s*\([0-9]*\))?";
530 $enum = "(\s*\([^\)]*\))?";
531
532 $remplace = array(
533 '/VARCHAR(\s*[^\s\(])/is' => 'VARCHAR(255)\\1',
534 );
535
536 $query = preg_replace(array_keys($remplace), $remplace, $query);
537 return $query;
538 }
539
540 /**
541 * @param $nom
542 * @param string $serveur
543 * @param bool $requeter
544 * @return array|null|resource|string
545 */
546 function spip_mysql_create_base($nom, $serveur='',$requeter=true) {
547 return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
548 }
549
550 // Fonction de creation d'une vue SQL nommee $nom
551 // http://doc.spip.org/@spip_mysql_create_view
552 /**
553 * @param $nom
554 * @param $query_select
555 * @param string $serveur
556 * @param bool $requeter
557 * @return array|bool|null|resource|string
558 */
559 function spip_mysql_create_view($nom, $query_select, $serveur='',$requeter=true) {
560 if (!$query_select) return false;
561 // vue deja presente
562 if (sql_showtable($nom, false, $serveur)) {
563 spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
564 return false;
565 }
566
567 $query = "CREATE VIEW $nom AS ". $query_select;
568 return spip_mysql_query($query, $serveur, $requeter);
569 }
570
571
572 // http://doc.spip.org/@spip_mysql_drop_table
573 /**
574 * @param $table
575 * @param string $exist
576 * @param string $serveur
577 * @param bool $requeter
578 * @return array|null|resource|string
579 */
580 function spip_mysql_drop_table($table, $exist='', $serveur='',$requeter=true)
581 {
582 if ($exist) $exist =" IF EXISTS";
583 return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
584 }
585
586 // supprime une vue
587 // http://doc.spip.org/@spip_mysql_drop_view
588 /**
589 * @param $view
590 * @param string $exist
591 * @param string $serveur
592 * @param bool $requeter
593 * @return array|null|resource|string
594 */
595 function spip_mysql_drop_view($view, $exist='', $serveur='',$requeter=true) {
596 if ($exist) $exist =" IF EXISTS";
597 return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
598 }
599
600 /**
601 * Retourne une ressource de la liste des tables de la base de données
602 *
603 * @param string $match
604 * Filtre sur tables à récupérer
605 * @param string $serveur
606 * Connecteur de la base
607 * @param bool $requeter
608 * true pour éxecuter la requête
609 * false pour retourner le texte de la requête.
610 * @return ressource
611 * Ressource à utiliser avec sql_fetch()
612 **/
613 function spip_mysql_showbase($match, $serveur='',$requeter=true)
614 {
615 return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
616 }
617
618 // http://doc.spip.org/@spip_mysql_repair
619 /**
620 * @param $table
621 * @param string $serveur
622 * @param bool $requeter
623 * @return array|null|resource|string
624 */
625 function spip_mysql_repair($table, $serveur='',$requeter=true)
626 {
627 return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
628 }
629
630 // Recupere la definition d'une table ou d'une vue MySQL
631 // colonnes, indexes, etc.
632 // au meme format que la definition des tables de SPIP
633 // http://doc.spip.org/@spip_mysql_showtable
634 /**
635 * @param $nom_table
636 * @param string $serveur
637 * @param bool $requeter
638 * @return array|null|resource|string
639 */
640 function spip_mysql_showtable($nom_table, $serveur='',$requeter=true)
641 {
642 $s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
643 if (!$s) return '';
644 if (!$requeter) return $s;
645
646 list(,$a) = mysql_fetch_array($s ,MYSQL_NUM);
647 if (preg_match("/^[^(),]*\((([^()]*\([^()]*\)[^()]*)*)\)[^()]*$/", $a, $r)){
648 $desc = $r[1];
649 // extraction d'une KEY éventuelle en prenant garde de ne pas
650 // relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
651 if (preg_match("/^(.*?),([^,]*KEY[ (].*)$/s", $desc, $r)) {
652 $namedkeys = $r[2];
653 $desc = $r[1];
654 }
655 else
656 $namedkeys = "";
657
658 $fields = array();
659 foreach(preg_split("/,\s*`/",$desc) as $v) {
660 preg_match("/^\s*`?([^`]*)`\s*(.*)/",$v,$r);
661 $fields[strtolower($r[1])] = $r[2];
662 }
663 $keys = array();
664
665 foreach(preg_split('/\)\s*,?/',$namedkeys) as $v) {
666 if (preg_match("/^\s*([^(]*)\((.*)$/",$v,$r)) {
667 $k = str_replace("`", '', trim($r[1]));
668 $t = strtolower(str_replace("`", '', $r[2]));
669 if ($k && !isset($keys[$k])) $keys[$k] = $t; else $keys[] = $t;
670 }
671 }
672 spip_mysql_free($s);
673 return array('field' => $fields, 'key' => $keys);
674 }
675
676 $res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
677 if($res) {
678 $nfields = array();
679 $nkeys = array();
680 while($val = spip_mysql_fetch($res)) {
681 $nfields[$val["Field"]] = $val['Type'];
682 if($val['Null']=='NO') {
683 $nfields[$val["Field"]] .= ' NOT NULL';
684 }
685 if($val['Default'] === '0' || $val['Default']) {
686 if(preg_match('/[A-Z_]/',$val['Default'])) {
687 $nfields[$val["Field"]] .= ' DEFAULT '.$val['Default'];
688 } else {
689 $nfields[$val["Field"]] .= " DEFAULT '".$val['Default']."'";
690 }
691 }
692 if($val['Extra'])
693 $nfields[$val["Field"]] .= ' '.$val['Extra'];
694 if($val['Key'] == 'PRI') {
695 $nkeys['PRIMARY KEY'] = $val["Field"];
696 } else if($val['Key'] == 'MUL') {
697 $nkeys['KEY '.$val["Field"]] = $val["Field"];
698 } else if($val['Key'] == 'UNI') {
699 $nkeys['UNIQUE KEY '.$val["Field"]] = $val["Field"];
700 }
701 }
702 spip_mysql_free($res);
703 return array('field' => $nfields, 'key' => $nkeys);
704 }
705 return "";
706 }
707
708 //
709 // Recuperation des resultats
710 //
711
712 // http://doc.spip.org/@spip_mysql_fetch
713 /**
714 * @param $r
715 * @param string $t
716 * @param string $serveur
717 * @param bool $requeter
718 * @return array
719 */
720 function spip_mysql_fetch($r, $t='', $serveur='',$requeter=true) {
721 if (!$t) $t = MYSQL_ASSOC;
722 if ($r) return mysql_fetch_array($r, $t);
723 }
724
725 function spip_mysql_seek($r, $row_number, $serveur='',$requeter=true) {
726 if ($r and mysql_num_rows($r)) return mysql_data_seek($r,$row_number);
727 }
728
729
730 // http://doc.spip.org/@spip_mysql_countsel
731 /**
732
733 * @param array $from
734 * @param array $where
735 * @param string $groupby
736 * @param array $having
737 * @param string $serveur
738 * @param bool $requeter
739 * @return array|int|null|resource|string
740 *
741 */
742 function spip_mysql_countsel($from = array(), $where = array(),
743 $groupby = '', $having = array(), $serveur='',$requeter=true)
744 {
745 $c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
746
747 $r = spip_mysql_select("COUNT($c)", $from, $where,'', '', '', $having, $serveur, $requeter);
748
749 if (!$requeter) return $r;
750 if (!is_resource($r)) return 0;
751 list($c) = mysql_fetch_array($r, MYSQL_NUM);
752 mysql_free_result($r);
753 return $c;
754 }
755
756 // Bien specifier le serveur auquel on s'adresse,
757 // mais a l'install la globale n'est pas encore completement definie
758 // http://doc.spip.org/@spip_mysql_error
759 /**
760 * @param string $query
761 * @param string $serveur
762 * @param bool $requeter
763 * @return string
764 */
765 function spip_mysql_error($query='', $serveur='',$requeter=true) {
766 $link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
767 $s = $link ? mysql_error($link) : mysql_error();
768 if ($s) spip_log("$s - $query", 'mysql.'._LOG_ERREUR);
769 return $s;
770 }
771
772 // A transposer dans les portages
773 // http://doc.spip.org/@spip_mysql_errno
774 /**
775 * @param string $serveur
776 * @param bool $requeter
777 * @return int
778 */
779 function spip_mysql_errno($serveur='',$requeter=true) {
780 $link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
781 $s = $link ? mysql_errno($link) : mysql_errno();
782 // 2006 MySQL server has gone away
783 // 2013 Lost connection to MySQL server during query
784 if (in_array($s, array(2006,2013)))
785 define('spip_interdire_cache', true);
786 if ($s) spip_log("Erreur mysql $s", _LOG_ERREUR);
787 return $s;
788 }
789
790 // Interface de abstract_sql
791 // http://doc.spip.org/@spip_mysql_count
792 /**
793 * @param $r
794 * @param string $serveur
795 * @param bool $requeter
796 * @return int
797 */
798 function spip_mysql_count($r, $serveur='',$requeter=true) {
799 if ($r) return mysql_num_rows($r);
800 }
801
802
803 // http://doc.spip.org/@spip_mysql_free
804 /**
805 * @param $r
806 * @param string $serveur
807 * @param bool $requeter
808 * @return bool
809 */
810 function spip_mysql_free($r, $serveur='',$requeter=true) {
811 return (is_resource($r)?mysql_free_result($r):false);
812 }
813
814 // http://doc.spip.org/@spip_mysql_insert
815 /**
816 * @param $table
817 * @param $champs
818 * @param $valeurs
819 * @param string $desc
820 * @param string $serveur
821 * @param bool $requeter
822 * @return int|string
823 */
824 function spip_mysql_insert($table, $champs, $valeurs, $desc='', $serveur='',$requeter=true) {
825
826 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
827 $prefixe = $connexion['prefixe'];
828 $link = $connexion['link'];
829 $db = $connexion['db'];
830
831 if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
832
833 $query ="INSERT INTO $table $champs VALUES $valeurs";
834 if (!$requeter) return $query;
835
836 if (isset($_GET['var_profile'])) {
837 include_spip('public/tracer');
838 $t = trace_query_start();
839 } else $t = 0 ;
840
841 $connexion['last'] = $query;
842 #spip_log($query, 'mysql.'._LOG_DEBUG);
843 if (mysql_query($query, $link))
844 $r = mysql_insert_id($link);
845 else {
846 if ($e = spip_mysql_errno($serveur)) // Log de l'erreur eventuelle
847 $e .= spip_mysql_error($query, $serveur); // et du fautif
848 }
849 return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
850
851 // return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
852 }
853
854 // http://doc.spip.org/@spip_mysql_insertq
855 /**
856 * @param $table
857 * @param array $couples
858 * @param array $desc
859 * @param string $serveur
860 * @param bool $requeter
861 * @return int|string
862 */
863 function spip_mysql_insertq($table, $couples=array(), $desc=array(), $serveur='',$requeter=true) {
864
865 if (!$desc) $desc = description_table($table, $serveur);
866 if (!$desc) $couples = array();
867 $fields = isset($desc['field'])?$desc['field']:array();
868
869 foreach ($couples as $champ => $val) {
870 $couples[$champ]= spip_mysql_cite($val, $fields[$champ]);
871 }
872
873 return spip_mysql_insert($table, "(".join(',',array_keys($couples)).")", "(".join(',', $couples).")", $desc, $serveur, $requeter);
874 }
875
876
877 // http://doc.spip.org/@spip_mysql_insertq_multi
878 /**
879 * @param $table
880 * @param array $tab_couples
881 * @param array $desc
882 * @param string $serveur
883 * @param bool $requeter
884 * @return bool|int|string
885 */
886 function spip_mysql_insertq_multi($table, $tab_couples=array(), $desc=array(), $serveur='',$requeter=true) {
887
888 if (!$desc) $desc = description_table($table, $serveur);
889 if (!$desc) $tab_couples = array();
890 $fields = isset($desc['field'])?$desc['field']:array();
891
892 $cles = "(" . join(',',array_keys(reset($tab_couples))) . ')';
893 $valeurs = array();
894 $r = false;
895
896 // Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
897 foreach ($tab_couples as $couples) {
898 foreach ($couples as $champ => $val){
899 $couples[$champ]= spip_mysql_cite($val, $fields[$champ]);
900 }
901 $valeurs[] = '(' .join(',', $couples) . ')';
902 if (count($valeurs)>=100){
903 $r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
904 $valeurs = array();
905 }
906 }
907 if (count($valeurs))
908 $r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
909
910 return $r; // dans le cas d'une table auto_increment, le dernier insert_id
911 }
912
913 // http://doc.spip.org/@spip_mysql_update
914 /**
915 * @param $table
916 * @param $champs
917 * @param string $where
918 * @param string $desc
919 * @param string $serveur
920 * @param bool $requeter
921 * @return array|null|resource|string
922 */
923 function spip_mysql_update($table, $champs, $where='', $desc='', $serveur='',$requeter=true) {
924 $set = array();
925 foreach ($champs as $champ => $val)
926 $set[] = $champ . "=$val";
927 if (!empty($set))
928 return spip_mysql_query(
929 calculer_mysql_expression('UPDATE', $table, ',')
930 . calculer_mysql_expression('SET', $set, ',')
931 . calculer_mysql_expression('WHERE', $where),
932 $serveur, $requeter);
933 }
934
935 // idem, mais les valeurs sont des constantes a mettre entre apostrophes
936 // sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
937 // http://doc.spip.org/@spip_mysql_updateq
938 /**
939 * @param $table
940 * @param $champs
941 * @param string $where
942 * @param array $desc
943 * @param string $serveur
944 * @param bool $requeter
945 * @return array|null|resource|string
946 */
947 function spip_mysql_updateq($table, $champs, $where='', $desc=array(), $serveur='',$requeter=true) {
948
949 if (!$champs) return;
950 if (!$desc) $desc = description_table($table, $serveur);
951 if (!$desc) $champs = array(); else $fields = $desc['field'];
952 $set = array();
953 foreach ($champs as $champ => $val) {
954 $set[] = $champ . '=' . spip_mysql_cite($val, $fields[$champ]);
955 }
956 return spip_mysql_query(
957 calculer_mysql_expression('UPDATE', $table, ',')
958 . calculer_mysql_expression('SET', $set, ',')
959 . calculer_mysql_expression('WHERE', $where),
960 $serveur, $requeter);
961 }
962
963 // http://doc.spip.org/@spip_mysql_delete
964 /**
965 * @param $table
966 * @param string $where
967 * @param string $serveur
968 * @param bool $requeter
969 * @return array|bool|int|null|resource|string
970 */
971 function spip_mysql_delete($table, $where='', $serveur='',$requeter=true) {
972 $res = spip_mysql_query(
973 calculer_mysql_expression('DELETE FROM', $table, ',')
974 . calculer_mysql_expression('WHERE', $where),
975 $serveur, $requeter);
976 if (!$requeter) return $res;
977 if ($res){
978 $connexion = &$GLOBALS['connexions'][$serveur ? $serveur : 0];
979 $link = $connexion['link'];
980 return $link ? mysql_affected_rows($link) : mysql_affected_rows();
981 }
982 else
983 return false;
984 }
985
986 // http://doc.spip.org/@spip_mysql_replace
987 /**
988 * @param $table
989 * @param $couples
990 * @param array $desc
991 * @param string $serveur
992 * @param bool $requeter
993 * @return array|null|resource|string
994 */
995 function spip_mysql_replace($table, $couples, $desc=array(), $serveur='',$requeter=true) {
996 return spip_mysql_query("REPLACE $table (" . join(',',array_keys($couples)) . ') VALUES (' .join(',',array_map('_q', $couples)) . ')', $serveur, $requeter);
997 }
998
999
1000 // http://doc.spip.org/@spip_mysql_replace_multi
1001 /**
1002 * @param $table
1003 * @param $tab_couples
1004 * @param array $desc
1005 * @param string $serveur
1006 * @param bool $requeter
1007 * @return array|null|resource|string
1008 */
1009 function spip_mysql_replace_multi($table, $tab_couples, $desc=array(), $serveur='',$requeter=true) {
1010 $cles = "(" . join(',',array_keys($tab_couples[0])). ')';
1011 $valeurs = array();
1012 foreach ($tab_couples as $couples) {
1013 $valeurs[] = '(' .join(',',array_map('_q', $couples)) . ')';
1014 }
1015 $valeurs = implode(', ',$valeurs);
1016 return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
1017 }
1018
1019
1020 // http://doc.spip.org/@spip_mysql_multi
1021 /**
1022
1023 * @param $objet
1024 * @param $lang
1025 * @return string
1026 *
1027 */
1028
1029 function spip_mysql_multi ($objet, $lang) {
1030 $lengthlang = strlen("[$lang]");
1031 $posmulti = "INSTR(".$objet.", '<multi>')";
1032 $posfinmulti = "INSTR(".$objet.", '</multi>')";
1033 $debutchaine = "LEFT(".$objet.", $posmulti-1)";
1034 $finchaine = "RIGHT(".$objet.", CHAR_LENGTH(".$objet.") -(7+$posfinmulti))";
1035 $chainemulti = "TRIM(SUBSTRING(".$objet.", $posmulti+7, $posfinmulti -(7+$posmulti)))";
1036 $poslang = "INSTR($chainemulti,'[".$lang."]')";
1037 $poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
1038 $chainelang = "TRIM(SUBSTRING(".$objet.", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
1039 $posfinlang = "INSTR(".$chainelang.", '[')";
1040 $chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
1041 //$chainelang = "LEFT($chainelang,$posfinlang-1)";
1042 $retour = "(TRIM(IF($posmulti = 0 , ".
1043 " TRIM(".$objet."), ".
1044 " CONCAT( ".
1045 " $debutchaine, ".
1046 " IF( ".
1047 " $poslang = 0, ".
1048 " $chainemulti, ".
1049 " $chainelang".
1050 " ), ".
1051 " $finchaine".
1052 " ) ".
1053 "))) AS multi";
1054
1055 return $retour;
1056 }
1057
1058 // http://doc.spip.org/@spip_mysql_hex
1059 /**
1060 * @param $v
1061 * @return string
1062 */
1063 function spip_mysql_hex($v)
1064 {
1065 return "0x" . $v;
1066 }
1067
1068 /**
1069 * @param $v
1070 * @param string $type
1071 * @return array|int|string
1072 */
1073 function spip_mysql_quote($v, $type='') {
1074 if ($type) {
1075 if (!is_array($v))
1076 return spip_mysql_cite($v,$type);
1077 // si c'est un tableau, le parcourir en propageant le type
1078 foreach($v as $k=>$r)
1079 $v[$k] = spip_mysql_quote($r, $type);
1080 return $v;
1081 }
1082 // si on ne connait pas le type, s'en remettre a _q :
1083 // on ne fera pas mieux
1084 else
1085 return _q($v);
1086 }
1087
1088 /**
1089 * @param $champ
1090 * @param $interval
1091 * @param $unite
1092 * @return string
1093 */
1094 function spip_mysql_date_proche($champ, $interval, $unite)
1095 {
1096 return '('
1097 . $champ
1098 . (($interval <= 0) ? '>' : '<')
1099 . (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1100 . '('
1101 . sql_quote(date('Y-m-d H:i:s'))
1102 . ', INTERVAL '
1103 . (($interval > 0) ? $interval : (0-$interval))
1104 . ' '
1105 . $unite
1106 . '))';
1107 }
1108
1109 //
1110 // IN (...) est limite a 255 elements, d'ou cette fonction assistante
1111 //
1112 // http://doc.spip.org/@spip_mysql_in
1113 /**
1114 * @param $val
1115 * @param $valeurs
1116 * @param string $not
1117 * @param string $serveur
1118 * @param bool $requeter
1119 * @return string
1120 */
1121 function spip_mysql_in($val, $valeurs, $not='', $serveur='',$requeter=true) {
1122 $n = $i = 0;
1123 $in_sql ="";
1124 while ($n = strpos($valeurs, ',', $n+1)) {
1125 if ((++$i) >= 255) {
1126 $in_sql .= "($val $not IN (" .
1127 substr($valeurs, 0, $n) .
1128 "))\n" .
1129 ($not ? "AND\t" : "OR\t");
1130 $valeurs = substr($valeurs, $n+1);
1131 $i = $n = 0;
1132 }
1133 }
1134 $in_sql .= "($val $not IN ($valeurs))";
1135
1136 return "($in_sql)";
1137 }
1138
1139 // pour compatibilite. Ne plus utiliser.
1140 // http://doc.spip.org/@calcul_mysql_in
1141 /**
1142 * @param $val
1143 * @param $valeurs
1144 * @param string $not
1145 * @return string
1146 */
1147 function calcul_mysql_in($val, $valeurs, $not='') {
1148 if (is_array($valeurs))
1149 $valeurs = join(',', array_map('_q', $valeurs));
1150 elseif ($valeurs[0]===',') $valeurs = substr($valeurs,1);
1151 if (!strlen(trim($valeurs))) return ($not ? "0=0" : '0=1');
1152 return spip_mysql_in($val, $valeurs, $not);
1153 }
1154
1155 // http://doc.spip.org/@spip_mysql_cite
1156 /**
1157 * @param $v
1158 * @param $type
1159 * @return int|string
1160 */
1161 function spip_mysql_cite($v, $type) {
1162 if(is_null($v)
1163 AND stripos($type,"NOT NULL")===false) return 'NULL'; // null php se traduit en NULL SQL
1164 if (sql_test_date($type) AND preg_match('/^\w+\(/', $v))
1165 return $v;
1166 if (sql_test_int($type)) {
1167 if (is_numeric($v) OR (ctype_xdigit(substr($v,2))
1168 AND $v[0]=='0' AND $v[1]=='x'))
1169 return $v;
1170 // si pas numerique, forcer le intval
1171 else
1172 return intval($v);
1173 }
1174 return ("'" . addslashes($v) . "'");
1175 }
1176
1177 // Ces deux fonctions n'ont pas d'equivalent exact PostGres
1178 // et ne sont la que pour compatibilite avec les extensions de SPIP < 1.9.3
1179
1180 //
1181 // Poser un verrou local a un SPIP donne
1182 // Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
1183 //
1184 // http://doc.spip.org/@spip_get_lock
1185 /**
1186 * @param $nom
1187 * @param int $timeout
1188 * @return mixed
1189 */
1190 function spip_get_lock($nom, $timeout = 0) {
1191
1192 define('_LOCK_TIME', intval(time()/3600-316982));
1193
1194 $connexion = &$GLOBALS['connexions'][0];
1195 $bd = $connexion['db'];
1196 $prefixe = $connexion['prefixe'];
1197 $nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1198
1199 $connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
1200 $q = @sql_fetch(mysql_query($q));
1201 if (!$q) spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
1202 return $q['n'];
1203 }
1204
1205 // http://doc.spip.org/@spip_release_lock
1206 /**
1207 * @param $nom
1208 */
1209 function spip_release_lock($nom) {
1210
1211 $connexion = &$GLOBALS['connexions'][0];
1212 $bd = $connexion['db'];
1213 $prefixe = $connexion['prefixe'];
1214 $nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1215
1216 $connexion['last'] = $q = "SELECT RELEASE_LOCK(" . _q($nom) . ")";
1217 @mysql_query($q);
1218 }
1219
1220 // Renvoie false si on n'a pas les fonctions mysql (pour l'install)
1221 // http://doc.spip.org/@spip_versions_mysql
1222 /**
1223 * @return bool
1224 */
1225 function spip_versions_mysql() {
1226 charger_php_extension('mysql');
1227 return function_exists('mysql_query');
1228 }
1229
1230 // Tester si mysql ne veut pas du nom de la base dans les requetes
1231
1232 // http://doc.spip.org/@test_rappel_nom_base_mysql
1233 /**
1234 * @param $server_db
1235 * @return string
1236 */
1237 function test_rappel_nom_base_mysql($server_db)
1238 {
1239 $GLOBALS['mysql_rappel_nom_base'] = true;
1240 sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1241 $ok = spip_query("INSERT INTO spip_meta (nom,valeur) VALUES ('mysql_rappel_nom_base', 'test')", $server_db);
1242
1243 if ($ok) {
1244 sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1245 return '';
1246 } else {
1247 $GLOBALS['mysql_rappel_nom_base'] = false;
1248 return "\$GLOBALS['mysql_rappel_nom_base'] = false; ".
1249 "/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
1250 }
1251 }
1252
1253 // http://doc.spip.org/@test_sql_mode_mysql
1254 /**
1255 * @param $server_db
1256 * @return string
1257 */
1258 function test_sql_mode_mysql($server_db){
1259 $res = sql_select("version() as v",'','','','','','',$server_db);
1260 $row = sql_fetch($res,$server_db);
1261 if (version_compare($row['v'],'5.0.0','>=')){
1262 define('_MYSQL_SET_SQL_MODE',true);
1263 return "define('_MYSQL_SET_SQL_MODE',true);\n";
1264 }
1265 return '';
1266 }
1267
1268 ?>