source: trunk/include/dblayer/functions_pgsql.inc.php @ 12922

Last change on this file since 12922 was 12922, checked in by mistic100, 12 years ago

update Piwigo headers to 2012, last change before the expected (or not) apocalypse

File size: 17.4 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2012 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24define('REQUIRED_PGSQL_VERSION', '8.0');
25define('DB_ENGINE', 'PostgreSQL');
26
27define('DB_REGEX_OPERATOR', '~');
28define('DB_RANDOM_FUNCTION', 'RANDOM');
29
30/**
31 *
32 * simple functions
33 *
34 */
35
36function pwg_db_connect($host, $user, $password, $database)
37{
38  $connection_string = '';
39  if (strpos($host,':') !== false) 
40  {
41    list($host, $port) = explode(':', $host);
42  }
43  $connection_string = sprintf('host=%s', $host);
44  if (!empty($port))
45  {
46    $connection_string .= sprintf(' port=%d', $port);
47  }
48  $connection_string .= sprintf(' user=%s password=%s dbname=%s', 
49                                $user, 
50                                $password,
51                                $database);
52  $link = @pg_connect($connection_string);
53  if (!$link)
54  {
55    throw new Exception("Can't connect to server");
56  }
57  else
58  {
59    return $link;
60  }
61}
62
63function pwg_db_check_version()
64{
65  $current_version = pwg_get_db_version();
66  if (version_compare($current_version, REQUIRED_PGSQL_VERSION, '<'))
67  {
68    fatal_error(
69      sprintf(
70        'your database version is too old, you have "%s" and you need at least "%s"',
71        $current_version,
72        REQUIRED_PGSQL_VERSION
73        )
74      );
75  }
76}
77
78function pwg_db_check_charset() 
79{
80  return true;
81}
82
83function pwg_get_db_version() 
84{
85  list($pg_version) = pwg_db_fetch_row(pwg_query('SHOW SERVER_VERSION;'));
86 
87  return $pg_version;
88}
89
90function pwg_query($query)
91{
92  global $conf,$page,$debug,$t2;
93
94  $replace_pattern = '`REPLACE INTO\s(\S*)\s*([^)]*\))\s*VALUES\(([^,]*),(.*)\)\s*`mi';
95  $select_distinct_pattern = '/SELECT\s+DISTINCT\s*(\S[^;]*\S)\s*(FROM[^(;]+WHERE[^;]+)\s+ORDER\s+BY\s+([^;]*\S)\s*;?/i';
96
97  $start = get_moment();
98
99  if (preg_match($replace_pattern, $query, $matches)
100      && $matches[1]==SESSIONS_TABLE)
101  {
102    $select_query = '
103SELECT id FROM '.$matches[1].'
104  WHERE id='.$matches[3];
105    ( $result = pg_query($select_query)) or die($query."\n<br>".pg_last_error());
106    if (pwg_db_num_rows($result)==1)
107    {
108      $query = '
109UPDATE '.$matches[1].'
110  SET expiration=now()
111  WHERE id='.$matches[3];
112    }
113    else
114    {
115      $query = '
116INSERT INTO '.$matches[1].'
117  '.$matches[2].' VALUES('.$matches[3].','.$matches[4].')';
118    }
119    ( $result = pg_query($query)) or die($query."\n<br>".pg_last_error());     
120  }
121  elseif (preg_match($select_distinct_pattern, $query, $matches))
122  {
123    $select_fields_string='';
124    $distinct_fields_string='';
125    $orderby_fields_string='';
126   
127    foreach (preg_split( '/\s*,\s*/', $matches[1]) as $field)
128    {
129      $split_field = preg_split( '/\s*AS\s*/i', $field);
130      if (isset($split_field[1]))
131      {
132         $distinct_fields[ $split_field[1] ] = $field;
133      }
134      else
135      {
136        $distinct_fields[ $field ] = $field;
137      }
138    }
139   
140    foreach (preg_split( '/\s*,\s*/', $matches[3]) as $field)
141    {
142      $kv = preg_split( '/\s+/', $field );
143      $orderby_fields[ $kv[0] ] = $kv[1];
144    }
145   
146    foreach ($distinct_fields as $as_field => $field)
147    {
148      if ($distinct_fields_string)
149      {
150        $distinct_fields_string=$distinct_fields_string.', ';
151      }
152     
153      $distinct_fields_string=$distinct_fields_string.$as_field;
154     
155      if ($select_fields_string)
156      {
157        $select_fields_string=$select_fields_string.', ';
158      }
159     
160      $select_fields_string=$select_fields_string.$field;
161     
162      if ($orderby_fields_string)
163      {
164        $orderby_fields_string=$orderby_fields_string.', ';
165      }
166     
167      $orderby_fields_string=$orderby_fields_string.$as_field.' ';
168     
169      if (isset($orderby_fields[$as_field]))
170      {
171        $orderby_fields_string=$orderby_fields_string.$orderby_fields[$as_field];
172        unset($orderby_fields[$as_field]);
173      }
174      else
175      {
176        $orderby_fields_string=$orderby_fields_string.'ASC';
177      }
178     }
179   
180     foreach ($orderby_fields as $field => $order)
181     {
182       $orderby_fields_string=$orderby_fields_string.', '.$field.' '.$order;
183     }
184     
185     $query = '
186SELECT DISTINCT ON ('.$distinct_fields_string.') '.$select_fields_string.'
187   '.$matches[2].'
188   ORDER BY '.$orderby_fields_string;
189     ($result = pg_query($query)) or die($query."\n<br>".pg_last_error());
190  }
191  else 
192  {
193    ($result = pg_query($query)) or die($query."\n<br>".pg_last_error());
194  }
195
196  $time = get_moment() - $start;
197
198  if (!isset($page['count_queries']))
199  {
200    $page['count_queries'] = 0;
201    $page['queries_time'] = 0;
202  }
203
204  $page['count_queries']++;
205  $page['queries_time']+= $time;
206
207  if ($conf['show_queries'])
208  {
209    $output = '';
210    $output.= '<pre>['.$page['count_queries'].'] ';
211    $output.= "\n".$query;
212    $output.= "\n".'(this query time : ';
213    $output.= '<b>'.number_format($time, 3, '.', ' ').' s)</b>';
214    $output.= "\n".'(total SQL time  : ';
215    $output.= number_format($page['queries_time'], 3, '.', ' ').' s)';
216    $output.= "\n".'(total time      : ';
217    $output.= number_format( ($time+$start-$t2), 3, '.', ' ').' s)';
218    if ( $result!=null and preg_match('/\s*SELECT\s+/i',$query) )
219    {
220      $output.= "\n".'(num rows        : ';
221      $output.= pwg_db_num_rows($result).' )';
222    }
223    elseif ( $result!=null
224      and preg_match('/\s*INSERT|UPDATE|REPLACE|DELETE\s+/i',$query) )
225    {
226      $output.= "\n".'(affected rows   : ';
227      $output.= pwg_db_changes($result).' )';
228    }
229    $output.= "</pre>\n";
230
231    $debug .= $output;
232  }
233
234  return $result;
235}
236
237function pwg_db_nextval($column, $table)
238{
239  $query = '
240SELECT nextval(\''.$table.'_'.$column.'_seq\')';
241  list($next) = pwg_db_fetch_row(pwg_query($query));
242
243  return $next;
244}
245
246/**
247 *
248 * complex functions
249 *
250 */
251
252function pwg_db_changes($result) 
253{
254  return pg_affected_rows($result);
255}
256
257function pwg_db_num_rows($result) 
258{
259  return pg_num_rows($result);
260}
261
262function pwg_db_fetch_assoc($result)
263{
264  return pg_fetch_assoc($result);
265}
266
267function pwg_db_fetch_row($result)
268{
269  return pg_fetch_row($result);
270}
271
272function pwg_db_fetch_object($result)
273{
274  return pg_fetch_object($result);
275}
276
277function pwg_db_free_result($result) 
278{
279  return pg_free_result($result);
280}
281
282function pwg_db_real_escape_string($s)
283{
284  return pg_escape_string($s);
285}
286
287function pwg_db_insert_id($table=null, $column='id')
288{
289  $sequence = sprintf('%s_%s_seq', strtolower($table), $column);
290  $query = '
291SELECT CURRVAL(\''.$sequence.'\');';
292
293  list($id) = pwg_db_fetch_row(pwg_query($query));
294
295  return $id;
296}
297
298/**
299 *
300 * complex functions
301 *
302 */
303
304/**
305 * creates an array based on a query, this function is a very common pattern
306 * used here
307 *
308 * @param string $query
309 * @param string $fieldname
310 * @return array
311 */
312function array_from_query($query, $fieldname)
313{
314  $array = array();
315
316  $result = pwg_query($query);
317  while ($row = pg_fetch_assoc($result))
318  {
319    array_push($array, $row[$fieldname]);
320  }
321
322  return $array;
323}
324
325define('MASS_UPDATES_SKIP_EMPTY', 1);
326/**
327 * updates multiple lines in a table
328 *
329 * @param string table_name
330 * @param array dbfields
331 * @param array datas
332 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
333 * @return void
334 */
335function mass_updates($tablename, $dbfields, $datas, $flags=0)
336{
337  if (count($datas) == 0)
338    return;
339
340  if (count($datas) < 10)
341  {
342    foreach ($datas as $data)
343    {
344      $query = '
345UPDATE '.$tablename.'
346  SET ';
347      $is_first = true;
348      foreach ($dbfields['update'] as $key)
349      {
350        $separator = $is_first ? '' : ",\n    ";
351
352        if (isset($data[$key]) and $data[$key] != '')
353        {
354          $query.= $separator.$key.' = \''.$data[$key].'\'';
355        }
356        else
357        {
358          if ( $flags & MASS_UPDATES_SKIP_EMPTY )
359            continue; // next field
360          $query.= "$separator$key = NULL";
361        }
362        $is_first = false;
363      }
364      if (!$is_first)
365      {// only if one field at least updated
366        $query.= '
367  WHERE ';
368        $is_first = true;
369        foreach ($dbfields['primary'] as $key)
370        {
371          if (!$is_first)
372          {
373            $query.= ' AND ';
374          }
375          if ( isset($data[$key]) )
376          {
377            $query.= $key.' = \''.$data[$key].'\'';
378          }
379          else
380          {
381            $query.= $key.' IS NULL';
382          }
383          $is_first = false;
384        }
385        pwg_query($query);
386      }
387    } // foreach update
388  } 
389  else
390  {
391    $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
392    $temporary_tablename = $tablename.'_'.micro_seconds();
393    $query = '
394CREATE TABLE '.$temporary_tablename.'
395  AS SELECT * FROM '.$tablename.' WHERE 1=2';
396
397    pwg_query($query);
398    mass_inserts($temporary_tablename, $all_fields, $datas);
399    if ( $flags & MASS_UPDATES_SKIP_EMPTY )
400      $func_set = create_function('$s', 'return "$s = NULLIF(t2.$s, '.$tablename.'.$s)";');
401    else
402      $func_set = create_function('$s', 'return "$s = t2.$s";');
403
404    // update of images table by joining with temporary table
405    $query = '
406UPDATE '.$tablename.'
407  SET '.
408      implode(
409        "\n    , ",
410        array_map($func_set, $dbfields['update'])
411        ).'
412FROM '.$temporary_tablename.' AS t2
413  WHERE '.
414      implode(
415        "\n    AND ",
416        array_map(
417          create_function('$s', 'return "'.$tablename.'.$s = t2.$s";'),
418          $dbfields['primary']
419          )
420        );
421    pwg_query($query);
422    $query = '
423DROP TABLE '.$temporary_tablename;
424    pwg_query($query);
425  }
426}
427
428/**
429 * updates one line in a table
430 *
431 * @param string table_name
432 * @param array set_fields
433 * @param array where_fields
434 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
435 * @return void
436 */
437function single_update($tablename, $set_fields, $where_fields, $flags=0)
438{
439  if (count($set_fields) == 0)
440  {
441    return;
442  }
443
444  $query = '
445UPDATE '.$tablename.'
446  SET ';
447  $is_first = true;
448  foreach ($set_fields as $key => $value)
449  {
450    $separator = $is_first ? '' : ",\n    ";
451
452    if (isset($value) and $value != '')
453    {
454      $query.= $separator.$key.' = \''.$value.'\'';
455    }
456    else
457    {
458      if ( $flags & MASS_UPDATES_SKIP_EMPTY )
459        continue; // next field
460      $query.= "$separator$key = NULL";
461    }
462    $is_first = false;
463  }
464  if (!$is_first)
465  {// only if one field at least updated
466    $query.= '
467  WHERE ';
468    $is_first = true;
469    foreach ($where_fields as $key => $value)
470    {
471      if (!$is_first)
472      {
473        $query.= ' AND ';
474      }
475      if ( isset($value) )
476      {
477        $query.= $key.' = \''.$value.'\'';
478      }
479      else
480      {
481        $query.= $key.' IS NULL';
482      }
483      $is_first = false;
484    }
485    pwg_query($query);
486  }
487}
488
489/**
490 * inserts multiple lines in a table
491 *
492 * @param string table_name
493 * @param array dbfields
494 * @param array inserts
495 * @return void
496 */
497function mass_inserts($table_name, $dbfields, $datas)
498{
499  if (count($datas) != 0)
500  {
501    $first = true;
502
503    $packet_size = 16777216;
504    $packet_size = $packet_size - 2000; // The last list of values MUST not exceed 2000 character*/
505    $query = '';
506
507    foreach ($datas as $insert)
508    {
509      if (strlen($query) >= $packet_size)
510      {
511        pwg_query($query);
512        $first = true;
513      }
514
515      if ($first)
516      {
517        $query = '
518INSERT INTO '.$table_name.'
519  ('.implode(',', $dbfields).')
520  VALUES';
521        $first = false;
522      }
523      else
524      {
525        $query .= '
526  , ';
527      }
528
529      $query .= '(';
530      foreach ($dbfields as $field_id => $dbfield)
531      {
532        if ($field_id > 0)
533        {
534          $query .= ',';
535        }
536
537        if (!isset($insert[$dbfield]) or $insert[$dbfield] === '')
538        {
539          $query .= 'NULL';
540        }
541        else
542        {
543          $query .= "'".$insert[$dbfield]."'";
544        }
545      }
546      $query .= ')';
547    }
548    pwg_query($query);
549  }
550}
551
552/**
553 * inserts one line in a table
554 *
555 * @param string table_name
556 * @param array dbfields
557 * @param array insert
558 * @return void
559 */
560function single_insert($table_name, $data)
561{
562  if (count($data) != 0)
563  {
564    $query = '
565INSERT INTO '.$table_name.'
566  ('.implode(',', array_keys($data)).')
567  VALUES';
568
569    $query .= '(';
570    $is_first = true;
571    foreach ($data as $key => $value)
572    {
573      if (!$is_first)
574      {
575        $query .= ',';
576      }
577      else
578      {
579        $is_first = false;
580      }
581     
582      if ($value === '')
583      {
584        $query .= 'NULL';
585      }
586      else
587      {
588        $query .= "'".$value."'";
589      }
590    }
591    $query .= ')';
592   
593    pwg_query($query);
594  }
595}
596
597/**
598 * Do maintenance on all PWG tables
599 *
600 * @return none
601 */
602function do_maintenance_all_tables()
603{
604  global $prefixeTable, $page;
605
606  $all_tables = array();
607
608  // List all tables
609  $query = 'SELECT tablename FROM pg_tables
610WHERE tablename like \''.$prefixeTable.'%\'';
611
612  $all_tables = array_from_query($query, 'tablename');
613
614  // Optimize all tables
615  foreach ($all_tables as $table)
616  {
617    $query = 'VACUUM FULL '.$table;
618    pwg_query($query);
619  }
620  array_push($page['infos'],
621             l10n('All optimizations have been successfully completed.')
622             );
623}
624
625function pwg_db_concat($array)
626{
627  return implode($array, ' || ');
628}
629
630function pwg_db_concat_ws($array, $separator)
631{
632  return implode($array, ' || \''.$separator.'\' || ');
633}
634
635function pwg_db_cast_to_text($string)
636{
637  return 'CAST('.$string.' AS TEXT)';
638}
639
640/**
641 * returns an array containing the possible values of an enum field
642 *
643 * @param string tablename
644 * @param string fieldname
645 */
646function get_enums($table, $field)
647{
648  $typname = preg_replace('/'.$GLOBALS['prefixeTable'].'/', '', $table); 
649  $typname .= '_' . $field;
650
651  $query = 'SELECT
652enumlabel FROM pg_enum JOIN pg_type
653  ON pg_enum.enumtypid=pg_type.oid
654  WHERE typname=\''.$typname.'\'
655';
656  $result = pwg_query($query);
657  while ($row = pwg_db_fetch_assoc($result))
658  {
659    $options[] = $row['enumlabel'];
660  }
661
662  return $options;
663}
664
665// get_boolean transforms a string to a boolean value. If the string is
666// "false" (case insensitive), then the boolean value false is returned. In
667// any other case, true is returned.
668function get_boolean( $string )
669{
670  $boolean = true;
671  if ('f' == $string || 'false' == $string)
672  {
673    $boolean = false;
674  }
675  return $boolean;
676}
677
678/**
679 * returns boolean string 'true' or 'false' if the given var is boolean
680 *
681 * @param mixed $var
682 * @return mixed
683 */
684function boolean_to_string($var)
685{
686  if (is_bool($var))
687  {
688    return $var ? 'true' : 'false';
689  }
690  elseif ($var=='t' || $var=='f')
691  {
692    return ($var=='t') ? 'true' : 'false';
693  }
694  else
695  {
696    return $var;
697  }
698}
699
700/**
701 *
702 * interval and date functions
703 *
704 */
705
706function pwg_db_get_recent_period_expression($period, $date='CURRENT_DATE')
707{
708  if ($date!='CURRENT_DATE')
709  {
710    $date = '\''.$date.'\'::date';
711  }
712
713  return '('.$date.' - \''.$period.' DAY\'::interval)::date';
714}
715
716function pwg_db_get_recent_period($period, $date='CURRENT_DATE')
717{
718  $query = 'select '.pwg_db_get_recent_period_expression($period, $date);
719  list($d) = pwg_db_fetch_row(pwg_query($query));
720
721  return $d;
722}
723
724function pwg_db_get_flood_period_expression($seconds)
725{
726  return 'now() - \''.$seconds.' SECOND\'::interval';
727}
728
729function pwg_db_get_hour($date)
730{
731  return 'EXTRACT(HOUR FROM '.$date.')';
732}
733
734function pwg_db_get_date_YYYYMM($date)
735{
736  return 'TO_CHAR('.$date.', \'YYYYMM\')';
737}
738
739function pwg_db_get_date_MMDD($date)
740{
741  return 'TO_CHAR('.$date.', \'MMDD\')';
742}
743
744function pwg_db_get_year($date)
745{
746  return 'EXTRACT(YEAR FROM '.$date.')';
747}
748
749function pwg_db_get_month($date)
750{
751  return 'EXTRACT(MONTH FROM '.$date.')';
752}
753
754function pwg_db_get_week($date, $mode=null)
755{
756  return 'EXTRACT(WEEK FROM '.$date.')';
757}
758
759function pwg_db_get_dayofmonth($date)
760{
761  return 'EXTRACT(DAY FROM '.$date.')';
762}
763
764function pwg_db_get_dayofweek($date)
765{
766  return 'EXTRACT(DOW FROM '.$date.')::INTEGER - 1';
767}
768
769function pwg_db_get_weekday($date)
770{
771  return 'EXTRACT(ISODOW FROM '.$date.')::INTEGER - 1';
772}
773
774function pwg_db_date_to_ts($date) 
775{
776  return 'EXTRACT(EPOCH FROM '.$date.')';
777}
778
779// my_error returns (or send to standard output) the message concerning the
780// error occured for the last pgsql query.
781function my_error($header, $die)
782{
783  $error = '[pgsql error]'.pg_last_error()."\n";
784  $error .= $header;
785
786  if ($die)
787  {
788    fatal_error($error);
789  }
790  echo("<pre>");
791  trigger_error($error, E_USER_WARNING);
792  echo("</pre>");
793}
794
795
796?>
Note: See TracBrowser for help on using the repository browser.