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