source: trunk/include/dblayer/functions_mysql.inc.php @ 30140

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