[ Index ]

PHP Cross Reference of phpBB-3.3.0-deutsch

title

Body

[close]

/phpbb/db/driver/ -> driver.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  namespace phpbb\db\driver;
  15  
  16  /**
  17  * Database Abstraction Layer
  18  */
  19  abstract class driver implements driver_interface
  20  {
  21      var $db_connect_id;
  22      var $query_result;
  23      var $return_on_error = false;
  24      var $transaction = false;
  25      var $sql_time = 0;
  26      var $num_queries = array();
  27      var $open_queries = array();
  28  
  29      var $curtime = 0;
  30      var $query_hold = '';
  31      var $html_hold = '';
  32      var $sql_report = '';
  33  
  34      var $persistency = false;
  35      var $user = '';
  36      var $server = '';
  37      var $dbname = '';
  38  
  39      // Set to true if error triggered
  40      var $sql_error_triggered = false;
  41  
  42      // Holding the last sql query on sql error
  43      var $sql_error_sql = '';
  44      // Holding the error information - only populated if sql_error_triggered is set
  45      var $sql_error_returned = array();
  46  
  47      // Holding transaction count
  48      var $transactions = 0;
  49  
  50      // Supports multi inserts?
  51      var $multi_insert = false;
  52  
  53      /**
  54      * Current sql layer
  55      */
  56      var $sql_layer = '';
  57  
  58      /**
  59      * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions
  60      */
  61      var $any_char;
  62      var $one_char;
  63  
  64      /**
  65      * Exact version of the DBAL, directly queried
  66      */
  67      var $sql_server_version = false;
  68  
  69      const LOGICAL_OP = 0;
  70      const STATEMENTS = 1;
  71      const LEFT_STMT = 0;
  72      const COMPARE_OP = 1;
  73      const RIGHT_STMT = 2;
  74      const SUBQUERY_OP = 3;
  75      const SUBQUERY_SELECT_TYPE = 4;
  76      const SUBQUERY_BUILD = 5;
  77  
  78      /**
  79      * @var bool
  80      */
  81      protected $debug_load_time = false;
  82  
  83      /**
  84      * @var bool
  85      */
  86      protected $debug_sql_explain = false;
  87  
  88      /**
  89      * Constructor
  90      */
  91  	function __construct()
  92      {
  93          $this->num_queries = array(
  94              'cached'    => 0,
  95              'normal'    => 0,
  96              'total'        => 0,
  97          );
  98  
  99          // Fill default sql layer based on the class being called.
 100          // This can be changed by the specified layer itself later if needed.
 101          $this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\'));
 102  
 103          // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
 104          $this->any_char = chr(0) . '%';
 105          $this->one_char = chr(0) . '_';
 106      }
 107  
 108      /**
 109      * {@inheritdoc}
 110      */
 111  	public function set_debug_load_time($value)
 112      {
 113          $this->debug_load_time = $value;
 114      }
 115  
 116      /**
 117      * {@inheritdoc}
 118      */
 119  	public function set_debug_sql_explain($value)
 120      {
 121          $this->debug_sql_explain = $value;
 122      }
 123  
 124      /**
 125      * {@inheritdoc}
 126      */
 127  	public function get_sql_layer()
 128      {
 129          return $this->sql_layer;
 130      }
 131  
 132      /**
 133      * {@inheritdoc}
 134      */
 135  	public function get_db_name()
 136      {
 137          return $this->dbname;
 138      }
 139  
 140      /**
 141      * {@inheritdoc}
 142      */
 143  	public function get_any_char()
 144      {
 145          return $this->any_char;
 146      }
 147  
 148      /**
 149      * {@inheritdoc}
 150      */
 151  	public function get_one_char()
 152      {
 153          return $this->one_char;
 154      }
 155  
 156      /**
 157      * {@inheritdoc}
 158      */
 159  	public function get_db_connect_id()
 160      {
 161          return $this->db_connect_id;
 162      }
 163  
 164      /**
 165      * {@inheritdoc}
 166      */
 167  	public function get_sql_error_triggered()
 168      {
 169          return $this->sql_error_triggered;
 170      }
 171  
 172      /**
 173      * {@inheritdoc}
 174      */
 175  	public function get_sql_error_sql()
 176      {
 177          return $this->sql_error_sql;
 178      }
 179  
 180      /**
 181      * {@inheritdoc}
 182      */
 183  	public function get_transaction()
 184      {
 185          return $this->transaction;
 186      }
 187  
 188      /**
 189      * {@inheritdoc}
 190      */
 191  	public function get_sql_time()
 192      {
 193          return $this->sql_time;
 194      }
 195  
 196      /**
 197      * {@inheritdoc}
 198      */
 199  	public function get_sql_error_returned()
 200      {
 201          return $this->sql_error_returned;
 202      }
 203  
 204      /**
 205      * {@inheritdoc}
 206      */
 207  	public function get_multi_insert()
 208      {
 209          return $this->multi_insert;
 210      }
 211  
 212      /**
 213      * {@inheritdoc}
 214      */
 215  	public function set_multi_insert($multi_insert)
 216      {
 217          $this->multi_insert = $multi_insert;
 218      }
 219  
 220      /**
 221      * {@inheritDoc}
 222      */
 223  	function sql_return_on_error($fail = false)
 224      {
 225          $this->sql_error_triggered = false;
 226          $this->sql_error_sql = '';
 227  
 228          $this->return_on_error = $fail;
 229      }
 230  
 231      /**
 232      * {@inheritDoc}
 233      */
 234  	function sql_num_queries($cached = false)
 235      {
 236          return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
 237      }
 238  
 239      /**
 240      * {@inheritDoc}
 241      */
 242  	function sql_add_num_queries($cached = false)
 243      {
 244          $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
 245          $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
 246          $this->num_queries['total'] += 1;
 247      }
 248  
 249      /**
 250      * {@inheritDoc}
 251      */
 252  	function sql_close()
 253      {
 254          if (!$this->db_connect_id)
 255          {
 256              return false;
 257          }
 258  
 259          if ($this->transaction)
 260          {
 261              do
 262              {
 263                  $this->sql_transaction('commit');
 264              }
 265              while ($this->transaction);
 266          }
 267  
 268          foreach ($this->open_queries as $query_id)
 269          {
 270              $this->sql_freeresult($query_id);
 271          }
 272  
 273          // Connection closed correctly. Set db_connect_id to false to prevent errors
 274          if ($result = $this->_sql_close())
 275          {
 276              $this->db_connect_id = false;
 277          }
 278  
 279          return $result;
 280      }
 281  
 282      /**
 283      * {@inheritDoc}
 284      */
 285  	function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
 286      {
 287          if (empty($query))
 288          {
 289              return false;
 290          }
 291  
 292          // Never use a negative total or offset
 293          $total = ($total < 0) ? 0 : $total;
 294          $offset = ($offset < 0) ? 0 : $offset;
 295  
 296          return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
 297      }
 298  
 299      /**
 300      * {@inheritDoc}
 301      */
 302  	function sql_fetchrowset($query_id = false)
 303      {
 304          if ($query_id === false)
 305          {
 306              $query_id = $this->query_result;
 307          }
 308  
 309          if ($query_id)
 310          {
 311              $result = array();
 312              while ($row = $this->sql_fetchrow($query_id))
 313              {
 314                  $result[] = $row;
 315              }
 316  
 317              return $result;
 318          }
 319  
 320          return false;
 321      }
 322  
 323      /**
 324      * {@inheritDoc}
 325      */
 326  	function sql_rowseek($rownum, &$query_id)
 327      {
 328          global $cache;
 329  
 330          if ($query_id === false)
 331          {
 332              $query_id = $this->query_result;
 333          }
 334  
 335          if ($cache && $cache->sql_exists($query_id))
 336          {
 337              return $cache->sql_rowseek($rownum, $query_id);
 338          }
 339  
 340          if (!$query_id)
 341          {
 342              return false;
 343          }
 344  
 345          $this->sql_freeresult($query_id);
 346          $query_id = $this->sql_query($this->last_query_text);
 347  
 348          if (!$query_id)
 349          {
 350              return false;
 351          }
 352  
 353          // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
 354          for ($i = 0; $i < $rownum; $i++)
 355          {
 356              if (!$this->sql_fetchrow($query_id))
 357              {
 358                  return false;
 359              }
 360          }
 361  
 362          return true;
 363      }
 364  
 365      /**
 366      * {@inheritDoc}
 367      */
 368  	function sql_fetchfield($field, $rownum = false, $query_id = false)
 369      {
 370          global $cache;
 371  
 372          if ($query_id === false)
 373          {
 374              $query_id = $this->query_result;
 375          }
 376  
 377          if ($query_id)
 378          {
 379              if ($rownum !== false)
 380              {
 381                  $this->sql_rowseek($rownum, $query_id);
 382              }
 383  
 384              if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
 385              {
 386                  return $cache->sql_fetchfield($query_id, $field);
 387              }
 388  
 389              $row = $this->sql_fetchrow($query_id);
 390              return (isset($row[$field])) ? $row[$field] : false;
 391          }
 392  
 393          return false;
 394      }
 395  
 396      /**
 397      * {@inheritDoc}
 398      */
 399  	function sql_like_expression($expression)
 400      {
 401          $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
 402          $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
 403  
 404          return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
 405      }
 406  
 407      /**
 408      * {@inheritDoc}
 409      */
 410  	function sql_not_like_expression($expression)
 411      {
 412          $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
 413          $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
 414  
 415          return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
 416      }
 417  
 418      /**
 419      * {@inheritDoc}
 420      */
 421  	public function sql_case($condition, $action_true, $action_false = false)
 422      {
 423          $sql_case = 'CASE WHEN ' . $condition;
 424          $sql_case .= ' THEN ' . $action_true;
 425          $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
 426          $sql_case .= ' END';
 427          return $sql_case;
 428      }
 429  
 430      /**
 431      * {@inheritDoc}
 432      */
 433  	public function sql_concatenate($expr1, $expr2)
 434      {
 435          return $expr1 . ' || ' . $expr2;
 436      }
 437  
 438      /**
 439      * {@inheritDoc}
 440      */
 441  	function sql_buffer_nested_transactions()
 442      {
 443          return false;
 444      }
 445  
 446      /**
 447      * {@inheritDoc}
 448      */
 449  	function sql_transaction($status = 'begin')
 450      {
 451          switch ($status)
 452          {
 453              case 'begin':
 454                  // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
 455                  if ($this->transaction)
 456                  {
 457                      $this->transactions++;
 458                      return true;
 459                  }
 460  
 461                  $result = $this->_sql_transaction('begin');
 462  
 463                  if (!$result)
 464                  {
 465                      $this->sql_error();
 466                  }
 467  
 468                  $this->transaction = true;
 469              break;
 470  
 471              case 'commit':
 472                  // If there was a previously opened transaction we do not commit yet...
 473                  // but count back the number of inner transactions
 474                  if ($this->transaction && $this->transactions)
 475                  {
 476                      $this->transactions--;
 477                      return true;
 478                  }
 479  
 480                  // Check if there is a transaction (no transaction can happen if
 481                  // there was an error, with a combined rollback and error returning enabled)
 482                  // This implies we have transaction always set for autocommit db's
 483                  if (!$this->transaction)
 484                  {
 485                      return false;
 486                  }
 487  
 488                  $result = $this->_sql_transaction('commit');
 489  
 490                  if (!$result)
 491                  {
 492                      $this->sql_error();
 493                  }
 494  
 495                  $this->transaction = false;
 496                  $this->transactions = 0;
 497              break;
 498  
 499              case 'rollback':
 500                  $result = $this->_sql_transaction('rollback');
 501                  $this->transaction = false;
 502                  $this->transactions = 0;
 503              break;
 504  
 505              default:
 506                  $result = $this->_sql_transaction($status);
 507              break;
 508          }
 509  
 510          return $result;
 511      }
 512  
 513      /**
 514      * {@inheritDoc}
 515      */
 516  	function sql_build_array($query, $assoc_ary = false)
 517      {
 518          if (!is_array($assoc_ary))
 519          {
 520              return false;
 521          }
 522  
 523          $fields = $values = array();
 524  
 525          if ($query == 'INSERT' || $query == 'INSERT_SELECT')
 526          {
 527              foreach ($assoc_ary as $key => $var)
 528              {
 529                  $fields[] = $key;
 530  
 531                  if (is_array($var) && is_string($var[0]))
 532                  {
 533                      // This is used for INSERT_SELECT(s)
 534                      $values[] = $var[0];
 535                  }
 536                  else
 537                  {
 538                      $values[] = $this->_sql_validate_value($var);
 539                  }
 540              }
 541  
 542              $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
 543          }
 544          else if ($query == 'MULTI_INSERT')
 545          {
 546              trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
 547          }
 548          else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
 549          {
 550              $values = array();
 551              foreach ($assoc_ary as $key => $var)
 552              {
 553                  $values[] = "$key = " . $this->_sql_validate_value($var);
 554              }
 555              $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
 556          }
 557  
 558          return $query;
 559      }
 560  
 561      /**
 562      * {@inheritDoc}
 563      */
 564  	function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
 565      {
 566          $array = (array) $array;
 567  
 568          if (!count($array))
 569          {
 570              if (!$allow_empty_set)
 571              {
 572                  // Print the backtrace to help identifying the location of the problematic code
 573                  $this->sql_error('No values specified for SQL IN comparison');
 574              }
 575              else
 576              {
 577                  // NOT IN () actually means everything so use a tautology
 578                  if ($negate)
 579                  {
 580                      return '1=1';
 581                  }
 582                  // IN () actually means nothing so use a contradiction
 583                  else
 584                  {
 585                      return '1=0';
 586                  }
 587              }
 588          }
 589  
 590          if (count($array) == 1)
 591          {
 592              @reset($array);
 593              $var = current($array);
 594  
 595              return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
 596          }
 597          else
 598          {
 599              return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
 600          }
 601      }
 602  
 603      /**
 604      * {@inheritDoc}
 605      */
 606  	function sql_bit_and($column_name, $bit, $compare = '')
 607      {
 608          if (method_exists($this, '_sql_bit_and'))
 609          {
 610              return $this->_sql_bit_and($column_name, $bit, $compare);
 611          }
 612  
 613          return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
 614      }
 615  
 616      /**
 617      * {@inheritDoc}
 618      */
 619  	function sql_bit_or($column_name, $bit, $compare = '')
 620      {
 621          if (method_exists($this, '_sql_bit_or'))
 622          {
 623              return $this->_sql_bit_or($column_name, $bit, $compare);
 624          }
 625  
 626          return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
 627      }
 628  
 629      /**
 630      * {@inheritDoc}
 631      */
 632  	function cast_expr_to_bigint($expression)
 633      {
 634          return $expression;
 635      }
 636  
 637      /**
 638      * {@inheritDoc}
 639      */
 640  	function cast_expr_to_string($expression)
 641      {
 642          return $expression;
 643      }
 644  
 645      /**
 646      * {@inheritDoc}
 647      */
 648  	function sql_lower_text($column_name)
 649      {
 650          return "LOWER($column_name)";
 651      }
 652  
 653      /**
 654      * {@inheritDoc}
 655      */
 656  	function sql_multi_insert($table, $sql_ary)
 657      {
 658          if (!count($sql_ary))
 659          {
 660              return false;
 661          }
 662  
 663          if ($this->multi_insert)
 664          {
 665              $ary = array();
 666              foreach ($sql_ary as $id => $_sql_ary)
 667              {
 668                  // If by accident the sql array is only one-dimensional we build a normal insert statement
 669                  if (!is_array($_sql_ary))
 670                  {
 671                      return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
 672                  }
 673  
 674                  $values = array();
 675                  foreach ($_sql_ary as $key => $var)
 676                  {
 677                      $values[] = $this->_sql_validate_value($var);
 678                  }
 679                  $ary[] = '(' . implode(', ', $values) . ')';
 680              }
 681  
 682              return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
 683          }
 684          else
 685          {
 686              foreach ($sql_ary as $ary)
 687              {
 688                  if (!is_array($ary))
 689                  {
 690                      return false;
 691                  }
 692  
 693                  $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
 694  
 695                  if (!$result)
 696                  {
 697                      return false;
 698                  }
 699              }
 700          }
 701  
 702          return true;
 703      }
 704  
 705      /**
 706      * Function for validating values
 707      * @access private
 708      */
 709  	function _sql_validate_value($var)
 710      {
 711          if (is_null($var))
 712          {
 713              return 'NULL';
 714          }
 715          else if (is_string($var))
 716          {
 717              return "'" . $this->sql_escape($var) . "'";
 718          }
 719          else
 720          {
 721              return (is_bool($var)) ? intval($var) : $var;
 722          }
 723      }
 724  
 725      /**
 726      * {@inheritDoc}
 727      */
 728  	function sql_build_query($query, $array)
 729      {
 730          $sql = '';
 731          switch ($query)
 732          {
 733              case 'SELECT':
 734              case 'SELECT_DISTINCT';
 735  
 736                  $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
 737  
 738                  // Build table array. We also build an alias array for later checks.
 739                  $table_array = $aliases = array();
 740                  $used_multi_alias = false;
 741  
 742                  foreach ($array['FROM'] as $table_name => $alias)
 743                  {
 744                      if (is_array($alias))
 745                      {
 746                          $used_multi_alias = true;
 747  
 748                          foreach ($alias as $multi_alias)
 749                          {
 750                              $table_array[] = $table_name . ' ' . $multi_alias;
 751                              $aliases[] = $multi_alias;
 752                          }
 753                      }
 754                      else
 755                      {
 756                          $table_array[] = $table_name . ' ' . $alias;
 757                          $aliases[] = $alias;
 758                      }
 759                  }
 760  
 761                  // We run the following code to determine if we need to re-order the table array. ;)
 762                  // The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison.
 763                  // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
 764                  if (!empty($array['LEFT_JOIN']) && count($array['FROM']) > 1 && $used_multi_alias !== false)
 765                  {
 766                      // Take first LEFT JOIN
 767                      $join = current($array['LEFT_JOIN']);
 768  
 769                      // Determine the table used there (even if there are more than one used, we only want to have one
 770                      preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
 771  
 772                      // If there is a first join match, we need to make sure the table order is correct
 773                      if (!empty($matches[1]))
 774                      {
 775                          $first_join_match = trim($matches[1]);
 776                          $table_array = $last = array();
 777  
 778                          foreach ($array['FROM'] as $table_name => $alias)
 779                          {
 780                              if (is_array($alias))
 781                              {
 782                                  foreach ($alias as $multi_alias)
 783                                  {
 784                                      ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
 785                                  }
 786                              }
 787                              else
 788                              {
 789                                  ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
 790                              }
 791                          }
 792  
 793                          $table_array = array_merge($table_array, $last);
 794                      }
 795                  }
 796  
 797                  $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
 798  
 799                  if (!empty($array['LEFT_JOIN']))
 800                  {
 801                      foreach ($array['LEFT_JOIN'] as $join)
 802                      {
 803                          $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
 804                      }
 805                  }
 806  
 807                  if (!empty($array['WHERE']))
 808                  {
 809                      $sql .= ' WHERE ';
 810  
 811                      if (is_array($array['WHERE']))
 812                      {
 813                          $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
 814                      }
 815                      else
 816                      {
 817                          $sql_where = $array['WHERE'];
 818                      }
 819  
 820                      $sql .= $this->_sql_custom_build('WHERE', $sql_where);
 821                  }
 822  
 823                  if (!empty($array['GROUP_BY']))
 824                  {
 825                      $sql .= ' GROUP BY ' . $array['GROUP_BY'];
 826                  }
 827  
 828                  if (!empty($array['ORDER_BY']))
 829                  {
 830                      $sql .= ' ORDER BY ' . $array['ORDER_BY'];
 831                  }
 832  
 833              break;
 834          }
 835  
 836          return $sql;
 837      }
 838  
 839  
 840  	protected function _process_boolean_tree_first($operations_ary)
 841      {
 842          // In cases where an array exists but there is no head condition,
 843          // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
 844          if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
 845              $operations_ary[self::LOGICAL_OP] !== 'OR')
 846          {
 847              $operations_ary = array('AND', array($operations_ary));
 848          }
 849          return $this->_process_boolean_tree($operations_ary) . "\n";
 850      }
 851  
 852  	protected function _process_boolean_tree($operations_ary)
 853      {
 854          $operation = $operations_ary[self::LOGICAL_OP];
 855  
 856          foreach ($operations_ary[self::STATEMENTS] as &$condition)
 857          {
 858              switch ($condition[self::LOGICAL_OP])
 859              {
 860                  case 'AND':
 861                  case 'OR':
 862  
 863                      $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
 864  
 865                  break;
 866                  case 'NOT':
 867  
 868                      $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
 869  
 870                  break;
 871  
 872                  default:
 873  
 874                      switch (count($condition))
 875                      {
 876                          case 3:
 877  
 878                              // Typical 3 element clause with {left hand} {operator} {right hand}
 879                              switch ($condition[self::COMPARE_OP])
 880                              {
 881                                  case 'IN':
 882                                  case 'NOT_IN':
 883  
 884                                      // As this is used with an IN, assume it is a set of elements for sql_in_set()
 885                                      $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
 886  
 887                                  break;
 888  
 889                                  case 'LIKE':
 890  
 891                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
 892  
 893                                  break;
 894  
 895                                  case 'NOT_LIKE':
 896  
 897                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
 898  
 899                                  break;
 900  
 901                                  case 'IS_NOT':
 902  
 903                                      $condition[self::COMPARE_OP] = 'IS NOT';
 904  
 905                                  // no break
 906                                  case 'IS':
 907  
 908                                      // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
 909                                      // this should solve that
 910                                      if ($condition[self::RIGHT_STMT] === null)
 911                                      {
 912                                          $condition[self::RIGHT_STMT] = 'NULL';
 913                                      }
 914  
 915                                      $condition = implode(' ', $condition);
 916  
 917                                  break;
 918  
 919                                  default:
 920  
 921                                      $condition = implode(' ', $condition);
 922  
 923                                  break;
 924                              }
 925  
 926                          break;
 927  
 928                          case 5:
 929  
 930                              // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
 931  
 932                              $result = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
 933                              $result .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
 934                              $result .= ' )';
 935                              $condition = $result;
 936  
 937                          break;
 938  
 939                          default:
 940                              // This is an unpredicted clause setup. Just join all elements.
 941                              $condition = implode(' ', $condition);
 942  
 943                          break;
 944                      }
 945  
 946                  break;
 947              }
 948  
 949          }
 950  
 951          if ($operation === 'NOT')
 952          {
 953              $operations_ary =  implode("", $operations_ary[self::STATEMENTS]);
 954          }
 955          else
 956          {
 957              $operations_ary = implode(" \n    $operation ", $operations_ary[self::STATEMENTS]);
 958          }
 959  
 960          return $operations_ary;
 961      }
 962  
 963  
 964      /**
 965      * {@inheritDoc}
 966      */
 967  	function sql_error($sql = '')
 968      {
 969          global $auth, $user, $config;
 970  
 971          // Set var to retrieve errored status
 972          $this->sql_error_triggered = true;
 973          $this->sql_error_sql = $sql;
 974  
 975          $this->sql_error_returned = $this->_sql_error();
 976  
 977          if (!$this->return_on_error)
 978          {
 979              $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
 980  
 981              // Show complete SQL error and path to administrators only
 982              // Additionally show complete error on installation or if extended debug mode is enabled
 983              // The DEBUG constant is for development only!
 984              if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || $this->debug_sql_explain)
 985              {
 986                  $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
 987              }
 988              else
 989              {
 990                  // If error occurs in initiating the session we need to use a pre-defined language string
 991                  // This could happen if the connection could not be established for example (then we are not able to grab the default language)
 992                  if (!isset($user->lang['SQL_ERROR_OCCURRED']))
 993                  {
 994                      $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
 995                  }
 996                  else
 997                  {
 998                      if (!empty($config['board_contact']))
 999                      {
1000                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
1001                      }
1002                      else
1003                      {
1004                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
1005                      }
1006                  }
1007              }
1008  
1009              if ($this->transaction)
1010              {
1011                  $this->sql_transaction('rollback');
1012              }
1013  
1014              if (strlen($message) > 1024)
1015              {
1016                  // We need to define $msg_long_text here to circumvent text stripping.
1017                  global $msg_long_text;
1018                  $msg_long_text = $message;
1019  
1020                  trigger_error(false, E_USER_ERROR);
1021              }
1022  
1023              trigger_error($message, E_USER_ERROR);
1024          }
1025  
1026          if ($this->transaction)
1027          {
1028              $this->sql_transaction('rollback');
1029          }
1030  
1031          return $this->sql_error_returned;
1032      }
1033  
1034      /**
1035      * {@inheritDoc}
1036      */
1037  	function sql_report($mode, $query = '')
1038      {
1039          global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
1040          global $request;
1041  
1042          if (is_object($request) && !$request->variable('explain', false))
1043          {
1044              return false;
1045          }
1046  
1047          if (!$query && $this->query_hold != '')
1048          {
1049              $query = $this->query_hold;
1050          }
1051  
1052          switch ($mode)
1053          {
1054              case 'display':
1055                  if (!empty($cache))
1056                  {
1057                      $cache->unload();
1058                  }
1059                  $this->sql_close();
1060  
1061                  $mtime = explode(' ', microtime());
1062                  $totaltime = $mtime[0] + $mtime[1] - $starttime;
1063  
1064                  echo '<!DOCTYPE html>
1065                      <html dir="ltr">
1066                      <head>
1067                          <meta charset="utf-8">
1068                          <meta http-equiv="X-UA-Compatible" content="IE=edge">
1069                          <title>SQL Report</title>
1070                          <link href="' . htmlspecialchars($phpbb_path_helper->update_web_root_path($phpbb_root_path) . $phpbb_path_helper->get_adm_relative_path()) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" />
1071                      </head>
1072                      <body id="errorpage">
1073                      <div id="wrap">
1074                          <div id="page-header">
1075                              <a href="' . build_url('explain') . '">Return to previous page</a>
1076                          </div>
1077                          <div id="page-body">
1078                              <div id="acp">
1079                              <div class="panel">
1080                                  <span class="corners-top"><span></span></span>
1081                                  <div id="content">
1082                                      <h1>SQL Report</h1>
1083                                      <br />
1084                                      <p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" . (($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></p>
1085  
1086                                      <p>Time spent on ' . $this->sql_layer . ' queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p>
1087  
1088                                      <br /><br />
1089                                      ' . $this->sql_report . '
1090                                  </div>
1091                                  <span class="corners-bottom"><span></span></span>
1092                              </div>
1093                              </div>
1094                          </div>
1095                          <div id="page-footer">
1096                              Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
1097                          </div>
1098                      </div>
1099                      </body>
1100                      </html>';
1101  
1102                  exit_handler();
1103  
1104              break;
1105  
1106              case 'stop':
1107                  $endtime = explode(' ', microtime());
1108                  $endtime = $endtime[0] + $endtime[1];
1109  
1110                  $this->sql_report .= '
1111  
1112                      <table cellspacing="1">
1113                      <thead>
1114                      <tr>
1115                          <th>Query #' . $this->num_queries['total'] . '</th>
1116                      </tr>
1117                      </thead>
1118                      <tbody>
1119                      <tr>
1120                          <td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td>
1121                      </tr>
1122                      </tbody>
1123                      </table>
1124  
1125                      ' . $this->html_hold . '
1126  
1127                      <p style="text-align: center;">
1128                  ';
1129  
1130                  if ($this->query_result)
1131                  {
1132                      if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
1133                      {
1134                          $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
1135                      }
1136                      $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>';
1137                  }
1138                  else
1139                  {
1140                      $error = $this->sql_error();
1141                      $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
1142                  }
1143  
1144                  $this->sql_report .= '</p><br /><br />';
1145  
1146                  $this->sql_time += $endtime - $this->curtime;
1147              break;
1148  
1149              case 'start':
1150                  $this->query_hold = $query;
1151                  $this->html_hold = '';
1152  
1153                  $this->_sql_report($mode, $query);
1154  
1155                  $this->curtime = explode(' ', microtime());
1156                  $this->curtime = $this->curtime[0] + $this->curtime[1];
1157  
1158              break;
1159  
1160              case 'add_select_row':
1161  
1162                  $html_table = func_get_arg(2);
1163                  $row = func_get_arg(3);
1164  
1165                  if (!$html_table && count($row))
1166                  {
1167                      $html_table = true;
1168                      $this->html_hold .= '<table cellspacing="1"><tr>';
1169  
1170                      foreach (array_keys($row) as $val)
1171                      {
1172                          $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
1173                      }
1174                      $this->html_hold .= '</tr>';
1175                  }
1176                  $this->html_hold .= '<tr>';
1177  
1178                  $class = 'row1';
1179                  foreach (array_values($row) as $val)
1180                  {
1181                      $class = ($class == 'row1') ? 'row2' : 'row1';
1182                      $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
1183                  }
1184                  $this->html_hold .= '</tr>';
1185  
1186                  return $html_table;
1187  
1188              break;
1189  
1190              case 'fromcache':
1191  
1192                  $this->_sql_report($mode, $query);
1193  
1194              break;
1195  
1196              case 'record_fromcache':
1197  
1198                  $endtime = func_get_arg(2);
1199                  $splittime = func_get_arg(3);
1200  
1201                  $time_cache = $endtime - $this->curtime;
1202                  $time_db = $splittime - $endtime;
1203                  $color = ($time_db > $time_cache) ? 'green' : 'red';
1204  
1205                  $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
1206                  $this->sql_report .= '<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></tbody></table>';
1207                  $this->sql_report .= '<p style="text-align: center;">';
1208                  $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />';
1209  
1210                  // Pad the start time to not interfere with page timing
1211                  $starttime += $time_db;
1212  
1213              break;
1214  
1215              default:
1216  
1217                  $this->_sql_report($mode, $query);
1218  
1219              break;
1220          }
1221  
1222          return true;
1223      }
1224  
1225      /**
1226      * {@inheritDoc}
1227      */
1228  	function get_estimated_row_count($table_name)
1229      {
1230          return $this->get_row_count($table_name);
1231      }
1232  
1233      /**
1234      * {@inheritDoc}
1235      */
1236  	function get_row_count($table_name)
1237      {
1238          $sql = 'SELECT COUNT(*) AS rows_total
1239              FROM ' . $this->sql_escape($table_name);
1240          $result = $this->sql_query($sql);
1241          $rows_total = $this->sql_fetchfield('rows_total');
1242          $this->sql_freeresult($result);
1243  
1244          return $rows_total;
1245      }
1246  }


Generated: Tue Apr 7 19:44:41 2020 Cross-referenced by PHPXref 0.7.1