source: trunk/include/dblayer/functions_mysqli.inc.php @ 22184

Last change on this file since 22184 was 22184, checked in by plg, 11 years ago

merge r22183 from branch 2.5 to trunk

typo fixed in code comment

File size: 17.6 KB
RevLine 
[20462]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('DB_ENGINE', 'MySQL');
25define('REQUIRED_MYSQL_VERSION', '5.0.0');
26
27define('DB_REGEX_OPERATOR', 'REGEXP');
28define('DB_RANDOM_FUNCTION', 'RAND');
29
30/**
31 *
32 * simple functions
33 *
34 */
35
36function pwg_db_connect($host, $user, $password, $database)
37{
38  global $mysqli;
[22182]39
[22184]40  // $host can be:
[22182]41  //
42  // $host = localhost
43  // $host = 1.2.3.4:3405
44  // $host = /path/to/socket
45
46  $port = null;
47  $socket = null;
[20462]48 
[22182]49  if (strpos($host, '/') === 0)
50  {
51    $host = null;
52    $socket = $host;
53  }
54  elseif (strpos($host, ':') !== false)
55  {
56    list($host, $port) = explode(':', $host);
57  }
58
59  $dbname = null;
60 
61  $mysqli = new mysqli($host, $user, $password, $dbname, $port, $socket);
[20462]62  if (mysqli_connect_error())
63  {
64    throw new Exception("Can't connect to server");
65  }
66  if (!$mysqli->select_db($database))
67  {
68    throw new Exception('Connection to server succeed, but it was impossible to connect to database');
69  }
70}
71
72function pwg_db_check_charset() 
73{
74  $db_charset = 'utf8';
75  if (defined('DB_CHARSET') and DB_CHARSET != '')
76  {
77    $db_charset = DB_CHARSET;
78  }
79  pwg_query('SET NAMES "'.$db_charset.'"');
80}
81
82function pwg_db_check_version()
83{
84  $current_mysql = pwg_get_db_version();
85  if (version_compare($current_mysql, REQUIRED_MYSQL_VERSION, '<'))
86  {
87    fatal_error(
88      sprintf(
89        'your MySQL version is too old, you have "%s" and you need at least "%s"',
90        $current_mysql,
91        REQUIRED_MYSQL_VERSION
92        )
93      );
94  }
95}
96
97function pwg_get_db_version() 
98{
99  global $mysqli;
100 
101  return $mysqli->server_info;
102}
103
104function pwg_query($query)
105{
106  global $mysqli, $conf, $page, $debug, $t2;
107
108  $start = microtime(true);
109  ($result = $mysqli->query($query)) or my_error($query, $conf['die_on_sql_error']);
110
111  $time = microtime(true) - $start;
112
113  if (!isset($page['count_queries']))
114  {
115    $page['count_queries'] = 0;
116    $page['queries_time'] = 0;
117  }
118
119  $page['count_queries']++;
120  $page['queries_time']+= $time;
121
122  if ($conf['show_queries'])
123  {
124    $output = '';
125    $output.= '<pre>['.$page['count_queries'].'] ';
126    $output.= "\n".$query;
127    $output.= "\n".'(this query time : ';
128    $output.= '<b>'.number_format($time, 3, '.', ' ').' s)</b>';
129    $output.= "\n".'(total SQL time  : ';
130    $output.= number_format($page['queries_time'], 3, '.', ' ').' s)';
131    $output.= "\n".'(total time      : ';
132    $output.= number_format( ($time+$start-$t2), 3, '.', ' ').' s)';
133    if ( $result!=null and preg_match('/\s*SELECT\s+/i',$query) )
134    {
135      $output.= "\n".'(num rows        : ';
136      $output.= pwg_db_num_rows($result).' )';
137    }
138    elseif ( $result!=null
139      and preg_match('/\s*INSERT|UPDATE|REPLACE|DELETE\s+/i',$query) )
140    {
141      $output.= "\n".'(affected rows   : ';
142      $output.= pwg_db_changes().' )';
143    }
144    $output.= "</pre>\n";
145
146    $debug .= $output;
147  }
148
149  return $result;
150}
151
152function pwg_db_nextval($column, $table)
153{
154  $query = '
155SELECT IF(MAX('.$column.')+1 IS NULL, 1, MAX('.$column.')+1)
156  FROM '.$table;
157  list($next) = pwg_db_fetch_row(pwg_query($query));
158
159  return $next;
160}
161
162function pwg_db_changes() 
163{
164  global $mysqli;
165 
166  return $mysqli->affected_rows;
167}
168
169function pwg_db_num_rows($result) 
170{
171  return $result->num_rows;
172}
173
[20510]174function pwg_db_fetch_array($result)
175{
176  return $result->fetch_array();
177}
178
[20462]179function pwg_db_fetch_assoc($result)
180{
181  return $result->fetch_assoc();
182}
183
184function pwg_db_fetch_row($result)
185{
186  return $result->fetch_row();
187}
188
189function pwg_db_fetch_object($result)
190{
191  return $result->fetch_object();
192}
193
194function pwg_db_free_result($result) 
195{
196  return $result->free_result();
197}
198
199function pwg_db_real_escape_string($s)
200{
201  global $mysqli;
202 
203  return $mysqli->real_escape_string($s);
204}
205
206function pwg_db_insert_id()
207{
208  global $mysqli;
209 
210  return $mysqli->insert_id;
211}
212
[21088]213function pwg_db_errno()
214{
215  global $mysqli;
216 
217  return $mysqli->errno;
218}
219
220function pwg_db_error()
221{
222  global $mysqli;
223 
224  return $mysqli->error;
225}
226
[20462]227function pwg_db_close()
228{
229  global $mysqli;
230 
231  return $mysqli->close();
232}
233
234/**
235 *
236 * complex functions
237 *
238 */
239
240/**
241 * creates an array based on a query, this function is a very common pattern
242 * used here
243 *
244 * @param string $query
245 * @param string $fieldname optional
246 * @return array
247 */
248function array_from_query($query, $fieldname=false)
249{
250  $array = array();
251
252  $result = pwg_query($query);
253  if (false === $fieldname)
254  {
255    while ($row = pwg_db_fetch_assoc($result))
256    {
257      $array[] = $row;     
258    }
259  }
260  else
261  {
262    while ($row = pwg_db_fetch_assoc($result))
263    {
264      $array[] = $row[$fieldname];
265    }
266  }
267  return $array;
268}
269
270define('MASS_UPDATES_SKIP_EMPTY', 1);
271/**
272 * updates multiple lines in a table
273 *
274 * @param string table_name
275 * @param array dbfields
276 * @param array datas
277 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
278 * @return void
279 */
280function mass_updates($tablename, $dbfields, $datas, $flags=0)
281{
282  if (count($datas) == 0)
283    return;
284 
285  // depending on the MySQL version, we use the multi table update or N update queries
286  if (count($datas) < 10)
287  {
288    foreach ($datas as $data)
289    {
290      $query = '
291UPDATE '.$tablename.'
292  SET ';
293      $is_first = true;
294      foreach ($dbfields['update'] as $key)
295      {
296        $separator = $is_first ? '' : ",\n    ";
297
298        if (isset($data[$key]) and $data[$key] != '')
299        {
300          $query.= $separator.$key.' = \''.$data[$key].'\'';
301        }
302        else
303        {
304          if ( $flags & MASS_UPDATES_SKIP_EMPTY )
305            continue; // next field
306          $query.= "$separator$key = NULL";
307        }
308        $is_first = false;
309      }
310      if (!$is_first)
311      {// only if one field at least updated
312        $query.= '
313  WHERE ';
314        $is_first = true;
315        foreach ($dbfields['primary'] as $key)
316        {
317          if (!$is_first)
318          {
319            $query.= ' AND ';
320          }
321          if ( isset($data[$key]) )
322          {
323            $query.= $key.' = \''.$data[$key].'\'';
324          }
325          else
326          {
327            $query.= $key.' IS NULL';
328          }
329          $is_first = false;
330        }
331        pwg_query($query);
332      }
333    } // foreach update
334  } // if mysqli_ver or count<X
335  else
336  {
337    // creation of the temporary table
338    $query = '
339SHOW FULL COLUMNS FROM '.$tablename;
340    $result = pwg_query($query);
341    $columns = array();
342    $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
343    while ($row = pwg_db_fetch_assoc($result))
344    {
345      if (in_array($row['Field'], $all_fields))
346      {
347        $column = $row['Field'];
348        $column.= ' '.$row['Type'];
349
350        $nullable = true;
351        if (!isset($row['Null']) or $row['Null'] == '' or $row['Null']=='NO')
352        {
353          $column.= ' NOT NULL';
354          $nullable = false;
355        }
356        if (isset($row['Default']))
357        {
358          $column.= " default '".$row['Default']."'";
359        }
360        elseif ($nullable)
361        {
362          $column.= " default NULL";
363        }
364        if (isset($row['Collation']) and $row['Collation'] != 'NULL')
365        {
366          $column.= " collate '".$row['Collation']."'";
367        }
368        array_push($columns, $column);
369      }
370    }
371
372    $temporary_tablename = $tablename.'_'.micro_seconds();
373
374    $query = '
375CREATE TABLE '.$temporary_tablename.'
376(
377  '.implode(",\n  ", $columns).',
378  UNIQUE KEY the_key ('.implode(',', $dbfields['primary']).')
379)';
380
381    pwg_query($query);
382    mass_inserts($temporary_tablename, $all_fields, $datas);
383    if ( $flags & MASS_UPDATES_SKIP_EMPTY )
384      $func_set = create_function('$s', 'return "t1.$s = IFNULL(t2.$s, t1.$s)";');
385    else
386      $func_set = create_function('$s', 'return "t1.$s = t2.$s";');
387
388    // update of images table by joining with temporary table
389    $query = '
390UPDATE '.$tablename.' AS t1, '.$temporary_tablename.' AS t2
391  SET '.
392      implode(
393        "\n    , ",
394        array_map($func_set,$dbfields['update'])
395        ).'
396  WHERE '.
397      implode(
398        "\n    AND ",
399        array_map(
400          create_function('$s', 'return "t1.$s = t2.$s";'),
401          $dbfields['primary']
402          )
403        );
404    pwg_query($query);
405    $query = '
406DROP TABLE '.$temporary_tablename;
407    pwg_query($query);
408  }
409}
410
411/**
412 * updates one line in a table
413 *
414 * @param string table_name
415 * @param array set_fields
416 * @param array where_fields
417 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
418 * @return void
419 */
420function single_update($tablename, $set_fields, $where_fields, $flags=0)
421{
422  if (count($set_fields) == 0)
423  {
424    return;
425  }
426
427  $query = '
428UPDATE '.$tablename.'
429  SET ';
430  $is_first = true;
431  foreach ($set_fields as $key => $value)
432  {
433    $separator = $is_first ? '' : ",\n    ";
434
435    if (isset($value) and $value !== '')
436    {
437      $query.= $separator.$key.' = \''.$value.'\'';
438    }
439    else
440    {
441      if ( $flags & MASS_UPDATES_SKIP_EMPTY )
442        continue; // next field
443      $query.= "$separator$key = NULL";
444    }
445    $is_first = false;
446  }
447  if (!$is_first)
448  {// only if one field at least updated
449    $query.= '
450  WHERE ';
451    $is_first = true;
452    foreach ($where_fields as $key => $value)
453    {
454      if (!$is_first)
455      {
456        $query.= ' AND ';
457      }
458      if ( isset($value) )
459      {
460        $query.= $key.' = \''.$value.'\'';
461      }
462      else
463      {
464        $query.= $key.' IS NULL';
465      }
466      $is_first = false;
467    }
468    pwg_query($query);
469  }
470}
471
472
473/**
474 * inserts multiple lines in a table
475 *
476 * @param string table_name
477 * @param array dbfields
478 * @param array inserts
479 * @return void
480 */
481function mass_inserts($table_name, $dbfields, $datas, $options=array())
482{
483  $ignore = '';
484  if (isset($options['ignore']) and $options['ignore'])
485  {
486    $ignore = 'IGNORE';
487  }
488 
489  if (count($datas) != 0)
490  {
491    $first = true;
492
493    $query = 'SHOW VARIABLES LIKE \'max_allowed_packet\'';
494    list(, $packet_size) = pwg_db_fetch_row(pwg_query($query));
495    $packet_size = $packet_size - 2000; // The last list of values MUST not exceed 2000 character*/
496    $query = '';
497
498    foreach ($datas as $insert)
499    {
500      if (strlen($query) >= $packet_size)
501      {
502        pwg_query($query);
503        $first = true;
504      }
505
506      if ($first)
507      {
508        $query = '
509INSERT '.$ignore.' INTO '.$table_name.'
510  ('.implode(',', $dbfields).')
511  VALUES';
512        $first = false;
513      }
514      else
515      {
516        $query .= '
517  , ';
518      }
519
520      $query .= '(';
521      foreach ($dbfields as $field_id => $dbfield)
522      {
523        if ($field_id > 0)
524        {
525          $query .= ',';
526        }
527
528        if (!isset($insert[$dbfield]) or $insert[$dbfield] === '')
529        {
530          $query .= 'NULL';
531        }
532        else
533        {
534          $query .= "'".$insert[$dbfield]."'";
535        }
536      }
537      $query .= ')';
538    }
539    pwg_query($query);
540  }
541}
542
543/**
544 * inserts one line in a table
545 *
546 * @param string table_name
547 * @param array dbfields
548 * @param array insert
549 * @return void
550 */
551function single_insert($table_name, $data)
552{
553  if (count($data) != 0)
554  {
555    $query = '
556INSERT INTO '.$table_name.'
557  ('.implode(',', array_keys($data)).')
558  VALUES';
559
560    $query .= '(';
561    $is_first = true;
562    foreach ($data as $key => $value)
563    {
564      if (!$is_first)
565      {
566        $query .= ',';
567      }
568      else
569      {
570        $is_first = false;
571      }
572     
573      if ($value === '')
574      {
575        $query .= 'NULL';
576      }
577      else
578      {
579        $query .= "'".$value."'";
580      }
581    }
582    $query .= ')';
583   
584    pwg_query($query);
585  }
586}
587
588/**
589 * Do maintenance on all PWG tables
590 *
591 * @return none
592 */
593function do_maintenance_all_tables()
594{
595  global $prefixeTable, $page;
596
597  $all_tables = array();
598
599  // List all tables
600  $query = 'SHOW TABLES LIKE \''.$prefixeTable.'%\'';
601  $result = pwg_query($query);
602  while ($row = pwg_db_fetch_row($result))
603  {
604    array_push($all_tables, $row[0]);
605  }
606
607  // Repair all tables
608  $query = 'REPAIR TABLE '.implode(', ', $all_tables);
609  $mysqli_rc = pwg_query($query);
610
611  // Re-Order all tables
612  foreach ($all_tables as $table_name)
613  {
614    $all_primary_key = array();
615
616    $query = 'DESC '.$table_name.';';
617    $result = pwg_query($query);
618    while ($row = pwg_db_fetch_assoc($result))
619    {
620      if ($row['Key'] == 'PRI')
621      {
622        array_push($all_primary_key, $row['Field']);
623      }
624    }
625
626    if (count($all_primary_key) != 0)
627    {
628      $query = 'ALTER TABLE '.$table_name.' ORDER BY '.implode(', ', $all_primary_key).';';
629      $mysqli_rc = $mysqli_rc && pwg_query($query);
630    }
631  }
632
633  // Optimize all tables
634  $query = 'OPTIMIZE TABLE '.implode(', ', $all_tables);
635  $mysqli_rc = $mysqli_rc && pwg_query($query);
636  if ($mysqli_rc)
637  {
638    array_push(
639          $page['infos'],
640          l10n('All optimizations have been successfully completed.')
641          );
642  }
643  else
644  {
645    array_push(
646          $page['errors'],
647          l10n('Optimizations have been completed with some errors.')
648          );
649  }
650}
651
652function pwg_db_concat($array)
653{
654  $string = implode($array, ',');
655  return 'CONCAT('. $string.')';
656}
657
658function pwg_db_concat_ws($array, $separator)
659{
660  $string = implode($array, ',');
661  return 'CONCAT_WS(\''.$separator.'\','. $string.')';
662}
663
664function pwg_db_cast_to_text($string)
665{
666  return $string;
667}
668
669/**
670 * returns an array containing the possible values of an enum field
671 *
672 * @param string tablename
673 * @param string fieldname
674 */
675function get_enums($table, $field)
676{
677  // retrieving the properties of the table. Each line represents a field :
678  // columns are 'Field', 'Type'
679  $result = pwg_query('desc '.$table);
680  while ($row = pwg_db_fetch_assoc($result))
681  {
682    // we are only interested in the the field given in parameter for the
683    // function
684    if ($row['Field'] == $field)
685    {
686      // retrieving possible values of the enum field
687      // enum('blue','green','black')
688      $options = explode(',', substr($row['Type'], 5, -1));
689      foreach ($options as $i => $option)
690      {
691        $options[$i] = str_replace("'", '',$option);
692      }
693    }
694  }
695  pwg_db_free_result($result);
696  return $options;
697}
698
699/**
700 * Smartly checks if a variable is equivalent to true or false
701 *
702 * @param mixed input
703 * @return bool
704 */
705function get_boolean($input)
706{
707  if ('false' === strtolower($input))
708  {
709    return false;
710  }
711
712  return (bool)$input;
713}
714
715/**
716 * returns boolean string 'true' or 'false' if the given var is boolean
717 *
718 * @param mixed $var
719 * @return mixed
720 */
721function boolean_to_string($var)
722{
723  if (is_bool($var))
724  {
725    return $var ? 'true' : 'false';
726  }
727  else
728  {
729    return $var;
730  }
731}
732
733/**
734 *
735 * interval and date functions
736 *
737 */
738
739function pwg_db_get_recent_period_expression($period, $date='CURRENT_DATE')
740{
741  if ($date!='CURRENT_DATE')
742  {
743    $date = '\''.$date.'\'';
744  }
745
746  return 'SUBDATE('.$date.',INTERVAL '.$period.' DAY)';
747}
748
749function pwg_db_get_recent_period($period, $date='CURRENT_DATE')
750{
751  $query = '
752SELECT '.pwg_db_get_recent_period_expression($period);
753  list($d) = pwg_db_fetch_row(pwg_query($query));
754
755  return $d;
756}
757
758function pwg_db_get_flood_period_expression($seconds)
759{
760  return 'SUBDATE(now(), INTERVAL '.$seconds.' SECOND)';
761}
762
763function pwg_db_get_hour($date) 
764{
765  return 'hour('.$date.')';
766}
767
768function pwg_db_get_date_YYYYMM($date)
769{
770  return 'DATE_FORMAT('.$date.', \'%Y%m\')';
771}
772
773function pwg_db_get_date_MMDD($date)
774{
775  return 'DATE_FORMAT('.$date.', \'%m%d\')';
776}
777
778function pwg_db_get_year($date)
779{
780  return 'YEAR('.$date.')';
781}
782
783function pwg_db_get_month($date)
784{
785  return 'MONTH('.$date.')';
786}
787
788function pwg_db_get_week($date, $mode=null)
789{
790  if ($mode)
791  {
792    return 'WEEK('.$date.', '.$mode.')';
793  }
794  else
795  {
796    return 'WEEK('.$date.')';
797  }
798}
799
800function pwg_db_get_dayofmonth($date)
801{
802  return 'DAYOFMONTH('.$date.')';
803}
804
805function pwg_db_get_dayofweek($date)
806{
807  return 'DAYOFWEEK('.$date.')';
808}
809
810function pwg_db_get_weekday($date)
811{
812  return 'WEEKDAY('.$date.')';
813}
814
815function pwg_db_date_to_ts($date) 
816{
817  return 'UNIX_TIMESTAMP('.$date.')';
818}
819
820// my_error returns (or send to standard output) the message concerning the
821// error occured for the last mysql query.
822function my_error($header, $die)
823{
824  global $mysqli;
825 
826  $error = "[mysql error ".$mysqli->errno.'] '.$mysqli->error."\n";
827  $error .= $header;
828
829  if ($die)
830  {
831    fatal_error($error);
832  }
833  echo("<pre>");
834  trigger_error($error, E_USER_WARNING);
835  echo("</pre>");
836}
837
838?>
Note: See TracBrowser for help on using the repository browser.