Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
driver.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 namespace phpbb\db\driver;
0015
0016 /**
0017 * Database Abstraction Layer
0018 */
0019 abstract class driver implements driver_interface
0020 {
0021 var $db_connect_id;
0022 var $query_result;
0023 var $return_on_error = false;
0024 var $transaction = false;
0025 var $sql_time = 0;
0026 var $num_queries = array();
0027 var $open_queries = array();
0028
0029 var $curtime = 0;
0030 var $query_hold = '';
0031 var $html_hold = '';
0032 var $sql_report = '';
0033
0034 var $persistency = false;
0035 var $user = '';
0036 var $server = '';
0037 var $dbname = '';
0038
0039 // Set to true if error triggered
0040 var $sql_error_triggered = false;
0041
0042 // Holding the last sql query on sql error
0043 var $sql_error_sql = '';
0044 // Holding the error information - only populated if sql_error_triggered is set
0045 var $sql_error_returned = array();
0046
0047 // Holding transaction count
0048 var $transactions = 0;
0049
0050 // Supports multi inserts?
0051 var $multi_insert = false;
0052
0053 /**
0054 * Current sql layer
0055 */
0056 var $sql_layer = '';
0057
0058 /**
0059 * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions
0060 */
0061 var $any_char;
0062 var $one_char;
0063
0064 /**
0065 * Exact version of the DBAL, directly queried
0066 */
0067 var $sql_server_version = false;
0068
0069 const LOGICAL_OP = 0;
0070 const STATEMENTS = 1;
0071 const LEFT_STMT = 0;
0072 const COMPARE_OP = 1;
0073 const RIGHT_STMT = 2;
0074 const SUBQUERY_OP = 3;
0075 const SUBQUERY_SELECT_TYPE = 4;
0076 const SUBQUERY_BUILD = 5;
0077
0078 /**
0079 * Constructor
0080 */
0081 function __construct()
0082 {
0083 $this->num_queries = array(
0084 'cached' => 0,
0085 'normal' => 0,
0086 'total' => 0,
0087 );
0088
0089 // Fill default sql layer based on the class being called.
0090 // This can be changed by the specified layer itself later if needed.
0091 $this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\'));
0092
0093 // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
0094 $this->any_char = chr(0) . '%';
0095 $this->one_char = chr(0) . '_';
0096 }
0097
0098 /**
0099 * {@inheritdoc}
0100 */
0101 public function get_sql_layer()
0102 {
0103 return $this->sql_layer;
0104 }
0105
0106 /**
0107 * {@inheritdoc}
0108 */
0109 public function get_db_name()
0110 {
0111 return $this->dbname;
0112 }
0113
0114 /**
0115 * {@inheritdoc}
0116 */
0117 public function get_any_char()
0118 {
0119 return $this->any_char;
0120 }
0121
0122 /**
0123 * {@inheritdoc}
0124 */
0125 public function get_one_char()
0126 {
0127 return $this->one_char;
0128 }
0129
0130 /**
0131 * {@inheritdoc}
0132 */
0133 public function get_db_connect_id()
0134 {
0135 return $this->db_connect_id;
0136 }
0137
0138 /**
0139 * {@inheritdoc}
0140 */
0141 public function get_sql_error_triggered()
0142 {
0143 return $this->sql_error_triggered;
0144 }
0145
0146 /**
0147 * {@inheritdoc}
0148 */
0149 public function get_sql_error_sql()
0150 {
0151 return $this->sql_error_sql;
0152 }
0153
0154 /**
0155 * {@inheritdoc}
0156 */
0157 public function get_transaction()
0158 {
0159 return $this->transaction;
0160 }
0161
0162 /**
0163 * {@inheritdoc}
0164 */
0165 public function get_sql_time()
0166 {
0167 return $this->sql_time;
0168 }
0169
0170 /**
0171 * {@inheritdoc}
0172 */
0173 public function get_sql_error_returned()
0174 {
0175 return $this->sql_error_returned;
0176 }
0177
0178 /**
0179 * {@inheritdoc}
0180 */
0181 public function get_multi_insert()
0182 {
0183 return $this->multi_insert;
0184 }
0185
0186 /**
0187 * {@inheritdoc}
0188 */
0189 public function set_multi_insert($multi_insert)
0190 {
0191 $this->multi_insert = $multi_insert;
0192 }
0193
0194 /**
0195 * {@inheritDoc}
0196 */
0197 function sql_return_on_error($fail = false)
0198 {
0199 $this->sql_error_triggered = false;
0200 $this->sql_error_sql = '';
0201
0202 $this->return_on_error = $fail;
0203 }
0204
0205 /**
0206 * {@inheritDoc}
0207 */
0208 function sql_num_queries($cached = false)
0209 {
0210 return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
0211 }
0212
0213 /**
0214 * {@inheritDoc}
0215 */
0216 function sql_add_num_queries($cached = false)
0217 {
0218 $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
0219 $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
0220 $this->num_queries['total'] += 1;
0221 }
0222
0223 /**
0224 * {@inheritDoc}
0225 */
0226 function sql_close()
0227 {
0228 if (!$this->db_connect_id)
0229 {
0230 return false;
0231 }
0232
0233 if ($this->transaction)
0234 {
0235 do
0236 {
0237 $this->sql_transaction('commit');
0238 }
0239 while ($this->transaction);
0240 }
0241
0242 foreach ($this->open_queries as $query_id)
0243 {
0244 $this->sql_freeresult($query_id);
0245 }
0246
0247 // Connection closed correctly. Set db_connect_id to false to prevent errors
0248 if ($result = $this->_sql_close())
0249 {
0250 $this->db_connect_id = false;
0251 }
0252
0253 return $result;
0254 }
0255
0256 /**
0257 * {@inheritDoc}
0258 */
0259 function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
0260 {
0261 if (empty($query))
0262 {
0263 return false;
0264 }
0265
0266 // Never use a negative total or offset
0267 $total = ($total < 0) ? 0 : $total;
0268 $offset = ($offset < 0) ? 0 : $offset;
0269
0270 return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
0271 }
0272
0273 /**
0274 * {@inheritDoc}
0275 */
0276 function sql_fetchrowset($query_id = false)
0277 {
0278 if ($query_id === false)
0279 {
0280 $query_id = $this->query_result;
0281 }
0282
0283 if ($query_id)
0284 {
0285 $result = array();
0286 while ($row = $this->sql_fetchrow($query_id))
0287 {
0288 $result[] = $row;
0289 }
0290
0291 return $result;
0292 }
0293
0294 return false;
0295 }
0296
0297 /**
0298 * {@inheritDoc}
0299 */
0300 function sql_rowseek($rownum, &$query_id)
0301 {
0302 global $cache;
0303
0304 if ($query_id === false)
0305 {
0306 $query_id = $this->query_result;
0307 }
0308
0309 if ($cache && $cache->sql_exists($query_id))
0310 {
0311 return $cache->sql_rowseek($rownum, $query_id);
0312 }
0313
0314 if (!$query_id)
0315 {
0316 return false;
0317 }
0318
0319 $this->sql_freeresult($query_id);
0320 $query_id = $this->sql_query($this->last_query_text);
0321
0322 if (!$query_id)
0323 {
0324 return false;
0325 }
0326
0327 // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
0328 for ($i = 0; $i < $rownum; $i++)
0329 {
0330 if (!$this->sql_fetchrow($query_id))
0331 {
0332 return false;
0333 }
0334 }
0335
0336 return true;
0337 }
0338
0339 /**
0340 * {@inheritDoc}
0341 */
0342 function sql_fetchfield($field, $rownum = false, $query_id = false)
0343 {
0344 global $cache;
0345
0346 if ($query_id === false)
0347 {
0348 $query_id = $this->query_result;
0349 }
0350
0351 if ($query_id)
0352 {
0353 if ($rownum !== false)
0354 {
0355 $this->sql_rowseek($rownum, $query_id);
0356 }
0357
0358 if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
0359 {
0360 return $cache->sql_fetchfield($query_id, $field);
0361 }
0362
0363 $row = $this->sql_fetchrow($query_id);
0364 return (isset($row[$field])) ? $row[$field] : false;
0365 }
0366
0367 return false;
0368 }
0369
0370 /**
0371 * {@inheritDoc}
0372 */
0373 function sql_like_expression($expression)
0374 {
0375 $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
0376 $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0377
0378 return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
0379 }
0380
0381 /**
0382 * {@inheritDoc}
0383 */
0384 function sql_not_like_expression($expression)
0385 {
0386 $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
0387 $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0388
0389 return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
0390 }
0391
0392 /**
0393 * {@inheritDoc}
0394 */
0395 public function sql_case($condition, $action_true, $action_false = false)
0396 {
0397 $sql_case = 'CASE WHEN ' . $condition;
0398 $sql_case .= ' THEN ' . $action_true;
0399 $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
0400 $sql_case .= ' END';
0401 return $sql_case;
0402 }
0403
0404 /**
0405 * {@inheritDoc}
0406 */
0407 public function sql_concatenate($expr1, $expr2)
0408 {
0409 return $expr1 . ' || ' . $expr2;
0410 }
0411
0412 /**
0413 * {@inheritDoc}
0414 */
0415 function sql_buffer_nested_transactions()
0416 {
0417 return false;
0418 }
0419
0420 /**
0421 * {@inheritDoc}
0422 */
0423 function sql_transaction($status = 'begin')
0424 {
0425 switch ($status)
0426 {
0427 case 'begin':
0428 // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
0429 if ($this->transaction)
0430 {
0431 $this->transactions++;
0432 return true;
0433 }
0434
0435 $result = $this->_sql_transaction('begin');
0436
0437 if (!$result)
0438 {
0439 $this->sql_error();
0440 }
0441
0442 $this->transaction = true;
0443 break;
0444
0445 case 'commit':
0446 // If there was a previously opened transaction we do not commit yet...
0447 // but count back the number of inner transactions
0448 if ($this->transaction && $this->transactions)
0449 {
0450 $this->transactions--;
0451 return true;
0452 }
0453
0454 // Check if there is a transaction (no transaction can happen if
0455 // there was an error, with a combined rollback and error returning enabled)
0456 // This implies we have transaction always set for autocommit db's
0457 if (!$this->transaction)
0458 {
0459 return false;
0460 }
0461
0462 $result = $this->_sql_transaction('commit');
0463
0464 if (!$result)
0465 {
0466 $this->sql_error();
0467 }
0468
0469 $this->transaction = false;
0470 $this->transactions = 0;
0471 break;
0472
0473 case 'rollback':
0474 $result = $this->_sql_transaction('rollback');
0475 $this->transaction = false;
0476 $this->transactions = 0;
0477 break;
0478
0479 default:
0480 $result = $this->_sql_transaction($status);
0481 break;
0482 }
0483
0484 return $result;
0485 }
0486
0487 /**
0488 * {@inheritDoc}
0489 */
0490 function sql_build_array($query, $assoc_ary = false)
0491 {
0492 if (!is_array($assoc_ary))
0493 {
0494 return false;
0495 }
0496
0497 $fields = $values = array();
0498
0499 if ($query == 'INSERT' || $query == 'INSERT_SELECT')
0500 {
0501 foreach ($assoc_ary as $key => $var)
0502 {
0503 $fields[] = $key;
0504
0505 if (is_array($var) && is_string($var[0]))
0506 {
0507 // This is used for INSERT_SELECT(s)
0508 $values[] = $var[0];
0509 }
0510 else
0511 {
0512 $values[] = $this->_sql_validate_value($var);
0513 }
0514 }
0515
0516 $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
0517 }
0518 else if ($query == 'MULTI_INSERT')
0519 {
0520 trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
0521 }
0522 else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
0523 {
0524 $values = array();
0525 foreach ($assoc_ary as $key => $var)
0526 {
0527 $values[] = "$key = " . $this->_sql_validate_value($var);
0528 }
0529 $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
0530 }
0531
0532 return $query;
0533 }
0534
0535 /**
0536 * {@inheritDoc}
0537 */
0538 function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
0539 {
0540 if (!sizeof($array))
0541 {
0542 if (!$allow_empty_set)
0543 {
0544 // Print the backtrace to help identifying the location of the problematic code
0545 $this->sql_error('No values specified for SQL IN comparison');
0546 }
0547 else
0548 {
0549 // NOT IN () actually means everything so use a tautology
0550 if ($negate)
0551 {
0552 return '1=1';
0553 }
0554 // IN () actually means nothing so use a contradiction
0555 else
0556 {
0557 return '1=0';
0558 }
0559 }
0560 }
0561
0562 if (!is_array($array))
0563 {
0564 $array = array($array);
0565 }
0566
0567 if (sizeof($array) == 1)
0568 {
0569 @reset($array);
0570 $var = current($array);
0571
0572 return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
0573 }
0574 else
0575 {
0576 return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
0577 }
0578 }
0579
0580 /**
0581 * {@inheritDoc}
0582 */
0583 function sql_bit_and($column_name, $bit, $compare = '')
0584 {
0585 if (method_exists($this, '_sql_bit_and'))
0586 {
0587 return $this->_sql_bit_and($column_name, $bit, $compare);
0588 }
0589
0590 return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0591 }
0592
0593 /**
0594 * {@inheritDoc}
0595 */
0596 function sql_bit_or($column_name, $bit, $compare = '')
0597 {
0598 if (method_exists($this, '_sql_bit_or'))
0599 {
0600 return $this->_sql_bit_or($column_name, $bit, $compare);
0601 }
0602
0603 return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0604 }
0605
0606 /**
0607 * {@inheritDoc}
0608 */
0609 function cast_expr_to_bigint($expression)
0610 {
0611 return $expression;
0612 }
0613
0614 /**
0615 * {@inheritDoc}
0616 */
0617 function cast_expr_to_string($expression)
0618 {
0619 return $expression;
0620 }
0621
0622 /**
0623 * {@inheritDoc}
0624 */
0625 function sql_lower_text($column_name)
0626 {
0627 return "LOWER($column_name)";
0628 }
0629
0630 /**
0631 * {@inheritDoc}
0632 */
0633 function sql_multi_insert($table, $sql_ary)
0634 {
0635 if (!sizeof($sql_ary))
0636 {
0637 return false;
0638 }
0639
0640 if ($this->multi_insert)
0641 {
0642 $ary = array();
0643 foreach ($sql_ary as $id => $_sql_ary)
0644 {
0645 // If by accident the sql array is only one-dimensional we build a normal insert statement
0646 if (!is_array($_sql_ary))
0647 {
0648 return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
0649 }
0650
0651 $values = array();
0652 foreach ($_sql_ary as $key => $var)
0653 {
0654 $values[] = $this->_sql_validate_value($var);
0655 }
0656 $ary[] = '(' . implode(', ', $values) . ')';
0657 }
0658
0659 return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
0660 }
0661 else
0662 {
0663 foreach ($sql_ary as $ary)
0664 {
0665 if (!is_array($ary))
0666 {
0667 return false;
0668 }
0669
0670 $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
0671
0672 if (!$result)
0673 {
0674 return false;
0675 }
0676 }
0677 }
0678
0679 return true;
0680 }
0681
0682 /**
0683 * Function for validating values
0684 * @access private
0685 */
0686 function _sql_validate_value($var)
0687 {
0688 if (is_null($var))
0689 {
0690 return 'NULL';
0691 }
0692 else if (is_string($var))
0693 {
0694 return "'" . $this->sql_escape($var) . "'";
0695 }
0696 else
0697 {
0698 return (is_bool($var)) ? intval($var) : $var;
0699 }
0700 }
0701
0702 /**
0703 * {@inheritDoc}
0704 */
0705 function sql_build_query($query, $array)
0706 {
0707 $sql = '';
0708 switch ($query)
0709 {
0710 case 'SELECT':
0711 case 'SELECT_DISTINCT';
0712
0713 $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
0714
0715 // Build table array. We also build an alias array for later checks.
0716 $table_array = $aliases = array();
0717 $used_multi_alias = false;
0718
0719 foreach ($array['FROM'] as $table_name => $alias)
0720 {
0721 if (is_array($alias))
0722 {
0723 $used_multi_alias = true;
0724
0725 foreach ($alias as $multi_alias)
0726 {
0727 $table_array[] = $table_name . ' ' . $multi_alias;
0728 $aliases[] = $multi_alias;
0729 }
0730 }
0731 else
0732 {
0733 $table_array[] = $table_name . ' ' . $alias;
0734 $aliases[] = $alias;
0735 }
0736 }
0737
0738 // We run the following code to determine if we need to re-order the table array. ;)
0739 // 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.
0740 // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
0741 if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false)
0742 {
0743 // Take first LEFT JOIN
0744 $join = current($array['LEFT_JOIN']);
0745
0746 // Determine the table used there (even if there are more than one used, we only want to have one
0747 preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
0748
0749 // If there is a first join match, we need to make sure the table order is correct
0750 if (!empty($matches[1]))
0751 {
0752 $first_join_match = trim($matches[1]);
0753 $table_array = $last = array();
0754
0755 foreach ($array['FROM'] as $table_name => $alias)
0756 {
0757 if (is_array($alias))
0758 {
0759 foreach ($alias as $multi_alias)
0760 {
0761 ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
0762 }
0763 }
0764 else
0765 {
0766 ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
0767 }
0768 }
0769
0770 $table_array = array_merge($table_array, $last);
0771 }
0772 }
0773
0774 $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
0775
0776 if (!empty($array['LEFT_JOIN']))
0777 {
0778 foreach ($array['LEFT_JOIN'] as $join)
0779 {
0780 $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
0781 }
0782 }
0783
0784 if (!empty($array['WHERE']))
0785 {
0786 $sql .= ' WHERE ';
0787
0788 if (is_array($array['WHERE']))
0789 {
0790 $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
0791 }
0792 else
0793 {
0794 $sql_where = $array['WHERE'];
0795 }
0796
0797 $sql .= $this->_sql_custom_build('WHERE', $sql_where);
0798 }
0799
0800 if (!empty($array['GROUP_BY']))
0801 {
0802 $sql .= ' GROUP BY ' . $array['GROUP_BY'];
0803 }
0804
0805 if (!empty($array['ORDER_BY']))
0806 {
0807 $sql .= ' ORDER BY ' . $array['ORDER_BY'];
0808 }
0809
0810 break;
0811 }
0812
0813 return $sql;
0814 }
0815
0816
0817 protected function _process_boolean_tree_first($operations_ary)
0818 {
0819 // In cases where an array exists but there is no head condition,
0820 // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
0821 if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
0822 $operations_ary[self::LOGICAL_OP] !== 'OR')
0823 {
0824 $operations_ary = array('AND', array($operations_ary));
0825 }
0826 return $this->_process_boolean_tree($operations_ary) . "\n";
0827 }
0828
0829 protected function _process_boolean_tree($operations_ary)
0830 {
0831 $operation = $operations_ary[self::LOGICAL_OP];
0832
0833 foreach ($operations_ary[self::STATEMENTS] as &$condition)
0834 {
0835 switch ($condition[self::LOGICAL_OP])
0836 {
0837 case 'AND':
0838 case 'OR':
0839
0840 $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
0841
0842 break;
0843 case 'NOT':
0844
0845 $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
0846
0847 break;
0848
0849 default:
0850
0851 switch (sizeof($condition))
0852 {
0853 case 3:
0854
0855 // Typical 3 element clause with {left hand} {operator} {right hand}
0856 switch ($condition[self::COMPARE_OP])
0857 {
0858 case 'IN':
0859 case 'NOT_IN':
0860
0861 // As this is used with an IN, assume it is a set of elements for sql_in_set()
0862 $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
0863
0864 break;
0865
0866 case 'LIKE':
0867
0868 $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
0869
0870 break;
0871
0872 case 'NOT_LIKE':
0873
0874 $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
0875
0876 break;
0877
0878 case 'IS_NOT':
0879
0880 $condition[self::COMPARE_OP] = 'IS NOT';
0881
0882 // no break
0883 case 'IS':
0884
0885 // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
0886 // this should solve that
0887 if ($condition[self::RIGHT_STMT] === null)
0888 {
0889 $condition[self::RIGHT_STMT] = 'NULL';
0890 }
0891
0892 $condition = implode(' ', $condition);
0893
0894 break;
0895
0896 default:
0897
0898 $condition = implode(' ', $condition);
0899
0900 break;
0901 }
0902
0903 break;
0904
0905 case 5:
0906
0907 // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
0908
0909 $condition = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
0910 $condition .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
0911 $condition .= ' )';
0912
0913 break;
0914
0915 default:
0916 // This is an unpredicted clause setup. Just join all elements.
0917 $condition = implode(' ', $condition);
0918
0919 break;
0920 }
0921
0922 break;
0923 }
0924
0925 }
0926
0927 if ($operation === 'NOT')
0928 {
0929 $operations_ary = implode("", $operations_ary[self::STATEMENTS]);
0930 }
0931 else
0932 {
0933 $operations_ary = implode(" \n $operation ", $operations_ary[self::STATEMENTS]);
0934 }
0935
0936 return $operations_ary;
0937 }
0938
0939
0940 /**
0941 * {@inheritDoc}
0942 */
0943 function sql_error($sql = '')
0944 {
0945 global $auth, $user, $config;
0946
0947 // Set var to retrieve errored status
0948 $this->sql_error_triggered = true;
0949 $this->sql_error_sql = $sql;
0950
0951 $this->sql_error_returned = $this->_sql_error();
0952
0953 if (!$this->return_on_error)
0954 {
0955 $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
0956
0957 // Show complete SQL error and path to administrators only
0958 // Additionally show complete error on installation or if extended debug mode is enabled
0959 // The DEBUG constant is for development only!
0960 if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG'))
0961 {
0962 $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
0963 }
0964 else
0965 {
0966 // If error occurs in initiating the session we need to use a pre-defined language string
0967 // This could happen if the connection could not be established for example (then we are not able to grab the default language)
0968 if (!isset($user->lang['SQL_ERROR_OCCURRED']))
0969 {
0970 $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
0971 }
0972 else
0973 {
0974 if (!empty($config['board_contact']))
0975 {
0976 $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
0977 }
0978 else
0979 {
0980 $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
0981 }
0982 }
0983 }
0984
0985 if ($this->transaction)
0986 {
0987 $this->sql_transaction('rollback');
0988 }
0989
0990 if (strlen($message) > 1024)
0991 {
0992 // We need to define $msg_long_text here to circumvent text stripping.
0993 global $msg_long_text;
0994 $msg_long_text = $message;
0995
0996 trigger_error(false, E_USER_ERROR);
0997 }
0998
0999 trigger_error($message, E_USER_ERROR);
1000 }
1001
1002 if ($this->transaction)
1003 {
1004 $this->sql_transaction('rollback');
1005 }
1006
1007 return $this->sql_error_returned;
1008 }
1009
1010 /**
1011 * {@inheritDoc}
1012 */
1013 function sql_report($mode, $query = '')
1014 {
1015 global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
1016 global $request;
1017
1018 if (is_object($request) && !$request->variable('explain', false))
1019 {
1020 return false;
1021 }
1022
1023 if (!$query && $this->query_hold != '')
1024 {
1025 $query = $this->query_hold;
1026 }
1027
1028 switch ($mode)
1029 {
1030 case 'display':
1031 if (!empty($cache))
1032 {
1033 $cache->unload();
1034 }
1035 $this->sql_close();
1036
1037 $mtime = explode(' ', microtime());
1038 $totaltime = $mtime[0] + $mtime[1] - $starttime;
1039
1040 echo '<!DOCTYPE html>
1041 <html dir="ltr">
1042 <head>
1043 <meta charset="utf-8">
1044 <meta http-equiv="X-UA-Compatible" content="IE=edge">
1045 <title>SQL Report</title>
1046 <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" />
1047 </head>
1048 <body id="errorpage">
1049 <div id="wrap">
1050 <div id="page-header">
1051 <a href="' . build_url('explain') . '">Return to previous page</a>
1052 </div>
1053 <div id="page-body">
1054 <div id="acp">
1055 <div class="panel">
1056 <span class="corners-top"><span></span></span>
1057 <div id="content">
1058 <h1>SQL Report</h1>
1059 <br />
1060 <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>
1061
1062 <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>
1063
1064 <br /><br />
1065 ' . $this->sql_report . '
1066 </div>
1067 <span class="corners-bottom"><span></span></span>
1068 </div>
1069 </div>
1070 </div>
1071 <div id="page-footer">
1072 Powered by <a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Limited
1073 </div>
1074 </div>
1075 </body>
1076 </html>';
1077
1078 exit_handler();
1079
1080 break;
1081
1082 case 'stop':
1083 $endtime = explode(' ', microtime());
1084 $endtime = $endtime[0] + $endtime[1];
1085
1086 $this->sql_report .= '
1087
1088 <table cellspacing="1">
1089 <thead>
1090 <tr>
1091 <th>Query #' . $this->num_queries['total'] . '</th>
1092 </tr>
1093 </thead>
1094 <tbody>
1095 <tr>
1096 <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>
1097 </tr>
1098 </tbody>
1099 </table>
1100
1101 ' . $this->html_hold . '
1102
1103 <p style="text-align: center;">
1104 ';
1105
1106 if ($this->query_result)
1107 {
1108 if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
1109 {
1110 $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
1111 }
1112 $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>';
1113 }
1114 else
1115 {
1116 $error = $this->sql_error();
1117 $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
1118 }
1119
1120 $this->sql_report .= '</p><br /><br />';
1121
1122 $this->sql_time += $endtime - $this->curtime;
1123 break;
1124
1125 case 'start':
1126 $this->query_hold = $query;
1127 $this->html_hold = '';
1128
1129 $this->_sql_report($mode, $query);
1130
1131 $this->curtime = explode(' ', microtime());
1132 $this->curtime = $this->curtime[0] + $this->curtime[1];
1133
1134 break;
1135
1136 case 'add_select_row':
1137
1138 $html_table = func_get_arg(2);
1139 $row = func_get_arg(3);
1140
1141 if (!$html_table && sizeof($row))
1142 {
1143 $html_table = true;
1144 $this->html_hold .= '<table cellspacing="1"><tr>';
1145
1146 foreach (array_keys($row) as $val)
1147 {
1148 $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : ' ') . '</th>';
1149 }
1150 $this->html_hold .= '</tr>';
1151 }
1152 $this->html_hold .= '<tr>';
1153
1154 $class = 'row1';
1155 foreach (array_values($row) as $val)
1156 {
1157 $class = ($class == 'row1') ? 'row2' : 'row1';
1158 $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : ' ') . '</td>';
1159 }
1160 $this->html_hold .= '</tr>';
1161
1162 return $html_table;
1163
1164 break;
1165
1166 case 'fromcache':
1167
1168 $this->_sql_report($mode, $query);
1169
1170 break;
1171
1172 case 'record_fromcache':
1173
1174 $endtime = func_get_arg(2);
1175 $splittime = func_get_arg(3);
1176
1177 $time_cache = $endtime - $this->curtime;
1178 $time_db = $splittime - $endtime;
1179 $color = ($time_db > $time_cache) ? 'green' : 'red';
1180
1181 $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
1182 $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>';
1183 $this->sql_report .= '<p style="text-align: center;">';
1184 $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 />';
1185
1186 // Pad the start time to not interfere with page timing
1187 $starttime += $time_db;
1188
1189 break;
1190
1191 default:
1192
1193 $this->_sql_report($mode, $query);
1194
1195 break;
1196 }
1197
1198 return true;
1199 }
1200
1201 /**
1202 * {@inheritDoc}
1203 */
1204 function get_estimated_row_count($table_name)
1205 {
1206 return $this->get_row_count($table_name);
1207 }
1208
1209 /**
1210 * {@inheritDoc}
1211 */
1212 function get_row_count($table_name)
1213 {
1214 $sql = 'SELECT COUNT(*) AS rows_total
1215 FROM ' . $this->sql_escape($table_name);
1216 $result = $this->sql_query($sql);
1217 $rows_total = $this->sql_fetchfield('rows_total');
1218 $this->sql_freeresult($result);
1219
1220 return $rows_total;
1221 }
1222 }
1223