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