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

Last change on this file since 28064 was 27337, checked in by mistic100, 10 years ago

feature 2999: (incomplete) doc of mysqli functions

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