[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / req / pg.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 PostgreSQL
16 *
17 * @package SPIP\SQL\PostgreSQL
18 */
19
20 if (!defined('_ECRIRE_INC_VERSION')) return;
21
22 define('_DEFAULT_DB', 'spip');
23
24 // Se connecte et retourne le nom de la fonction a connexion persistante
25 // A la premiere connexion de l'installation (BD pas precisee)
26 // si on ne peut se connecter sans la preciser
27 // on reessaye avec le login comme nom de BD
28 // et si ca marche toujours pas, avec "spip" (constante ci-dessus)
29 // si ca ne marche toujours pas, echec.
30
31 // http://doc.spip.org/@req_pg_dist
32 function req_pg_dist($addr, $port, $login, $pass, $db='', $prefixe='') {
33 static $last_connect = array();
34 if (!charger_php_extension('pgsql')) return false;
35
36 // si provient de selectdb
37 if (empty($addr) && empty($port) && empty($login) && empty($pass)){
38 foreach (array('addr','port','login','pass','prefixe') as $a){
39 $$a = $last_connect[$a];
40 }
41 }
42 @list($host, $p) = explode(';', $addr);
43 if ($p >0) $port = " port=$p" ; else $port = '';
44 $erreurs = array();
45 if ($db) {
46 @$link = pg_connect("host=$host$port dbname=$db user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW);
47 } elseif (!@$link = pg_connect("host=$host$port user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW)) {
48 $erreurs[] = pg_last_error();
49 if (@$link = pg_connect("host=$host$port dbname=$login user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW)) {
50 $db = $login;
51 } else {
52 $erreurs[] = pg_last_error();
53 $db = _DEFAULT_DB;
54 $link = pg_connect("host=$host$port dbname=$db user=$login password='$pass'", PGSQL_CONNECT_FORCE_NEW);
55 }
56 }
57 if (!$link) {
58 $erreurs[] = pg_last_error();
59 foreach($erreurs as $e)
60 spip_log('Echec pg_connect. Erreur : ' . $e,'pg.'._LOG_HS);
61 return false;
62 }
63
64 if ($link)
65 $last_connect = array (
66 'addr' => $addr,
67 'port' => $port,
68 'login' => $login,
69 'pass' => $pass,
70 'db' => $db,
71 'prefixe' => $prefixe,
72 );
73
74 spip_log("Connexion vers $host, base $db, prefixe $prefixe " . ($link ? 'operationnelle' : 'impossible'),'pg.'._LOG_DEBUG);
75
76 return !$link ? false : array(
77 'db' => $db,
78 'prefixe' => $prefixe ? $prefixe : $db,
79 'link' => $link,
80 );
81 }
82
83 $GLOBALS['spip_pg_functions_1'] = array(
84 'alter' => 'spip_pg_alter',
85 'count' => 'spip_pg_count',
86 'countsel' => 'spip_pg_countsel',
87 'create' => 'spip_pg_create',
88 'create_base' => 'spip_pg_create_base',
89 'create_view' => 'spip_pg_create_view',
90 'date_proche' => 'spip_pg_date_proche',
91 'delete' => 'spip_pg_delete',
92 'drop_table' => 'spip_pg_drop_table',
93 'drop_view' => 'spip_pg_drop_view',
94 'errno' => 'spip_pg_errno',
95 'error' => 'spip_pg_error',
96 'explain' => 'spip_pg_explain',
97 'fetch' => 'spip_pg_fetch',
98 'seek' => 'spip_pg_seek',
99 'free' => 'spip_pg_free',
100 'hex' => 'spip_pg_hex',
101 'in' => 'spip_pg_in',
102 'insert' => 'spip_pg_insert',
103 'insertq' => 'spip_pg_insertq',
104 'insertq_multi' => 'spip_pg_insertq_multi',
105 'listdbs' => 'spip_pg_listdbs',
106 'multi' => 'spip_pg_multi',
107 'optimize' => 'spip_pg_optimize',
108 'query' => 'spip_pg_query',
109 'quote' => 'spip_pg_quote',
110 'replace' => 'spip_pg_replace',
111 'replace_multi' => 'spip_pg_replace_multi',
112 'select' => 'spip_pg_select',
113 'selectdb' => 'spip_pg_selectdb',
114 'set_connect_charset' => 'spip_pg_set_connect_charset',
115 'showbase' => 'spip_pg_showbase',
116 'showtable' => 'spip_pg_showtable',
117 'update' => 'spip_pg_update',
118 'updateq' => 'spip_pg_updateq',
119 );
120
121 // Par ou ca passe une fois les traductions faites
122 // http://doc.spip.org/@spip_pg_trace_query
123 function spip_pg_trace_query($query, $serveur='')
124 {
125 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
126 $prefixe = $connexion['prefixe'];
127 $link = $connexion['link'];
128 $db = $connexion['db'];
129
130 if (isset($_GET['var_profile'])) {
131 include_spip('public/tracer');
132 $t = trace_query_start();
133 } else $t = 0 ;
134
135 $connexion['last'] = $query;
136 $r = spip_pg_query_simple($link, $query);
137
138 if ($e = spip_pg_errno($serveur)) // Log de l'erreur eventuelle
139 $e .= spip_pg_error($query, $serveur); // et du fautif
140 return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
141 }
142
143 // Fonction de requete generale quand on est sur que c'est SQL standard.
144 // Elle change juste le noms des tables ($table_prefix) dans le FROM etc
145
146 // http://doc.spip.org/@spip_pg_query
147 function spip_pg_query($query, $serveur='',$requeter=true)
148 {
149 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
150 $prefixe = $connexion['prefixe'];
151 $link = $connexion['link'];
152 $db = $connexion['db'];
153
154 if (preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
155 $suite = strstr($query, $regs[0]);
156 $query = substr($query, 0, -strlen($suite));
157 } else $suite ='';
158 $query = preg_replace('/([,\s])spip_/', '\1'.$prefixe.'_', $query) . $suite;
159
160 // renvoyer la requete inerte si demandee
161 if (!$requeter) return $query;
162
163 return spip_pg_trace_query($query, $serveur);
164 }
165
166 function spip_pg_query_simple($link, $query){
167 #spip_log(var_export($query,true), 'pg.'._LOG_DEBUG);
168 return pg_query($link, $query);
169 }
170
171 /*
172 * Retrouver les champs 'timestamp'
173 * pour les ajouter aux 'insert' ou 'replace'
174 * afin de simuler le fonctionnement de mysql
175 *
176 * stocke le resultat pour ne pas faire
177 * de requetes showtable intempestives
178 */
179 function spip_pg_ajouter_champs_timestamp($table, $couples, $desc='', $serveur=''){
180 static $tables = array();
181
182 if (!isset($tables[$table])){
183
184 if (!$desc){
185 $trouver_table = charger_fonction('trouver_table', 'base');
186 $desc = $trouver_table($table, $serveur);
187 // si pas de description, on ne fait rien, ou on die() ?
188 if (!$desc) return $couples;
189 }
190
191 // recherche des champs avec simplement 'TIMESTAMP'
192 // cependant, il faudra peut etre etendre
193 // avec la gestion de DEFAULT et ON UPDATE
194 // mais ceux-ci ne sont pas utilises dans le core
195 $tables[$table] = array();
196 foreach ($desc['field'] as $k=>$v){
197 $v = strtolower(ltrim($v));
198 // ne pas ajouter de timestamp now() si un default est specifie
199 if (strpos($v, 'timestamp')===0 AND strpos($v, 'default')===false)
200 $tables[$table][] = $k;
201 }
202 }
203
204 // ajout des champs type 'timestamp' absents
205 foreach ($tables[$table] as $maj){
206 if (!array_key_exists($maj, $couples))
207 $couples[$maj] = "NOW()";
208 }
209 return $couples;
210 }
211
212
213 // Alter en PG ne traite pas les index
214 // http://doc.spip.org/@spip_pg_alter
215 function spip_pg_alter($query, $serveur='',$requeter=true) {
216 // il faudrait une regexp pour eviter de spliter ADD PRIMARY KEY (colA, colB)
217 // tout en cassant en deux alter distincts "ADD PRIMARY KEY (colA, colB), ADD INDEX (chose)"...
218 // ou revoir l'api de sql_alter en creant un
219 // sql_alter_table($table,array($actions));
220 if (!preg_match("/\s*((\s*IGNORE)?\s*TABLE\s*([^\s]*))\s*(.*)?/is", $query, $regs)){
221 spip_log("$query mal comprise", 'pg.'._LOG_ERREUR);
222 return false;
223 }
224 $debut = $regs[1];
225 $table = $regs[3];
226 $suite = $regs[4];
227 $todo = explode(',', $suite);
228 // on remet les morceaux dechires ensembles... que c'est laid !
229 $todo2 = array(); $i=0;
230 $ouverte = false;
231 while ($do = array_shift($todo)) {
232 $todo2[$i] = isset($todo2[$i]) ? $todo2[$i] . "," . $do : $do;
233 $o=(false!==strpos($do,"("));
234 $f=(false!==strpos($do,")"));
235 if ($o AND !$f) $ouverte=true;
236 elseif ($f) $ouverte=false;
237 if (!$ouverte) $i++;
238 }
239 $todo=$todo2;
240 $query = $debut.' '.array_shift($todo);
241
242 if (!preg_match('/^\s*(IGNORE\s*)?TABLE\s+(\w+)\s+(ADD|DROP|CHANGE|MODIFY|RENAME)\s*(.*)$/is', $query, $r)) {
243 spip_log("$query incompris", 'pg.'._LOG_ERREUR);
244 } else {
245 if ($r[1]) spip_log("j'ignore IGNORE dans $query", 'pg.'._LOG_AVERTISSEMENT);
246 $f = 'spip_pg_alter_' . strtolower($r[3]);
247 if (function_exists($f))
248 $f($r[2], $r[4], $serveur, $requeter);
249 else spip_log("$query non prevu", 'pg.'._LOG_ERREUR);
250 }
251 // Alter a plusieurs args. Faudrait optimiser.
252 if ($todo)
253 spip_pg_alter("TABLE $table " . join(',',$todo));
254
255 }
256
257 // http://doc.spip.org/@spip_pg_alter_change
258 function spip_pg_alter_change($table, $arg, $serveur='',$requeter=true)
259 {
260 if (!preg_match('/^`?(\w+)`?\s+`?(\w+)`?\s+(.*?)\s*(DEFAULT .*?)?(NOT\s+NULL)?\s*(DEFAULT .*?)?$/i',$arg, $r)) {
261 spip_log("alter change: $arg incompris", 'pg.'._LOG_ERREUR);
262 } else {
263 list(,$old, $new, $type, $default, $null, $def2) = $r;
264 $actions = array("ALTER $old TYPE " . mysql2pg_type($type));
265 if ($null)
266 $actions[]= "ALTER $old SET NOT NULL";
267 else
268 $actions[]= "ALTER $old DROP NOT NULL";
269
270 if ($d = ($default ? $default : $def2))
271 $actions[]= "ALTER $old SET $d";
272 else
273 $actions[]= "ALTER $old DROP DEFAULT";
274
275 spip_pg_query("ALTER TABLE $table " . join(', ', $actions));
276
277 if ($old != $new)
278 spip_pg_query("ALTER TABLE $table RENAME $old TO $new", $serveur);
279 }
280 }
281
282 // http://doc.spip.org/@spip_pg_alter_add
283 function spip_pg_alter_add($table, $arg, $serveur='',$requeter=true) {
284 if (!preg_match('/^(COLUMN|INDEX|KEY|PRIMARY\s+KEY|)\s*(.*)$/', $arg, $r)) {
285 spip_log("alter add $arg incompris", 'pg.'._LOG_ERREUR);
286 return NULL;
287 }
288 if (!$r[1] OR $r[1]=='COLUMN') {
289 preg_match('/`?(\w+)`?(.*)/',$r[2], $m);
290 if (preg_match('/^(.*)(BEFORE|AFTER|FIRST)(.*)$/is', $m[2], $n)) {
291 $m[2]=$n[1];
292 }
293 return spip_pg_query("ALTER TABLE $table ADD " . $m[1] . ' ' . mysql2pg_type($m[2]), $serveur, $requeter);
294 } elseif ($r[1][0] == 'P') {
295 // la primary peut etre sur plusieurs champs
296 $r[2] = trim(str_replace('`','',$r[2]));
297 $m = ($r[2][0]=='(') ? substr($r[2],1,-1) : $r[2];
298 return spip_pg_query("ALTER TABLE $table ADD CONSTRAINT $table" .'_pkey PRIMARY KEY (' . $m . ')', $serveur, $requeter);
299 } else {
300 preg_match('/([^\s,]*)\s*(.*)?/',$r[2], $m);
301 // peut etre "(colonne)" ou "nom_index (colonnes)"
302 // bug potentiel si qqn met "(colonne, colonne)"
303 //
304 // nom_index (colonnes)
305 if ($m[2]) {
306 $colonnes = substr($m[2],1,-1);
307 $nom_index = $m[1];
308 }
309 else {
310 // (colonne)
311 if ($m[1][0] == "(") {
312 $colonnes = substr($m[1],1,-1);
313 if (false!==strpos(",",$colonnes)) {
314 spip_log(_LOG_GRAVITE_ERREUR,"PG : Erreur, impossible de creer un index sur plusieurs colonnes"
315 ." sans qu'il ait de nom ($table, ($colonnes))", 'pg');
316 } else {
317 $nom_index = $colonnes;
318 }
319 }
320 // nom_index
321 else {
322 $nom_index = $colonnes = $m[1];
323 }
324 }
325 return spip_pg_create_index($nom_index, $table, $colonnes, $serveur, $requeter);
326 }
327 }
328
329 // http://doc.spip.org/@spip_pg_alter_drop
330 function spip_pg_alter_drop($table, $arg, $serveur='',$requeter=true) {
331 if (!preg_match('/^(COLUMN|INDEX|KEY|PRIMARY\s+KEY|)\s*`?(\w*)`?/', $arg, $r))
332 spip_log("alter drop: $arg incompris", 'pg.'._LOG_ERREUR);
333 else {
334 if (!$r[1] OR $r[1]=='COLUMN')
335 return spip_pg_query("ALTER TABLE $table DROP " . $r[2], $serveur);
336 elseif ($r[1][0] == 'P')
337 return spip_pg_query("ALTER TABLE $table DROP CONSTRAINT $table" . '_pkey', $serveur);
338 else {
339 return spip_pg_query("DROP INDEX " . $table . '_' . $r[2], $serveur);
340 }
341 }
342 }
343
344 function spip_pg_alter_modify($table, $arg, $serveur='',$requeter=true) {
345 if (!preg_match('/^`?(\w+)`?\s+(.*)$/',$arg, $r)) {
346 spip_log("alter modify: $arg incompris", 'pg.'._LOG_ERREUR);
347 } else {
348 return spip_pg_alter_change($table, $r[1].' '.$arg, $serveur='',$requeter=true);
349 }
350 }
351
352 // attention (en pg) :
353 // - alter table A rename to X = changer le nom de la table
354 // - alter table A rename X to Y = changer le nom de la colonne X en Y
355 // pour l'instant, traiter simplement RENAME TO X
356 function spip_pg_alter_rename($table, $arg, $serveur='',$requeter=true) {
357 $rename="";
358 // si TO, mais pas au debut
359 if (!stripos($arg,'TO ')){
360 $rename=$arg;
361 }
362 elseif (preg_match('/^(TO)\s*`?(\w*)`?/', $arg, $r)) {
363 $rename=$r[2];
364 } else {
365 spip_log("alter rename: $arg incompris", 'pg.'._LOG_ERREUR);
366 }
367 return $rename?spip_pg_query("ALTER TABLE $table RENAME TO $rename"):false;
368 }
369
370
371 /**
372 * Fonction de creation d'un INDEX
373 *
374 * @param string $nom : nom de l'index
375 * @param string $table : table sql de l'index
376 * @param string/array $champs : liste de champs sur lesquels s'applique l'index
377 * @param string $serveur : nom de la connexion sql utilisee
378 * @param bool $requeter : true pour executer la requete ou false pour retourner le texte de la requete
379 *
380 * @return bool ou requete
381 */
382 function spip_pg_create_index($nom, $table, $champs, $serveur='', $requeter=true) {
383 if (!($nom OR $table OR $champs)) {
384 spip_log("Champ manquant pour creer un index pg ($nom, $table, (".@join(',',$champs)."))",'pg.'._LOG_ERREUR);
385 return false;
386 }
387
388 $nom = str_replace("`","",$nom);
389 $champs = str_replace("`","",$champs);
390
391 // PG ne differentie pas noms des index en fonction des tables
392 // il faut donc creer des noms uniques d'index pour une base pg
393 $nom = $table.'_'.$nom;
394 // enlever d'eventuelles parentheses deja presentes sur champs
395 if (!is_array($champs)){
396 if ($champs[0]=="(") $champs = substr($champs,1,-1);
397 $champs = array($champs);
398 }
399 $query = "CREATE INDEX $nom ON $table (" . join(',',$champs) . ")";
400 if (!$requeter) return $query;
401 $res = spip_pg_query($query, $serveur, $requeter);
402 return $res;
403 }
404
405
406 // http://doc.spip.org/@spip_pg_explain
407 function spip_pg_explain($query, $serveur='',$requeter=true){
408 if (strpos(ltrim($query), 'SELECT') !== 0) return array();
409 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
410 $prefixe = $connexion['prefixe'];
411 $link = $connexion['link'];
412 if (preg_match('/\s(SET|VALUES|WHERE)\s/i', $query, $regs)) {
413 $suite = strstr($query, $regs[0]);
414 $query = substr($query, 0, -strlen($suite));
415 } else $suite ='';
416 $query = 'EXPLAIN ' . preg_replace('/([,\s])spip_/', '\1'.$prefixe.'_', $query) . $suite;
417
418 if (!$requeter) return $query;
419 $r = spip_pg_query_simple($link,$query);
420 return spip_pg_fetch($r, NULL, $serveur);
421 }
422
423
424 /**
425 * Selectionne une base de donnees
426 *
427 * @param string $nom
428 * Nom de la base a utiliser
429 * @param string $serveur
430 * Nom du connecteur
431 * @param bool $requeter
432 * Inutilise
433 *
434 * @return bool|string
435 * Nom de la base en cas de success.
436 * False en cas d'erreur.
437 **/
438 function spip_pg_selectdb($db, $serveur='',$requeter=true) {
439 // se connecter a la base indiquee
440 // avec les identifiants connus
441 $index = $serveur ? strtolower($serveur) : 0;
442
443 if ($link = spip_connect_db('', '', '', '', $db, 'pg', '', '')){
444 if (($db==$link['db']) && $GLOBALS['connexions'][$index] = $link)
445 return $db;
446 } else
447 return false;
448 }
449
450 // Qu'une seule base pour le moment
451
452 // http://doc.spip.org/@spip_pg_listdbs
453 function spip_pg_listdbs($serveur) {
454 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
455 $link = $connexion['link'];
456 $dbs = array();
457 $res = spip_pg_query_simple($link, "select * From pg_database");
458 while ($row = pg_fetch_array($res, NULL, PGSQL_NUM))
459 $dbs[] = reset($row);
460
461 return $dbs;
462 }
463
464 // http://doc.spip.org/@spip_pg_select
465 function spip_pg_select($select, $from, $where='',
466 $groupby=array(), $orderby='', $limit='',
467 $having='', $serveur='',$requeter=true){
468
469 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
470 $prefixe = $connexion['prefixe'];
471 $link = $connexion['link'];
472 $db = $connexion['db'];
473
474 $limit = preg_match("/^\s*(([0-9]+),)?\s*([0-9]+)\s*$/", $limit,$limatch);
475 if ($limit) {
476 $offset = $limatch[2];
477 $count = $limatch[3];
478 }
479
480 $select = spip_pg_frommysql($select);
481
482 // si pas de tri explicitement demande, le GROUP BY ne
483 // contient que la clef primaire.
484 // lui ajouter alors le champ de tri par defaut
485 if (preg_match("/FIELD\(([a-z]+\.[a-z]+),/i", $orderby[0], $groupbyplus)) {
486 $groupby[] = $groupbyplus[1];
487 }
488
489 $orderby = spip_pg_orderby($orderby, $select);
490
491 if ($having) {
492 if (is_array($having))
493 $having = join("\n\tAND ", array_map('calculer_pg_where', $having));
494 }
495 $from = spip_pg_from($from, $prefixe);
496 $query = "SELECT ". $select
497 . (!$from ? '' : "\nFROM $from")
498 . (!$where ? '' : ("\nWHERE " . (!is_array($where) ? calculer_pg_where($where) : (join("\n\tAND ", array_map('calculer_pg_where', $where))))))
499 . spip_pg_groupby($groupby, $from, $select)
500 . (!$having ? '' : "\nHAVING $having")
501 . ($orderby ? ("\nORDER BY $orderby") :'')
502 . (!$limit ? '' : (" LIMIT $count" . (!$offset ? '' : " OFFSET $offset")));
503
504 // renvoyer la requete inerte si demandee
505 if ($requeter === false) return $query;
506
507 $r = spip_pg_trace_query($query, $serveur);
508 return $r ? $r : $query;;
509 }
510
511 // Le traitement des prefixes de table dans un Select se limite au FROM
512 // car le reste de la requete utilise les alias (AS) systematiquement
513
514 // http://doc.spip.org/@spip_pg_from
515 function spip_pg_from($from, $prefixe)
516 {
517 if (is_array($from)) $from = spip_pg_select_as($from);
518 return !$prefixe ? $from : preg_replace('/(\b)spip_/','\1'.$prefixe.'_', $from);
519 }
520
521 // http://doc.spip.org/@spip_pg_orderby
522 function spip_pg_orderby($order, $select)
523 {
524 $res = array();
525 $arg = (is_array($order) ? $order : preg_split('/\s*,\s*/',$order));
526
527 foreach($arg as $v) {
528 if (preg_match('/(case\s+.*?else\s+0\s+end)\s*AS\s+' . $v .'/', $select, $m)) {
529
530 $res[] = $m[1];
531 } else $res[]=$v;
532 }
533 return spip_pg_frommysql(join(',',$res));
534 }
535
536 // Conversion a l'arrach' des jointures MySQL en jointures PG
537 // A refaire pour tirer parti des possibilites de PG et de MySQL5
538 // et pour enlever les repetitions (sans incidence de perf, mais ca fait sale)
539
540 // http://doc.spip.org/@spip_pg_groupby
541 function spip_pg_groupby($groupby, $from, $select)
542 {
543 $join = strpos($from, ",");
544 // ismplifier avant de decouper
545 if (is_string($select))
546 // fct SQL sur colonne et constante apostrophee ==> la colonne
547 $select = preg_replace('/\w+\(\s*([^(),\']*),\s*\'[^\']*\'[^)]*\)/','\\1', $select);
548
549 if ($join OR $groupby) $join = is_array($select) ? $select : explode(", ", $select);
550 if ($join) {
551 // enlever les 0 as points, '', ...
552 foreach($join as $k=>$v){
553 $v = str_replace('DISTINCT ','',$v);
554 // fct SQL sur colonne et constante apostrophee ==> la colonne
555 $v = preg_replace('/\w+\(\s*([^(),\']*),\s*\'[^\']*\'[^)]*\)/','\\1', $v);
556 $v = preg_replace('/CAST\(\s*([^(),\' ]*\s+)as\s*\w+\)/','\\1', $v);
557 // resultat d'agregat ne sont pas a mettre dans le groupby
558 $v = preg_replace('/(SUM|COUNT|MAX|MIN|UPPER)\([^)]+\)(\s*AS\s+\w+)\s*,?/i','', $v);
559 // idem sans AS (fetch numerique)
560 $v = preg_replace('/(SUM|COUNT|MAX|MIN|UPPER)\([^)]+\)\s*,?/i','', $v);
561 // des AS simples : on garde le cote droit du AS
562 $v = preg_replace('/^.*\sAS\s+(\w+)\s*$/i','\\1', $v);
563 // ne reste plus que les vrais colonnes, ou des constantes a virer
564 if (preg_match(',^[\'"],',$v) OR is_numeric($v))
565 unset($join[$k]);
566 else
567 $join[$k] = trim($v);
568 }
569 $join = array_diff($join,array(''));
570 $join = implode(',',$join);
571 }
572 if (is_array($groupby)) $groupby = join(',',$groupby);
573 if ($join) $groupby = $groupby ? "$groupby, $join" : $join;
574 if (!$groupby) return '';
575
576 $groupby = spip_pg_frommysql($groupby);
577 // Ne pas mettre dans le Group-By des valeurs numeriques
578 // issue de prepare_recherche
579 $groupby = preg_replace('/^\s*\d+\s+AS\s+\w+\s*,?\s*/i','', $groupby);
580 $groupby = preg_replace('/,\s*\d+\s+AS\s+\w+\s*/i','', $groupby);
581 $groupby = preg_replace('/\s+AS\s+\w+\s*/i','', $groupby);
582
583 return "\nGROUP BY $groupby";
584 }
585
586 // Conversion des operateurs MySQL en PG
587 // IMPORTANT: "0+X" est vu comme conversion numerique du debut de X
588 // Les expressions de date ne sont pas gerees au-dela de 3 ()
589 // Le 'as' du 'CAST' est en minuscule pour echapper au dernier preg_replace
590 // de spip_pg_groupby.
591 // A ameliorer.
592
593 // http://doc.spip.org/@spip_pg_frommysql
594 function spip_pg_frommysql($arg)
595 {
596 if (is_array($arg)) $arg = join(", ", $arg);
597
598 $res = spip_pg_fromfield($arg);
599
600 $res = preg_replace('/\brand[(][)]/i','random()', $res);
601
602 $res = preg_replace('/\b0\.0[+]([a-zA-Z0-9_.]+)\s*/',
603 'CAST(substring(\1, \'^ *[0-9.]+\') as float)',
604 $res);
605 $res = preg_replace('/\b0[+]([a-zA-Z0-9_.]+)\s*/',
606 'CAST(substring(\1, \'^ *[0-9]+\') as int)',
607 $res);
608 $res = preg_replace('/\bconv[(]([^,]*)[^)]*[)]/i',
609 'CAST(substring(\1, \'^ *[0-9]+\') as int)',
610 $res);
611
612 $res = preg_replace('/UNIX_TIMESTAMP\s*[(]\s*[)]/',
613 ' EXTRACT(epoch FROM NOW())', $res);
614
615 // la fonction md5(integer) n'est pas connu en pg
616 // il faut donc forcer les types en text (cas de md5(id_article))
617 $res = preg_replace('/md5\s*[(]([^)]*)[)]/i',
618 'MD5(CAST(\1 AS text))', $res);
619
620 $res = preg_replace('/UNIX_TIMESTAMP\s*[(]([^)]*)[)]/',
621 ' EXTRACT(epoch FROM \1)', $res);
622
623 $res = preg_replace('/\bDAYOFMONTH\s*[(]([^()]*([(][^()]*[)][^()]*)*[^)]*)[)]/',
624 ' EXTRACT(day FROM \1)',
625 $res);
626
627 $res = preg_replace('/\bMONTH\s*[(]([^()]*([(][^)]*[)][^()]*)*[^)]*)[)]/',
628 ' EXTRACT(month FROM \1)',
629 $res);
630
631 $res = preg_replace('/\bYEAR\s*[(]([^()]*([(][^)]*[)][^()]*)*[^)]*)[)]/',
632 ' EXTRACT(year FROM \1)',
633 $res);
634
635 $res = preg_replace('/TO_DAYS\s*[(]([^()]*([(][^)]*[)][()]*)*)[)]/',
636 ' EXTRACT(day FROM \1 - \'0001-01-01\')',
637 $res);
638
639 $res = preg_replace("/(EXTRACT[(][^ ]* FROM *)\"([^\"]*)\"/", '\1\'\2\'', $res);
640
641 $res = preg_replace('/DATE_FORMAT\s*[(]([^,]*),\s*\'%Y%m%d\'[)]/', 'to_char(\1, \'YYYYMMDD\')', $res);
642
643 $res = preg_replace('/DATE_FORMAT\s*[(]([^,]*),\s*\'%Y%m\'[)]/', 'to_char(\1, \'YYYYMM\')', $res);
644
645 $res = preg_replace('/DATE_SUB\s*[(]([^,]*),/', '(\1 -', $res);
646 $res = preg_replace('/DATE_ADD\s*[(]([^,]*),/', '(\1 +', $res);
647 $res = preg_replace('/INTERVAL\s+(\d+\s+\w+)/', 'INTERVAL \'\1\'', $res);
648 $res = preg_replace('/([+<>-]=?)\s*(\'\d+-\d+-\d+\s+\d+:\d+(:\d+)\')/', '\1 timestamp \2', $res);
649 $res = preg_replace('/(\'\d+-\d+-\d+\s+\d+:\d+:\d+\')\s*([+<>-]=?)/', 'timestamp \1 \2', $res);
650
651 $res = preg_replace('/([+<>-]=?)\s*(\'\d+-\d+-\d+\')/', '\1 timestamp \2', $res);
652 $res = preg_replace('/(\'\d+-\d+-\d+\')\s*([+<>-]=?)/', 'timestamp \1 \2', $res);
653
654 $res = preg_replace('/(timestamp .\d+)-00-/','\1-01-', $res);
655 $res = preg_replace('/(timestamp .\d+-\d+)-00/','\1-01',$res);
656 # correct en theorie mais produit des debordements arithmetiques
657 # $res = preg_replace("/(EXTRACT[(][^ ]* FROM *)(timestamp *'[^']*' *[+-] *timestamp *'[^']*') *[)]/", '\2', $res);
658 $res = preg_replace("/(EXTRACT[(][^ ]* FROM *)('[^']*')/", '\1 timestamp \2', $res);
659 $res = preg_replace("/\sLIKE\s+/", ' ILIKE ', $res);
660 return str_replace('REGEXP', '~', $res);
661 }
662
663 // http://doc.spip.org/@spip_pg_fromfield
664 function spip_pg_fromfield($arg)
665 {
666 while(preg_match('/^(.*?)FIELD\s*\(([^,]*)((,[^,)]*)*)\)/', $arg, $m)) {
667
668 preg_match_all('/,([^,]*)/', $m[3], $r, PREG_PATTERN_ORDER);
669 $res = '';
670 $n=0;
671 $index = $m[2];
672 foreach($r[1] as $v) {
673 $n++;
674 $res .= "\nwhen $index=$v then $n";
675 }
676 $arg = $m[1] . "case $res else 0 end "
677 . substr($arg,strlen($m[0]));
678 }
679 return $arg;
680 }
681
682 // http://doc.spip.org/@calculer_pg_where
683 function calculer_pg_where($v)
684 {
685 if (!is_array($v))
686 return spip_pg_frommysql($v);
687
688 $op = str_replace('REGEXP', '~', array_shift($v));
689 if (!($n=count($v)))
690 return $op;
691 else {
692 $arg = calculer_pg_where(array_shift($v));
693 if ($n==1) {
694 return "$op($arg)";
695 } else {
696 $arg2 = calculer_pg_where(array_shift($v));
697 if ($n==2) {
698 return "($arg $op $arg2)";
699 } else return "($arg $op ($arg2) : $v[0])";
700 }
701 }
702 }
703
704
705 // http://doc.spip.org/@calculer_pg_expression
706 function calculer_pg_expression($expression, $v, $join = 'AND'){
707 if (empty($v))
708 return '';
709
710 $exp = "\n$expression ";
711
712 if (!is_array($v)) $v = array($v);
713
714 if (strtoupper($join) === 'AND')
715 return $exp . join("\n\t$join ", array_map('calculer_pg_where', $v));
716 else
717 return $exp . join($join, $v);
718 }
719
720 // http://doc.spip.org/@spip_pg_select_as
721 function spip_pg_select_as($args)
722 {
723 $argsas = "";
724 foreach($args as $k => $v) {
725 if (substr($k,-1)=='@') {
726 // c'est une jointure qui se refere au from precedent
727 // pas de virgule
728 $argsas .= ' ' . $v ;
729 }
730 else {
731 $as = '';
732 // spip_log("$k : $v", _LOG_DEBUG);
733 if (!is_numeric($k)) {
734 if (preg_match('/\.(.*)$/', $k, $r))
735 $v = $k;
736 elseif ($v != $k) {
737 $p = strpos($v, " ");
738 if ($p)
739 $v = substr($v,0,$p) . " AS $k" . substr($v,$p);
740 else $as = " AS $k";
741 }
742 }
743 // spip_log("subs $k : $v avec $as", _LOG_DEBUG);
744 // if (strpos($v, 'JOIN') === false) $argsas .= ', ';
745 $argsas .= ', '. $v . $as;
746 }
747 }
748 return substr($argsas,2);
749 }
750
751 // http://doc.spip.org/@spip_pg_fetch
752 function spip_pg_fetch($res, $t='', $serveur='',$requeter=true) {
753
754 if ($res) $res = pg_fetch_array($res, NULL, PGSQL_ASSOC);
755 return $res;
756 }
757
758 function spip_pg_seek($r, $row_number, $serveur='',$requeter=true) {
759 if ($r) return pg_result_seek($r,$row_number);
760 }
761
762
763 // http://doc.spip.org/@spip_pg_countsel
764 function spip_pg_countsel($from = array(), $where = array(), $groupby=array(),
765 $having = array(), $serveur='',$requeter=true)
766 {
767 $c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
768 $r = spip_pg_select("COUNT($c)", $from, $where,'', '', '', $having, $serveur, $requeter);
769 if (!$requeter) return $r;
770 if (!is_resource($r)) return 0;
771 list($c) = pg_fetch_array($r, NULL, PGSQL_NUM);
772 return $c;
773 }
774
775 // http://doc.spip.org/@spip_pg_count
776 function spip_pg_count($res, $serveur='',$requeter=true) {
777 return !$res ? 0 : pg_numrows($res);
778 }
779
780 // http://doc.spip.org/@spip_pg_free
781 function spip_pg_free($res, $serveur='',$requeter=true) {
782 // rien a faire en postgres
783 }
784
785 // http://doc.spip.org/@spip_pg_delete
786 function spip_pg_delete($table, $where='', $serveur='',$requeter=true) {
787
788 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
789 $prefixe = $connexion['prefixe'];
790 $link = $connexion['link'];
791 $db = $connexion['db'];
792 if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
793
794 $query = calculer_pg_expression('DELETE FROM', $table, ',')
795 . calculer_pg_expression('WHERE', $where, 'AND');
796
797 // renvoyer la requete inerte si demandee
798 if (!$requeter) return $query;
799
800 $res = spip_pg_trace_query($query, $serveur);
801 if ($res)
802 return pg_affected_rows($res);
803 else
804 return false;
805 }
806
807 // http://doc.spip.org/@spip_pg_insert
808 function spip_pg_insert($table, $champs, $valeurs, $desc=array(), $serveur='',$requeter=true) {
809 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
810 $prefixe = $connexion['prefixe'];
811 $link = $connexion['link'];
812 $db = $connexion['db'];
813
814 if (!$desc) $desc = description_table($table, $serveur);
815 $seq = spip_pg_sequence($table,true);
816 // si pas de cle primaire dans l'insertion, renvoyer curval
817 if (!preg_match(",\b$seq\b,",$champs)){
818 $seq = spip_pg_sequence($table);
819 if ($prefixe)
820 $seq = preg_replace('/^spip/', $prefixe, $seq);
821 $seq = "currval('$seq')";
822 }
823
824
825 if ($prefixe) {
826 $table = preg_replace('/^spip/', $prefixe, $table);
827 }
828 $ret = !$seq ? '' : (" RETURNING $seq");
829 $ins = (strlen($champs)<3)
830 ? " DEFAULT VALUES"
831 : "$champs VALUES $valeurs";
832 $q ="INSERT INTO $table $ins $ret";
833 if (!$requeter) return $q;
834 $connexion['last'] = $q;
835 $r = spip_pg_query_simple($link, $q);
836 # spip_log($q,'pg.'._LOG_DEBUG);
837 if ($r) {
838 if (!$ret) return 0;
839 if ($r2 = pg_fetch_array($r, NULL, PGSQL_NUM))
840 return $r2[0];
841 }
842 return false;
843 }
844
845 // http://doc.spip.org/@spip_pg_insertq
846 function spip_pg_insertq($table, $couples=array(), $desc=array(), $serveur='',$requeter=true) {
847
848 if (!$desc) $desc = description_table($table, $serveur);
849 if (!$desc) die("$table insertion sans description");
850 $fields = $desc['field'];
851
852 foreach ($couples as $champ => $val) {
853 $couples[$champ]= spip_pg_cite($val, $fields[$champ]);
854 }
855
856 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
857 $couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
858
859 return spip_pg_insert($table, "(".join(',',array_keys($couples)).")", "(".join(',', $couples).")", $desc, $serveur, $requeter);
860 }
861
862
863
864 // http://doc.spip.org/@spip_pg_insertq_multi
865 function spip_pg_insertq_multi($table, $tab_couples=array(), $desc=array(), $serveur='',$requeter=true) {
866
867 if (!$desc) $desc = description_table($table, $serveur);
868 if (!$desc) die("$table insertion sans description");
869 $fields = isset($desc['field'])?$desc['field']:array();
870
871 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
872 // une premiere fois pour ajouter maj dans les cles
873 $c = isset($tab_couples[0]) ? $tab_couples[0] : array();
874 $les_cles = spip_pg_ajouter_champs_timestamp($table, $c, $desc, $serveur);
875
876 $cles = "(" . join(',',array_keys($les_cles)). ')';
877 $valeurs = array();
878 foreach ($tab_couples as $couples) {
879 foreach ($couples as $champ => $val){
880 $couples[$champ]= spip_pg_cite($val, $fields[$champ]);
881 }
882 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
883 $couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
884
885 $valeurs[] = '(' .join(',', $couples) . ')';
886 }
887 $valeurs = implode(', ',$valeurs);
888
889 return spip_pg_insert($table, $cles, $valeurs, $desc, $serveur, $requeter);
890 }
891
892
893 // http://doc.spip.org/@spip_pg_update
894 function spip_pg_update($table, $couples, $where='', $desc='', $serveur='',$requeter=true) {
895
896 if (!$couples) return;
897 $connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
898 $prefixe = $connexion['prefixe'];
899 $link = $connexion['link'];
900 $db = $connexion['db'];
901 if ($prefixe) $table = preg_replace('/^spip/', $prefixe, $table);
902
903 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
904 $couples = spip_pg_ajouter_champs_timestamp($table, $couples, $desc, $serveur);
905
906 $set = array();
907 foreach ($couples as $champ => $val) {
908 $set[] = $champ . '=' . $val;
909 }
910
911 $query = calculer_pg_expression('UPDATE', $table, ',')
912 . calculer_pg_expression('SET', $set, ',')
913 . calculer_pg_expression('WHERE', $where, 'AND');
914
915 // renvoyer la requete inerte si demandee
916 if (!$requeter) return $query;
917
918 return spip_pg_trace_query($query, $serveur);
919 }
920
921 // idem, mais les valeurs sont des constantes a mettre entre apostrophes
922 // sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
923 // http://doc.spip.org/@spip_pg_updateq
924 function spip_pg_updateq($table, $couples, $where='', $desc=array(), $serveur='',$requeter=true) {
925 if (!$couples) return;
926 if (!$desc) $desc = description_table($table, $serveur);
927 $fields = $desc['field'];
928 foreach ($couples as $k => $val) {
929 $couples[$k] = spip_pg_cite($val, $fields[$k]);
930 }
931
932 return spip_pg_update($table, $couples, $where, $desc, $serveur, $requeter);
933 }
934
935
936 // http://doc.spip.org/@spip_pg_replace
937 function spip_pg_replace($table, $values, $desc, $serveur='',$requeter=true) {
938 if (!$values) {spip_log("replace vide $table",'pg.'._LOG_AVERTISSEMENT); return 0;}
939 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
940 $prefixe = $connexion['prefixe'];
941 $link = $connexion['link'];
942 $db = $connexion['db'];
943
944 if (!$desc) $desc = description_table($table, $serveur);
945 if (!$desc) die("$table insertion sans description");
946 $prim = $desc['key']['PRIMARY KEY'];
947 $ids = preg_split('/,\s*/', $prim);
948 $noprims = $prims = array();
949 foreach($values as $k=>$v) {
950 $values[$k] = $v = spip_pg_cite($v, $desc['field'][$k]);
951
952 if (!in_array($k, $ids))
953 $noprims[$k]= "$k=$v";
954 else $prims[$k]= "$k=$v";
955 }
956
957 // recherche de champs 'timestamp' pour mise a jour auto de ceux-ci
958 $values = spip_pg_ajouter_champs_timestamp($table, $values, $desc, $serveur);
959
960 $where = join(' AND ', $prims);
961 if (!$where) {
962 return spip_pg_insert($table, "(".join(',',array_keys($values)).")", "(".join(',', $values).")", $desc, $serveur);
963 }
964 $couples = join(',', $noprims);
965
966 $seq = spip_pg_sequence($table);
967 if ($prefixe) {
968 $table = preg_replace('/^spip/', $prefixe, $table);
969 $seq = preg_replace('/^spip/', $prefixe, $seq);
970 }
971
972 $connexion['last'] = $q = "UPDATE $table SET $couples WHERE $where";
973 if ($couples) {
974 $couples = spip_pg_query_simple($link, $q);
975 # spip_log($q,'pg.'._LOG_DEBUG);
976 if (!$couples) return false;
977 $couples = pg_affected_rows($couples);
978 }
979 if (!$couples) {
980 $ret = !$seq ? '' :
981 (" RETURNING nextval('$seq') < $prim");
982 $connexion['last'] = $q = "INSERT INTO $table (" . join(',',array_keys($values)) . ') VALUES (' .join(',', $values) . ")$ret";
983 $couples = spip_pg_query_simple($link, $q);
984 if (!$couples) {
985 return false;
986 } elseif ($ret) {
987 $r = pg_fetch_array($couples, NULL, PGSQL_NUM);
988 if ($r[0]) {
989 $connexion['last'] = $q = "SELECT setval('$seq', $prim) from $table";
990 // Le code de SPIP met parfois la sequence a 0 (dans l'import)
991 // MySQL n'en dit rien, on fait pareil pour PG
992 $r = @pg_query($link, $q);
993 }
994 }
995 }
996
997 return $couples;
998 }
999
1000
1001 // http://doc.spip.org/@spip_pg_replace_multi
1002 function spip_pg_replace_multi($table, $tab_couples, $desc=array(), $serveur='',$requeter=true) {
1003 // boucler pour traiter chaque requete independemment
1004 foreach ($tab_couples as $couples){
1005 $retour = spip_pg_replace($table, $couples, $desc, $serveur,$requeter);
1006 }
1007 // renvoie le dernier id
1008 return $retour;
1009 }
1010
1011
1012
1013 // Donne la sequence eventuelle associee a une table
1014 // Pas extensible pour le moment,
1015
1016 // http://doc.spip.org/@spip_pg_sequence
1017 function spip_pg_sequence($table,$raw=false)
1018 {
1019 global $tables_principales;
1020 include_spip('base/serial');
1021 if (!isset($tables_principales[$table])) return false;
1022 $desc = $tables_principales[$table];
1023 $prim = @$desc['key']['PRIMARY KEY'];
1024 if (!preg_match('/^\w+$/', $prim)
1025 OR strpos($desc['field'][$prim], 'int') === false)
1026 return '';
1027 else { return $raw?$prim:$table . '_' . $prim . "_seq";}
1028 }
1029
1030 // Explicite les conversions de Mysql d'une valeur $v de type $t
1031 // Dans le cas d'un champ date, pas d'apostrophe, c'est une syntaxe ad hoc
1032
1033 // http://doc.spip.org/@spip_pg_cite
1034 function spip_pg_cite($v, $t){
1035 if(is_null($v)) return 'NULL'; // null php se traduit en NULL SQL
1036
1037 if (sql_test_date($t)) {
1038 if ($v AND (strpos("0123456789", $v[0]) === false))
1039 return spip_pg_frommysql($v);
1040 else {
1041 if (strncmp($v,'0000',4)==0)
1042 $v = "0001" . substr($v,4);
1043 if (strpos($v, "-00-00") === 4)
1044 $v = substr($v,0,4)."-01-01".substr($v,10);
1045 return "timestamp '$v'";
1046 }
1047 }
1048 elseif (!sql_test_int($t))
1049 return ("'" . pg_escape_string($v) . "'");
1050 elseif (is_numeric($v) OR (strpos($v, 'CAST(') === 0))
1051 return $v;
1052 elseif ($v[0]== '0' AND $v[1]!=='x' AND ctype_xdigit(substr($v,1)))
1053 return substr($v,1);
1054 else {
1055 spip_log("Warning: '$v' n'est pas de type $t", 'pg.'._LOG_AVERTISSEMENT);
1056 return intval($v);
1057 }
1058 }
1059
1060 // http://doc.spip.org/@spip_pg_hex
1061 function spip_pg_hex($v)
1062 {
1063 return "CAST(x'" . $v . "' as bigint)";
1064 }
1065
1066 function spip_pg_quote($v, $type='')
1067 {
1068 if (!is_array($v))
1069 return spip_pg_cite($v,$type);
1070 // si c'est un tableau, le parcourir en propageant le type
1071 foreach($v as $k=>$r)
1072 $v[$k] = spip_pg_quote($r, $type);
1073 return join(",", $v);
1074 }
1075
1076 function spip_pg_date_proche($champ, $interval, $unite)
1077 {
1078 return '('
1079 . $champ
1080 . (($interval <= 0) ? '>' : '<')
1081 . (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1082 . '('
1083 . sql_quote(date('Y-m-d H:i:s'))
1084 . ', INTERVAL '
1085 . (($interval > 0) ? $interval : (0-$interval))
1086 . ' '
1087 . $unite
1088 . '))';
1089 }
1090
1091 // http://doc.spip.org/@spip_pg_in
1092 function spip_pg_in($val, $valeurs, $not='', $serveur) {
1093 //
1094 // IN (...) souvent limite a 255 elements, d'ou cette fonction assistante
1095 //
1096 // s'il n'y a pas de valeur, eviter de produire un IN vide: PG rale.
1097 if (!$valeurs) return $not ? '0=0' : '0=1';
1098 if (strpos($valeurs, "CAST(x'") !== false)
1099 return "($val=" . join("OR $val=", explode(',',$valeurs)).')';
1100 $n = $i = 0;
1101 $in_sql ="";
1102 while ($n = strpos($valeurs, ',', $n+1)) {
1103 if ((++$i) >= 255) {
1104 $in_sql .= "($val $not IN (" .
1105 substr($valeurs, 0, $n) .
1106 "))\n" .
1107 ($not ? "AND\t" : "OR\t");
1108 $valeurs = substr($valeurs, $n+1);
1109 $i = $n = 0;
1110 }
1111 }
1112 $in_sql .= "($val $not IN ($valeurs))";
1113
1114 return "($in_sql)";
1115 }
1116
1117 // http://doc.spip.org/@spip_pg_error
1118 function spip_pg_error($query='', $serveur, $requeter=true) {
1119 $link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
1120 $s = $link ? pg_last_error($link) : pg_last_error();
1121 if ($s) {
1122 $s = str_replace('ERROR', 'errcode: 1000 ', $s);
1123 spip_log("$s - $query", 'pg.'._LOG_ERREUR);
1124 }
1125 return $s;
1126 }
1127
1128 // http://doc.spip.org/@spip_pg_errno
1129 function spip_pg_errno($serveur='') {
1130 // il faudrait avoir la derniere ressource retournee et utiliser
1131 // http://fr2.php.net/manual/fr/function.pg-result-error.php
1132 return 0;
1133 }
1134
1135 // http://doc.spip.org/@spip_pg_drop_table
1136 function spip_pg_drop_table($table, $exist='', $serveur='',$requeter=true)
1137 {
1138 if ($exist) $exist =" IF EXISTS";
1139 if (spip_pg_query("DROP TABLE$exist $table", $serveur, $requeter))
1140 return true;
1141 else return false;
1142 }
1143
1144 // supprime une vue
1145 // http://doc.spip.org/@spip_pg_drop_view
1146 function spip_pg_drop_view($view, $exist='', $serveur='',$requeter=true) {
1147 if ($exist) $exist =" IF EXISTS";
1148 return spip_pg_query("DROP VIEW$exist $view", $serveur, $requeter);
1149 }
1150
1151 /**
1152 * Retourne une ressource de la liste des tables de la base de données
1153 *
1154 * @param string $match
1155 * Filtre sur tables à récupérer
1156 * @param string $serveur
1157 * Connecteur de la base
1158 * @param bool $requeter
1159 * true pour éxecuter la requête
1160 * false pour retourner le texte de la requête.
1161 * @return ressource
1162 * Ressource à utiliser avec sql_fetch()
1163 **/
1164 function spip_pg_showbase($match, $serveur='',$requeter=true)
1165 {
1166 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1167 $link = $connexion['link'];
1168 $connexion['last'] = $q = "SELECT tablename FROM pg_tables WHERE tablename ILIKE "._q($match);
1169 return spip_pg_query_simple($link, $q);
1170 }
1171
1172 // http://doc.spip.org/@spip_pg_showtable
1173 function spip_pg_showtable($nom_table, $serveur='',$requeter=true)
1174 {
1175 $connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1176 $link = $connexion['link'];
1177 $connexion['last'] = $q = "SELECT column_name, column_default, data_type FROM information_schema.columns WHERE table_name ILIKE " . _q($nom_table);
1178
1179 $res = spip_pg_query_simple($link, $q);
1180 if (!$res) return false;
1181
1182 // etrangement, $res peut ne rien contenir, mais arriver ici...
1183 // il faut en tenir compte dans le return
1184 $fields = array();
1185 while($field = pg_fetch_array($res, NULL, PGSQL_NUM)) {
1186 $fields[$field[0]] = $field[2] . (!$field[1] ? '' : (" DEFAULT " . $field[1]));
1187 }
1188 $connexion['last'] = $q = "SELECT indexdef FROM pg_indexes WHERE tablename ILIKE " . _q($nom_table);
1189 $res = spip_pg_query_simple($link, $q);
1190 $keys = array();
1191 while($index = pg_fetch_array($res, NULL, PGSQL_NUM)) {
1192 if (preg_match('/CREATE\s+(UNIQUE\s+)?INDEX\s([^\s]+).*\((.*)\)$/', $index[0],$r)) {
1193 $nom = str_replace($nom_table.'_','',$r[2]);
1194 $keys[($r[1] ? "PRIMARY KEY" : ("KEY " . $nom))] = $r[3];
1195 }
1196 }
1197
1198 return count($fields) ? array('field' => $fields, 'key' => $keys) : false;
1199 }
1200
1201 // Fonction de creation d'une table SQL nommee $nom
1202 // a partir de 2 tableaux PHP :
1203 // champs: champ => type
1204 // cles: type-de-cle => champ(s)
1205 // si $autoinc, c'est une auto-increment (i.e. serial) sur la Primary Key
1206 // Le nom des index est prefixe par celui de la table pour eviter les conflits
1207 // http://doc.spip.org/@spip_pg_create
1208 function spip_pg_create($nom, $champs, $cles, $autoinc=false, $temporary=false, $serveur='',$requeter=true) {
1209
1210 $connexion = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1211 $prefixe = $connexion['prefixe'];
1212 $link = $connexion['link'];
1213 $db = $connexion['db'];
1214 if ($prefixe) $nom = preg_replace('/^spip/', $prefixe, $nom);
1215 $query = $prim = $prim_name = $v = $s = $p='';
1216 $keys = array();
1217
1218 // certains plugins declarent les tables (permet leur inclusion dans le dump)
1219 // sans les renseigner (laisse le compilo recuperer la description)
1220 if (!is_array($champs) || !is_array($cles))
1221 return;
1222
1223 foreach($cles as $k => $v) {
1224 if (strpos($k, "KEY ") === 0) {
1225 $n = str_replace('`','',$k);
1226 $v = str_replace('`','"',$v);
1227 $i = $nom . preg_replace("/KEY +/", '_',$n);
1228 if ($k != $n) $i = "\"$i\"";
1229 $keys[] = "CREATE INDEX $i ON $nom ($v);";
1230 }
1231 elseif (strpos($k, "UNIQUE ") === 0) {
1232 $k = preg_replace("/^UNIQUE +/", '',$k);
1233 $prim .= "$s\n\t\tCONSTRAINT " . str_replace('`','"',$k) ." UNIQUE ($v)";
1234 }
1235 else {
1236 $prim .= "$s\n\t\t" . str_replace('`','"',$k) ." ($v)";
1237 }
1238 if ($k == "PRIMARY KEY")
1239 $prim_name = $v;
1240 $s = ",";
1241 }
1242 $s = '';
1243
1244 $character_set = "";
1245 if (@$GLOBALS['meta']['charset_sql_base'])
1246 $character_set .= " CHARACTER SET ".$GLOBALS['meta']['charset_sql_base'];
1247 if (@$GLOBALS['meta']['charset_collation_sql_base'])
1248 $character_set .= " COLLATE ".$GLOBALS['meta']['charset_collation_sql_base'];
1249
1250 foreach($champs as $k => $v) {
1251 $k = str_replace('`','"',$k);
1252 if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i',$v,$defs)){
1253 if (preg_match(',(char|text),i',$defs[1]) AND !preg_match(',binary,i',$defs[1]) ){
1254 $v = $defs[1] . $character_set . ' ' . substr($v,strlen($defs[1]));
1255 }
1256 }
1257
1258 $query .= "$s\n\t\t$k "
1259 . (($autoinc && ($prim_name == $k) && preg_match(',\b(big|small|medium|tiny)?int\b,i', $v))
1260 ? " bigserial"
1261 : mysql2pg_type($v)
1262 );
1263 $s = ",";
1264 }
1265 $temporary = $temporary ? 'TEMPORARY':'';
1266
1267 // En l'absence de "if not exists" en PG, on neutralise les erreurs
1268
1269 $q = "CREATE $temporary TABLE $nom ($query" . ($prim ? ",$prim" : '') . ")".
1270 ($character_set?" DEFAULT $character_set":"")
1271 ."\n";
1272
1273 if (!$requeter) return $q;
1274 $connexion['last'] = $q;
1275 $r = @pg_query($link, $q);
1276
1277 if (!$r)
1278 spip_log("Impossible de creer cette table: $q",'pg.'._LOG_ERREUR);
1279 else {
1280 foreach($keys as $index) {pg_query($link, $index);}
1281 }
1282 return $r;
1283 }
1284
1285
1286 function spip_pg_create_base($nom, $serveur='',$requeter=true) {
1287 return spip_pg_query("CREATE DATABASE $nom", $serveur, $requeter);
1288 }
1289
1290 // Fonction de creation d'une vue SQL nommee $nom
1291 // http://doc.spip.org/@spip_pg_create_view
1292 function spip_pg_create_view($nom, $query_select, $serveur='',$requeter=true) {
1293 if (!$query_select) return false;
1294 // vue deja presente
1295 if (sql_showtable($nom, false, $serveur)) {
1296 if ($requeter) spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)",'pg.'._LOG_ERREUR);
1297 return false;
1298 }
1299
1300 $query = "CREATE VIEW $nom AS ". $query_select;
1301 return spip_pg_query($query, $serveur, $requeter);
1302 }
1303
1304
1305 // http://doc.spip.org/@spip_pg_set_connect_charset
1306 function spip_pg_set_connect_charset($charset, $serveur='',$requeter=true){
1307 spip_log("changement de charset sql a ecrire en PG",'pg.'._LOG_ERREUR);
1308 }
1309
1310
1311 /**
1312 * Optimise une table SQL
1313 *
1314 * @param $table nom de la table a optimiser
1315 * @param $serveur nom de la connexion
1316 * @param $requeter effectuer la requete ? sinon retourner son code
1317 * @return bool|string true / false / requete
1318 **/
1319 // http://doc.spip.org/@spip_sqlite_optimize
1320 function spip_pg_optimize($table, $serveur='',$requeter=true){
1321 return spip_pg_query("VACUUM ". $table, $serveur, $requeter);
1322 }
1323
1324 // Selectionner la sous-chaine dans $objet
1325 // correspondant a $lang. Cf balise Multi de Spip
1326
1327 // http://doc.spip.org/@spip_pg_multi
1328 function spip_pg_multi ($objet, $lang) {
1329 $r = "regexp_replace("
1330 . $objet
1331 . ",'<multi>.*[[]"
1332 . $lang
1333 . "[]]([^[]*).*</multi>', E'\\\\1') AS multi";
1334 return $r;
1335 }
1336
1337 // Palanquee d'idiosyncrasies MySQL dans les creations de table
1338 // A completer par les autres, mais essayer de reduire en amont.
1339
1340 // http://doc.spip.org/@mysql2pg_type
1341 function mysql2pg_type($v){
1342 $remplace = array(
1343 '/auto_increment/i' => '', // non reconnu
1344 '/bigint/i' => 'bigint',
1345 '/mediumint/i' => 'mediumint',
1346 '/smallint/i'=> 'smallint',
1347 "/tinyint/i" => 'int',
1348 '/int\s*[(]\s*\d+\s*[)]/i' => 'int',
1349 "/longtext/i" => 'text',
1350 "/mediumtext/i" => 'text',
1351 "/tinytext/i" => 'text',
1352 "/longblob/i" => 'text',
1353 "/0000-00-00/" =>'0001-01-01',
1354 "/datetime/i" => 'timestamp',
1355 "/unsigned/i" => '',
1356 "/double/i" => 'double precision',
1357 '/VARCHAR\((\d+)\)\s+BINARY/i' => 'varchar(\1)',
1358 "/ENUM *[(][^)]*[)]/i" => "varchar(255)",
1359 '/(timestamp .* )ON .*$/is' => '\\1',
1360 );
1361
1362 return preg_replace(array_keys($remplace),array_values($remplace),$v);
1363 }
1364
1365 // Renvoie false si on n'a pas les fonctions pg (pour l'install)
1366 // http://doc.spip.org/@spip_versions_pg
1367 function spip_versions_pg(){
1368 charger_php_extension('pgsql');
1369 return function_exists('pg_connect');
1370 }
1371
1372 ?>