source: branches/2.3/include/dblayer/functions_mysql.inc.php @ 12747

Last change on this file since 12747 was 12747, checked in by plg, 9 years ago

bug 2534 fixed: clean (as clean as possible with MySQL+MyISAM) handle of
concurrency on user cache refresh. No more error when regenerating several
thumbnails at once.

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