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

Last change on this file since 27336 was 27336, checked in by rvelices, 10 years ago

arrayfromquery optimizations: move double if from inside loop to outside + use directly mysqli calls to avoid function call overhead for every row retrieved from db

File size: 18.5 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
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;
39
40  // $host can be:
41  //
42  // $host = localhost
43  // $host = 1.2.3.4:3405
44  // $host = /path/to/socket
45
46  $port = null;
47  $socket = null;
48 
49  if (strpos($host, '/') === 0)
50  {
51    $socket = $host;
52    $host = null;
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);
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
174function pwg_db_fetch_array($result)
175{
176  return $result->fetch_array();
177}
178
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
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
227function pwg_db_close()
228{
229  global $mysqli;
230 
231  return $mysqli->close();
232}
233
234/**
235 *
236 * complex functions
237 *
238 */
239
240define('MASS_UPDATES_SKIP_EMPTY', 1);
241/**
242 * updates multiple lines in a table
243 *
244 * @param string table_name
245 * @param array dbfields
246 * @param array datas
247 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
248 * @return void
249 */
250function mass_updates($tablename, $dbfields, $datas, $flags=0)
251{
252  if (count($datas) == 0)
253    return;
254 
255  // depending on the MySQL version, we use the multi table update or N update queries
256  if (count($datas) < 10)
257  {
258    foreach ($datas as $data)
259    {
260      $query = '
261UPDATE '.$tablename.'
262  SET ';
263      $is_first = true;
264      foreach ($dbfields['update'] as $key)
265      {
266        $separator = $is_first ? '' : ",\n    ";
267
268        if (isset($data[$key]) and $data[$key] != '')
269        {
270          $query.= $separator.$key.' = \''.$data[$key].'\'';
271        }
272        else
273        {
274          if ( $flags & MASS_UPDATES_SKIP_EMPTY )
275            continue; // next field
276          $query.= "$separator$key = NULL";
277        }
278        $is_first = false;
279      }
280      if (!$is_first)
281      {// only if one field at least updated
282        $query.= '
283  WHERE ';
284        $is_first = true;
285        foreach ($dbfields['primary'] as $key)
286        {
287          if (!$is_first)
288          {
289            $query.= ' AND ';
290          }
291          if ( isset($data[$key]) )
292          {
293            $query.= $key.' = \''.$data[$key].'\'';
294          }
295          else
296          {
297            $query.= $key.' IS NULL';
298          }
299          $is_first = false;
300        }
301        pwg_query($query);
302      }
303    } // foreach update
304  } // if mysqli_ver or count<X
305  else
306  {
307    // creation of the temporary table
308    $query = '
309SHOW FULL COLUMNS FROM '.$tablename;
310    $result = pwg_query($query);
311    $columns = array();
312    $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
313    while ($row = pwg_db_fetch_assoc($result))
314    {
315      if (in_array($row['Field'], $all_fields))
316      {
317        $column = $row['Field'];
318        $column.= ' '.$row['Type'];
319
320        $nullable = true;
321        if (!isset($row['Null']) or $row['Null'] == '' or $row['Null']=='NO')
322        {
323          $column.= ' NOT NULL';
324          $nullable = false;
325        }
326        if (isset($row['Default']))
327        {
328          $column.= " default '".$row['Default']."'";
329        }
330        elseif ($nullable)
331        {
332          $column.= " default NULL";
333        }
334        if (isset($row['Collation']) and $row['Collation'] != 'NULL')
335        {
336          $column.= " collate '".$row['Collation']."'";
337        }
338        $columns[] = $column;
339      }
340    }
341
342    $temporary_tablename = $tablename.'_'.micro_seconds();
343
344    $query = '
345CREATE TABLE '.$temporary_tablename.'
346(
347  '.implode(",\n  ", $columns).',
348  UNIQUE KEY the_key ('.implode(',', $dbfields['primary']).')
349)';
350
351    pwg_query($query);
352    mass_inserts($temporary_tablename, $all_fields, $datas);
353    if ( $flags & MASS_UPDATES_SKIP_EMPTY )
354      $func_set = create_function('$s', 'return "t1.$s = IFNULL(t2.$s, t1.$s)";');
355    else
356      $func_set = create_function('$s', 'return "t1.$s = t2.$s";');
357
358    // update of images table by joining with temporary table
359    $query = '
360UPDATE '.$tablename.' AS t1, '.$temporary_tablename.' AS t2
361  SET '.
362      implode(
363        "\n    , ",
364        array_map($func_set,$dbfields['update'])
365        ).'
366  WHERE '.
367      implode(
368        "\n    AND ",
369        array_map(
370          create_function('$s', 'return "t1.$s = t2.$s";'),
371          $dbfields['primary']
372          )
373        );
374    pwg_query($query);
375    $query = '
376DROP TABLE '.$temporary_tablename;
377    pwg_query($query);
378  }
379}
380
381/**
382 * updates one line in a table
383 *
384 * @param string table_name
385 * @param array set_fields
386 * @param array where_fields
387 * @param int flags - if MASS_UPDATES_SKIP_EMPTY - empty values do not overwrite existing ones
388 * @return void
389 */
390function single_update($tablename, $set_fields, $where_fields, $flags=0)
391{
392  if (count($set_fields) == 0)
393  {
394    return;
395  }
396
397  $query = '
398UPDATE '.$tablename.'
399  SET ';
400  $is_first = true;
401  foreach ($set_fields as $key => $value)
402  {
403    $separator = $is_first ? '' : ",\n    ";
404
405    if (isset($value) and $value !== '')
406    {
407      $query.= $separator.$key.' = \''.$value.'\'';
408    }
409    else
410    {
411      if ( $flags & MASS_UPDATES_SKIP_EMPTY )
412        continue; // next field
413      $query.= "$separator$key = NULL";
414    }
415    $is_first = false;
416  }
417  if (!$is_first)
418  {// only if one field at least updated
419    $query.= '
420  WHERE ';
421    $is_first = true;
422    foreach ($where_fields as $key => $value)
423    {
424      if (!$is_first)
425      {
426        $query.= ' AND ';
427      }
428      if ( isset($value) )
429      {
430        $query.= $key.' = \''.$value.'\'';
431      }
432      else
433      {
434        $query.= $key.' IS NULL';
435      }
436      $is_first = false;
437    }
438    pwg_query($query);
439  }
440}
441
442
443/**
444 * inserts multiple lines in a table
445 *
446 * @param string table_name
447 * @param array dbfields
448 * @param array inserts
449 * @return void
450 */
451function mass_inserts($table_name, $dbfields, $datas, $options=array())
452{
453  $ignore = '';
454  if (isset($options['ignore']) and $options['ignore'])
455  {
456    $ignore = 'IGNORE';
457  }
458 
459  if (count($datas) != 0)
460  {
461    $first = true;
462
463    $query = 'SHOW VARIABLES LIKE \'max_allowed_packet\'';
464    list(, $packet_size) = pwg_db_fetch_row(pwg_query($query));
465    $packet_size = $packet_size - 2000; // The last list of values MUST not exceed 2000 character*/
466    $query = '';
467
468    foreach ($datas as $insert)
469    {
470      if (strlen($query) >= $packet_size)
471      {
472        pwg_query($query);
473        $first = true;
474      }
475
476      if ($first)
477      {
478        $query = '
479INSERT '.$ignore.' INTO '.$table_name.'
480  ('.implode(',', $dbfields).')
481  VALUES';
482        $first = false;
483      }
484      else
485      {
486        $query .= '
487  , ';
488      }
489
490      $query .= '(';
491      foreach ($dbfields as $field_id => $dbfield)
492      {
493        if ($field_id > 0)
494        {
495          $query .= ',';
496        }
497
498        if (!isset($insert[$dbfield]) or $insert[$dbfield] === '')
499        {
500          $query .= 'NULL';
501        }
502        else
503        {
504          $query .= "'".$insert[$dbfield]."'";
505        }
506      }
507      $query .= ')';
508    }
509    pwg_query($query);
510  }
511}
512
513/**
514 * inserts one line in a table
515 *
516 * @param string table_name
517 * @param array dbfields
518 * @param array insert
519 * @return void
520 */
521function single_insert($table_name, $data)
522{
523  if (count($data) != 0)
524  {
525    $query = '
526INSERT INTO '.$table_name.'
527  ('.implode(',', array_keys($data)).')
528  VALUES';
529
530    $query .= '(';
531    $is_first = true;
532    foreach ($data as $key => $value)
533    {
534      if (!$is_first)
535      {
536        $query .= ',';
537      }
538      else
539      {
540        $is_first = false;
541      }
542     
543      if ($value === '')
544      {
545        $query .= 'NULL';
546      }
547      else
548      {
549        $query .= "'".$value."'";
550      }
551    }
552    $query .= ')';
553   
554    pwg_query($query);
555  }
556}
557
558/**
559 * Do maintenance on all PWG tables
560 *
561 * @return none
562 */
563function do_maintenance_all_tables()
564{
565  global $prefixeTable, $page;
566
567  $all_tables = array();
568
569  // List all tables
570  $query = 'SHOW TABLES LIKE \''.$prefixeTable.'%\'';
571  $result = pwg_query($query);
572  while ($row = pwg_db_fetch_row($result))
573  {
574    $all_tables[] = $row[0];
575  }
576
577  // Repair all tables
578  $query = 'REPAIR TABLE '.implode(', ', $all_tables);
579  $mysqli_rc = pwg_query($query);
580
581  // Re-Order all tables
582  foreach ($all_tables as $table_name)
583  {
584    $all_primary_key = array();
585
586    $query = 'DESC '.$table_name.';';
587    $result = pwg_query($query);
588    while ($row = pwg_db_fetch_assoc($result))
589    {
590      if ($row['Key'] == 'PRI')
591      {
592        $all_primary_key[] = $row['Field'];
593      }
594    }
595
596    if (count($all_primary_key) != 0)
597    {
598      $query = 'ALTER TABLE '.$table_name.' ORDER BY '.implode(', ', $all_primary_key).';';
599      $mysqli_rc = $mysqli_rc && pwg_query($query);
600    }
601  }
602
603  // Optimize all tables
604  $query = 'OPTIMIZE TABLE '.implode(', ', $all_tables);
605  $mysqli_rc = $mysqli_rc && pwg_query($query);
606  if ($mysqli_rc)
607  {
608    $page['infos'][] = l10n('All optimizations have been successfully completed.');
609  }
610  else
611  {
612    $page['errors'][] = l10n('Optimizations have been completed with some errors.');
613  }
614}
615
616function pwg_db_concat($array)
617{
618  $string = implode($array, ',');
619  return 'CONCAT('. $string.')';
620}
621
622function pwg_db_concat_ws($array, $separator)
623{
624  $string = implode($array, ',');
625  return 'CONCAT_WS(\''.$separator.'\','. $string.')';
626}
627
628function pwg_db_cast_to_text($string)
629{
630  return $string;
631}
632
633/**
634 * returns an array containing the possible values of an enum field
635 *
636 * @param string tablename
637 * @param string fieldname
638 */
639function get_enums($table, $field)
640{
641  // retrieving the properties of the table. Each line represents a field :
642  // columns are 'Field', 'Type'
643  $result = pwg_query('desc '.$table);
644  while ($row = pwg_db_fetch_assoc($result))
645  {
646    // we are only interested in the the field given in parameter for the
647    // function
648    if ($row['Field'] == $field)
649    {
650      // retrieving possible values of the enum field
651      // enum('blue','green','black')
652      $options = explode(',', substr($row['Type'], 5, -1));
653      foreach ($options as $i => $option)
654      {
655        $options[$i] = str_replace("'", '',$option);
656      }
657    }
658  }
659  pwg_db_free_result($result);
660  return $options;
661}
662
663/**
664 * Smartly checks if a variable is equivalent to true or false
665 *
666 * @param mixed input
667 * @return bool
668 */
669function get_boolean($input)
670{
671  if ('false' === strtolower($input))
672  {
673    return false;
674  }
675
676  return (bool)$input;
677}
678
679/**
680 * returns boolean string 'true' or 'false' if the given var is boolean
681 *
682 * @param mixed $var
683 * @return mixed
684 */
685function boolean_to_string($var)
686{
687  if (is_bool($var))
688  {
689    return $var ? 'true' : 'false';
690  }
691  else
692  {
693    return $var;
694  }
695}
696
697/**
698 *
699 * interval and date functions
700 *
701 */
702
703function pwg_db_get_recent_period_expression($period, $date='CURRENT_DATE')
704{
705  if ($date!='CURRENT_DATE')
706  {
707    $date = '\''.$date.'\'';
708  }
709
710  return 'SUBDATE('.$date.',INTERVAL '.$period.' DAY)';
711}
712
713function pwg_db_get_recent_period($period, $date='CURRENT_DATE')
714{
715  $query = '
716SELECT '.pwg_db_get_recent_period_expression($period);
717  list($d) = pwg_db_fetch_row(pwg_query($query));
718
719  return $d;
720}
721
722function pwg_db_get_flood_period_expression($seconds)
723{
724  return 'SUBDATE(now(), INTERVAL '.$seconds.' SECOND)';
725}
726
727function pwg_db_get_hour($date) 
728{
729  return 'hour('.$date.')';
730}
731
732function pwg_db_get_date_YYYYMM($date)
733{
734  return 'DATE_FORMAT('.$date.', \'%Y%m\')';
735}
736
737function pwg_db_get_date_MMDD($date)
738{
739  return 'DATE_FORMAT('.$date.', \'%m%d\')';
740}
741
742function pwg_db_get_year($date)
743{
744  return 'YEAR('.$date.')';
745}
746
747function pwg_db_get_month($date)
748{
749  return 'MONTH('.$date.')';
750}
751
752function pwg_db_get_week($date, $mode=null)
753{
754  if ($mode)
755  {
756    return 'WEEK('.$date.', '.$mode.')';
757  }
758  else
759  {
760    return 'WEEK('.$date.')';
761  }
762}
763
764function pwg_db_get_dayofmonth($date)
765{
766  return 'DAYOFMONTH('.$date.')';
767}
768
769function pwg_db_get_dayofweek($date)
770{
771  return 'DAYOFWEEK('.$date.')';
772}
773
774function pwg_db_get_weekday($date)
775{
776  return 'WEEKDAY('.$date.')';
777}
778
779function pwg_db_date_to_ts($date) 
780{
781  return 'UNIX_TIMESTAMP('.$date.')';
782}
783
784// my_error returns (or send to standard output) the message concerning the
785// error occured for the last mysql query.
786function my_error($header, $die)
787{
788  global $mysqli;
789 
790  $error = "[mysql error ".$mysqli->errno.'] '.$mysqli->error."\n";
791  $error .= $header;
792
793  if ($die)
794  {
795    fatal_error($error);
796  }
797  echo("<pre>");
798  trigger_error($error, E_USER_WARNING);
799  echo("</pre>");
800}
801
802/**
803 * Builds an data array from a SQL query.
804 * Depending on $key_name and $value_name it can return :
805 *
806 *    - an array of arrays of all fields (key=null, value=null)
807 *        array(
808 *          array('id'=>1, 'name'=>'DSC8956', ...),
809 *          array('id'=>2, 'name'=>'DSC8957', ...),
810 *          ...
811 *          )
812 *
813 *    - an array of a single field (key=null, value='...')
814 *        array('DSC8956', 'DSC8957', ...)
815 *
816 *    - an associative array of array of all fields (key='...', value=null)
817 *        array(
818 *          'DSC8956' => array('id'=>1, 'name'=>'DSC8956', ...),
819 *          'DSC8957' => array('id'=>2, 'name'=>'DSC8957', ...),
820 *          ...
821 *          )
822 *
823 *    - an associative array of a single field (key='...', value='...')
824 *        array(
825 *          'DSC8956' => 1,
826 *          'DSC8957' => 2,
827 *          ...
828 *          )
829 *
830 * @since 2.6
831 *
832 * @param string $query
833 * @param string $key_name
834 * @param string $value_name
835 * @return array
836 */
837function query2array($query, $key_name=null, $value_name=null)
838{
839  $result = pwg_query($query);
840  $data = array();
841
842  if (isset($key_name))
843  {
844    if (isset($value_name))
845    {
846      while ($row = $result->fetch_assoc())
847        $data[ $row[$key_name] ] = $row[$value_name];
848    }
849    else
850    {
851      while ($row = $result->fetch_assoc())
852        $data[ $row[$key_name] ] = $row;
853    }
854  }
855  else
856  {
857    if (isset($value_name))
858    {
859      while ($row = $result->fetch_assoc())
860        $data[] = $row[$value_name];
861    }
862    else
863    {
864      while ($row = $result->fetch_assoc())
865        $data[] = $row;
866    }
867  }
868
869  return $data;
870}
871
872?>
Note: See TracBrowser for help on using the repository browser.