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