[ Index ]

PHP Cross Reference of phpBB-3.3.14-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  	public function sql_nextid()
 641      {
 642          return $this->sql_last_inserted_id();
 643      }
 644  
 645      /**
 646      * {@inheritDoc}
 647      */
 648  	function cast_expr_to_string($expression)
 649      {
 650          return $expression;
 651      }
 652  
 653      /**
 654      * {@inheritDoc}
 655      */
 656  	function sql_lower_text($column_name)
 657      {
 658          return "LOWER($column_name)";
 659      }
 660  
 661      /**
 662      * {@inheritDoc}
 663      */
 664  	function sql_multi_insert($table, $sql_ary)
 665      {
 666          if (!count($sql_ary))
 667          {
 668              return false;
 669          }
 670  
 671          if ($this->multi_insert)
 672          {
 673              $ary = array();
 674              foreach ($sql_ary as $id => $_sql_ary)
 675              {
 676                  // If by accident the sql array is only one-dimensional we build a normal insert statement
 677                  if (!is_array($_sql_ary))
 678                  {
 679                      return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
 680                  }
 681  
 682                  $values = array();
 683                  foreach ($_sql_ary as $key => $var)
 684                  {
 685                      $values[] = $this->_sql_validate_value($var);
 686                  }
 687                  $ary[] = '(' . implode(', ', $values) . ')';
 688              }
 689  
 690              return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
 691          }
 692          else
 693          {
 694              foreach ($sql_ary as $ary)
 695              {
 696                  if (!is_array($ary))
 697                  {
 698                      return false;
 699                  }
 700  
 701                  $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
 702  
 703                  if (!$result)
 704                  {
 705                      return false;
 706                  }
 707              }
 708          }
 709  
 710          return true;
 711      }
 712  
 713      /**
 714      * Function for validating values
 715      * @access private
 716      */
 717  	function _sql_validate_value($var)
 718      {
 719          if (is_null($var))
 720          {
 721              return 'NULL';
 722          }
 723          else if (is_string($var))
 724          {
 725              return "'" . $this->sql_escape($var) . "'";
 726          }
 727          else
 728          {
 729              return (is_bool($var)) ? intval($var) : $var;
 730          }
 731      }
 732  
 733      /**
 734      * {@inheritDoc}
 735      */
 736  	function sql_build_query($query, $array)
 737      {
 738          $sql = '';
 739          switch ($query)
 740          {
 741              case 'SELECT':
 742              case 'SELECT_DISTINCT';
 743  
 744                  $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
 745  
 746                  // Build table array. We also build an alias array for later checks.
 747                  $table_array = $aliases = array();
 748                  $used_multi_alias = false;
 749  
 750                  foreach ($array['FROM'] as $table_name => $alias)
 751                  {
 752                      if (is_array($alias))
 753                      {
 754                          $used_multi_alias = true;
 755  
 756                          foreach ($alias as $multi_alias)
 757                          {
 758                              $table_array[] = $table_name . ' ' . $multi_alias;
 759                              $aliases[] = $multi_alias;
 760                          }
 761                      }
 762                      else
 763                      {
 764                          $table_array[] = $table_name . ' ' . $alias;
 765                          $aliases[] = $alias;
 766                      }
 767                  }
 768  
 769                  // We run the following code to determine if we need to re-order the table array. ;)
 770                  // 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.
 771                  // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
 772                  if (!empty($array['LEFT_JOIN']) && count($array['FROM']) > 1 && $used_multi_alias !== false)
 773                  {
 774                      // Take first LEFT JOIN
 775                      $join = current($array['LEFT_JOIN']);
 776  
 777                      // Determine the table used there (even if there are more than one used, we only want to have one
 778                      preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
 779  
 780                      // If there is a first join match, we need to make sure the table order is correct
 781                      if (!empty($matches[1]))
 782                      {
 783                          $first_join_match = trim($matches[1]);
 784                          $table_array = $last = array();
 785  
 786                          foreach ($array['FROM'] as $table_name => $alias)
 787                          {
 788                              if (is_array($alias))
 789                              {
 790                                  foreach ($alias as $multi_alias)
 791                                  {
 792                                      ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
 793                                  }
 794                              }
 795                              else
 796                              {
 797                                  ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
 798                              }
 799                          }
 800  
 801                          $table_array = array_merge($table_array, $last);
 802                      }
 803                  }
 804  
 805                  $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
 806  
 807                  if (!empty($array['LEFT_JOIN']))
 808                  {
 809                      foreach ($array['LEFT_JOIN'] as $join)
 810                      {
 811                          $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
 812                      }
 813                  }
 814  
 815                  if (!empty($array['WHERE']))
 816                  {
 817                      $sql .= ' WHERE ';
 818  
 819                      if (is_array($array['WHERE']))
 820                      {
 821                          $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
 822                      }
 823                      else
 824                      {
 825                          $sql_where = $array['WHERE'];
 826                      }
 827  
 828                      $sql .= $this->_sql_custom_build('WHERE', $sql_where);
 829                  }
 830  
 831                  if (!empty($array['GROUP_BY']))
 832                  {
 833                      $sql .= ' GROUP BY ' . $array['GROUP_BY'];
 834                  }
 835  
 836                  if (!empty($array['ORDER_BY']))
 837                  {
 838                      $sql .= ' ORDER BY ' . $array['ORDER_BY'];
 839                  }
 840  
 841              break;
 842          }
 843  
 844          return $sql;
 845      }
 846  
 847  
 848  	protected function _process_boolean_tree_first($operations_ary)
 849      {
 850          // In cases where an array exists but there is no head condition,
 851          // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
 852          if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
 853              $operations_ary[self::LOGICAL_OP] !== 'OR')
 854          {
 855              $operations_ary = array('AND', array($operations_ary));
 856          }
 857          return $this->_process_boolean_tree($operations_ary) . "\n";
 858      }
 859  
 860  	protected function _process_boolean_tree($operations_ary)
 861      {
 862          $operation = $operations_ary[self::LOGICAL_OP];
 863  
 864          foreach ($operations_ary[self::STATEMENTS] as &$condition)
 865          {
 866              switch ($condition[self::LOGICAL_OP])
 867              {
 868                  case 'AND':
 869                  case 'OR':
 870  
 871                      $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
 872  
 873                  break;
 874                  case 'NOT':
 875  
 876                      $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
 877  
 878                  break;
 879  
 880                  default:
 881  
 882                      switch (count($condition))
 883                      {
 884                          case 3:
 885  
 886                              // Typical 3 element clause with {left hand} {operator} {right hand}
 887                              switch ($condition[self::COMPARE_OP])
 888                              {
 889                                  case 'IN':
 890                                  case 'NOT_IN':
 891  
 892                                      // As this is used with an IN, assume it is a set of elements for sql_in_set()
 893                                      $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
 894  
 895                                  break;
 896  
 897                                  case 'LIKE':
 898  
 899                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
 900  
 901                                  break;
 902  
 903                                  case 'NOT_LIKE':
 904  
 905                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
 906  
 907                                  break;
 908  
 909                                  case 'IS_NOT':
 910  
 911                                      $condition[self::COMPARE_OP] = 'IS NOT';
 912  
 913                                  // no break
 914                                  case 'IS':
 915  
 916                                      // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
 917                                      // this should solve that
 918                                      if ($condition[self::RIGHT_STMT] === null)
 919                                      {
 920                                          $condition[self::RIGHT_STMT] = 'NULL';
 921                                      }
 922  
 923                                      $condition = implode(' ', $condition);
 924  
 925                                  break;
 926  
 927                                  default:
 928  
 929                                      $condition = implode(' ', $condition);
 930  
 931                                  break;
 932                              }
 933  
 934                          break;
 935  
 936                          case 5:
 937  
 938                              // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
 939  
 940                              $result = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
 941                              $result .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
 942                              $result .= ' )';
 943                              $condition = $result;
 944  
 945                          break;
 946  
 947                          default:
 948                              // This is an unpredicted clause setup. Just join all elements.
 949                              $condition = implode(' ', $condition);
 950  
 951                          break;
 952                      }
 953  
 954                  break;
 955              }
 956  
 957          }
 958  
 959          if ($operation === 'NOT')
 960          {
 961              $operations_ary =  implode("", $operations_ary[self::STATEMENTS]);
 962          }
 963          else
 964          {
 965              $operations_ary = implode(" \n    $operation ", $operations_ary[self::STATEMENTS]);
 966          }
 967  
 968          return $operations_ary;
 969      }
 970  
 971  
 972      /**
 973      * {@inheritDoc}
 974      */
 975  	function sql_error($sql = '')
 976      {
 977          global $auth, $user, $config;
 978  
 979          // Set var to retrieve errored status
 980          $this->sql_error_triggered = true;
 981          $this->sql_error_sql = $sql;
 982  
 983          $this->sql_error_returned = $this->_sql_error();
 984  
 985          if (!$this->return_on_error)
 986          {
 987              $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
 988  
 989              // Show complete SQL error and path to administrators only
 990              // Additionally show complete error on installation or if extended debug mode is enabled
 991              // The DEBUG constant is for development only!
 992              if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || $this->debug_sql_explain)
 993              {
 994                  $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql, ENT_COMPAT) : '';
 995              }
 996              else
 997              {
 998                  // If error occurs in initiating the session we need to use a pre-defined language string
 999                  // This could happen if the connection could not be established for example (then we are not able to grab the default language)
1000                  if (!isset($user->lang['SQL_ERROR_OCCURRED']))
1001                  {
1002                      $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
1003                  }
1004                  else
1005                  {
1006                      if (!empty($config['board_contact']))
1007                      {
1008                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact'], ENT_COMPAT) . '">', '</a>');
1009                      }
1010                      else
1011                      {
1012                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
1013                      }
1014                  }
1015              }
1016  
1017              if ($this->transaction)
1018              {
1019                  $this->sql_transaction('rollback');
1020              }
1021  
1022              if (strlen($message) > 1024)
1023              {
1024                  // We need to define $msg_long_text here to circumvent text stripping.
1025                  global $msg_long_text;
1026                  $msg_long_text = $message;
1027  
1028                  trigger_error(false, E_USER_ERROR);
1029              }
1030  
1031              trigger_error($message, E_USER_ERROR);
1032          }
1033  
1034          if ($this->transaction)
1035          {
1036              $this->sql_transaction('rollback');
1037          }
1038  
1039          return $this->sql_error_returned;
1040      }
1041  
1042      /**
1043      * {@inheritDoc}
1044      */
1045  	function sql_report($mode, $query = '')
1046      {
1047          global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
1048  
1049          if (!$query && $this->query_hold != '')
1050          {
1051              $query = $this->query_hold;
1052          }
1053  
1054          switch ($mode)
1055          {
1056              case 'display':
1057                  if (!empty($cache))
1058                  {
1059                      $cache->unload();
1060                  }
1061                  $this->sql_close();
1062  
1063                  $mtime = explode(' ', microtime());
1064                  $totaltime = $mtime[0] + $mtime[1] - $starttime;
1065  
1066                  echo '<!DOCTYPE html>
1067                      <html dir="ltr">
1068                      <head>
1069                          <meta charset="utf-8">
1070                          <meta http-equiv="X-UA-Compatible" content="IE=edge">
1071                          <title>SQL Report</title>
1072                          <link href="' . htmlspecialchars($phpbb_path_helper->update_web_root_path($phpbb_root_path) . $phpbb_path_helper->get_adm_relative_path(), ENT_COMPAT) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" />
1073                      </head>
1074                      <body id="errorpage">
1075                      <div id="wrap">
1076                          <div id="page-header">
1077                              <a href="' . build_url('explain') . '">Return to previous page</a>
1078                          </div>
1079                          <div id="page-body">
1080                              <div id="acp">
1081                              <div class="panel">
1082                                  <span class="corners-top"><span></span></span>
1083                                  <div id="content">
1084                                      <h1>SQL Report</h1>
1085                                      <br />
1086                                      <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>
1087  
1088                                      <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>
1089  
1090                                      <br /><br />
1091                                      ' . $this->sql_report . '
1092                                  </div>
1093                                  <span class="corners-bottom"><span></span></span>
1094                              </div>
1095                              </div>
1096                          </div>
1097                          <div id="page-footer">
1098                              Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
1099                          </div>
1100                      </div>
1101                      </body>
1102                      </html>';
1103  
1104                  exit_handler();
1105  
1106              break;
1107  
1108              case 'stop':
1109                  $endtime = explode(' ', microtime());
1110                  $endtime = $endtime[0] + $endtime[1];
1111  
1112                  $this->sql_report .= '
1113  
1114                      <table cellspacing="1">
1115                      <thead>
1116                      <tr>
1117                          <th>Query #' . $this->num_queries['total'] . '</th>
1118                      </tr>
1119                      </thead>
1120                      <tbody>
1121                      <tr>
1122                          <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), ENT_COMPAT)) . '</textarea></td>
1123                      </tr>
1124                      </tbody>
1125                      </table>
1126  
1127                      ' . $this->html_hold . '
1128  
1129                      <p style="text-align: center;">
1130                  ';
1131  
1132                  if ($this->query_result)
1133                  {
1134                      if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
1135                      {
1136                          $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
1137                      }
1138                      $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>';
1139                  }
1140                  else
1141                  {
1142                      $error = $this->sql_error();
1143                      $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message'], ENT_COMPAT);
1144                  }
1145  
1146                  $this->sql_report .= '</p><br /><br />';
1147  
1148                  $this->sql_time += $endtime - $this->curtime;
1149              break;
1150  
1151              case 'start':
1152                  $this->query_hold = $query;
1153                  $this->html_hold = '';
1154  
1155                  $this->_sql_report($mode, $query);
1156  
1157                  $this->curtime = explode(' ', microtime());
1158                  $this->curtime = $this->curtime[0] + $this->curtime[1];
1159  
1160              break;
1161  
1162              case 'add_select_row':
1163  
1164                  $html_table = func_get_arg(2);
1165                  $row = func_get_arg(3);
1166  
1167                  if (!$html_table && count($row))
1168                  {
1169                      $html_table = true;
1170                      $this->html_hold .= '<table cellspacing="1"><tr>';
1171  
1172                      foreach (array_keys($row) as $val)
1173                      {
1174                          $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
1175                      }
1176                      $this->html_hold .= '</tr>';
1177                  }
1178                  $this->html_hold .= '<tr>';
1179  
1180                  $class = 'row1';
1181                  foreach (array_values($row) as $val)
1182                  {
1183                      $class = ($class == 'row1') ? 'row2' : 'row1';
1184                      $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
1185                  }
1186                  $this->html_hold .= '</tr>';
1187  
1188                  return $html_table;
1189  
1190              break;
1191  
1192              case 'fromcache':
1193  
1194                  $this->_sql_report($mode, $query);
1195  
1196              break;
1197  
1198              case 'record_fromcache':
1199  
1200                  $endtime = func_get_arg(2);
1201                  $splittime = func_get_arg(3);
1202  
1203                  $time_cache = $endtime - $this->curtime;
1204                  $time_db = $splittime - $endtime;
1205                  $color = ($time_db > $time_cache) ? 'green' : 'red';
1206  
1207                  $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
1208                  $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), ENT_COMPAT)) . '</textarea></td></tr></tbody></table>';
1209                  $this->sql_report .= '<p style="text-align: center;">';
1210                  $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 />';
1211  
1212                  // Pad the start time to not interfere with page timing
1213                  $starttime += $time_db;
1214  
1215              break;
1216  
1217              default:
1218  
1219                  $this->_sql_report($mode, $query);
1220  
1221              break;
1222          }
1223  
1224          return true;
1225      }
1226  
1227      /**
1228      * {@inheritDoc}
1229      */
1230  	function get_estimated_row_count($table_name)
1231      {
1232          return $this->get_row_count($table_name);
1233      }
1234  
1235      /**
1236      * {@inheritDoc}
1237      */
1238  	function get_row_count($table_name)
1239      {
1240          $sql = 'SELECT COUNT(*) AS rows_total
1241              FROM ' . $this->sql_escape($table_name);
1242          $result = $this->sql_query($sql);
1243          $rows_total = $this->sql_fetchfield('rows_total');
1244          $this->sql_freeresult($result);
1245  
1246          return $rows_total;
1247      }
1248  
1249      /**
1250       * {@inheritDoc}
1251       */
1252  	public function clean_query_id($query_id)
1253      {
1254          // Some DBMS functions accept/return objects and/or resources instead if identifiers
1255          // Attempting to use objects/resources as array keys will throw error, hence correctly handle all cases
1256          if (is_resource($query_id))
1257          {
1258              return function_exists('get_resource_id') ? get_resource_id($query_id) : (int) $query_id;
1259          }
1260          else
1261          {
1262              return is_object($query_id) ? spl_object_id($query_id) : $query_id;
1263          }
1264      }
1265  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1