aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/phpbb/db/driver/driver.php
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/phpbb/db/driver/driver.php')
-rw-r--r--phpBB/phpbb/db/driver/driver.php419
1 files changed, 301 insertions, 118 deletions
diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php
index d721ed2eb7..30cb667344 100644
--- a/phpBB/phpbb/db/driver/driver.php
+++ b/phpBB/phpbb/db/driver/driver.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,9 +15,8 @@ namespace phpbb\db\driver;
/**
* Database Abstraction Layer
-* @package dbal
*/
-class driver
+abstract class driver implements driver_interface
{
var $db_connect_id;
var $query_result;
@@ -63,6 +66,15 @@ class driver
*/
var $sql_server_version = false;
+ const LOGICAL_OP = 0;
+ const STATEMENTS = 1;
+ const LEFT_STMT = 0;
+ const COMPARE_OP = 1;
+ const RIGHT_STMT = 2;
+ const SUBQUERY_OP = 3;
+ const SUBQUERY_SELECT_TYPE = 4;
+ const SUBQUERY_BUILD = 5;
+
/**
* Constructor
*/
@@ -84,7 +96,103 @@ class driver
}
/**
- * return on error or display error message
+ * {@inheritdoc}
+ */
+ public function get_sql_layer()
+ {
+ return $this->sql_layer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_name()
+ {
+ return $this->dbname;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_any_char()
+ {
+ return $this->any_char;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_one_char()
+ {
+ return $this->one_char;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_connect_id()
+ {
+ return $this->db_connect_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_triggered()
+ {
+ return $this->sql_error_triggered;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_sql()
+ {
+ return $this->sql_error_sql;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_transaction()
+ {
+ return $this->transaction;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_time()
+ {
+ return $this->sql_time;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_returned()
+ {
+ return $this->sql_error_returned;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_multi_insert()
+ {
+ return $this->multi_insert;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_multi_insert($multi_insert)
+ {
+ $this->multi_insert = $multi_insert;
+ }
+
+ /**
+ * {@inheritDoc}
*/
function sql_return_on_error($fail = false)
{
@@ -95,7 +203,7 @@ class driver
}
/**
- * Return number of sql queries and cached sql queries used
+ * {@inheritDoc}
*/
function sql_num_queries($cached = false)
{
@@ -103,7 +211,7 @@ class driver
}
/**
- * Add to query count
+ * {@inheritDoc}
*/
function sql_add_num_queries($cached = false)
{
@@ -113,7 +221,7 @@ class driver
}
/**
- * DBAL garbage collection, close sql connection
+ * {@inheritDoc}
*/
function sql_close()
{
@@ -146,8 +254,7 @@ class driver
}
/**
- * Build LIMIT query
- * Doing some validation here.
+ * {@inheritDoc}
*/
function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
{
@@ -164,7 +271,7 @@ class driver
}
/**
- * Fetch all rows
+ * {@inheritDoc}
*/
function sql_fetchrowset($query_id = false)
{
@@ -173,7 +280,7 @@ class driver
$query_id = $this->query_result;
}
- if ($query_id !== false)
+ if ($query_id)
{
$result = array();
while ($row = $this->sql_fetchrow($query_id))
@@ -188,8 +295,7 @@ class driver
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -205,7 +311,7 @@ class driver
return $cache->sql_rowseek($rownum, $query_id);
}
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
@@ -213,7 +319,7 @@ class driver
$this->sql_freeresult($query_id);
$query_id = $this->sql_query($this->last_query_text);
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
@@ -231,8 +337,7 @@ class driver
}
/**
- * Fetch field
- * if rownum is false, the current row is used, else it is pointing to the row (zero-based)
+ * {@inheritDoc}
*/
function sql_fetchfield($field, $rownum = false, $query_id = false)
{
@@ -243,7 +348,7 @@ class driver
$query_id = $this->query_result;
}
- if ($query_id !== false)
+ if ($query_id)
{
if ($rownum !== false)
{
@@ -263,29 +368,29 @@ class driver
}
/**
- * Correctly adjust LIKE expression for special characters
- * Some DBMS are handling them in a different way
- *
- * @param string $expression The expression to use. Every wildcard is escaped, except $this->any_char and $this->one_char
- * @return string LIKE expression including the keyword!
+ * {@inheritDoc}
*/
function sql_like_expression($expression)
{
- $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
- $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
+ $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
}
/**
- * Build a case expression
- *
- * Note: The two statements action_true and action_false must have the same data type (int, vchar, ...) in the database!
- *
- * @param string $condition The condition which must be true, to use action_true rather then action_else
- * @param string $action_true SQL expression that is used, if the condition is true
- * @param string $action_else SQL expression that is used, if the condition is false, optional
- * @return string CASE expression including the condition and statements
+ * {@inheritDoc}
+ */
+ function sql_not_like_expression($expression)
+ {
+ $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
+
+ return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
+ }
+
+ /**
+ * {@inheritDoc}
*/
public function sql_case($condition, $action_true, $action_false = false)
{
@@ -297,11 +402,7 @@ class driver
}
/**
- * Build a concatenated expression
- *
- * @param string $expr1 Base SQL expression where we append the second one
- * @param string $expr2 SQL expression that is appended to the first expression
- * @return string Concatenated string
+ * {@inheritDoc}
*/
public function sql_concatenate($expr1, $expr2)
{
@@ -309,9 +410,7 @@ class driver
}
/**
- * Returns whether results of a query need to be buffered to run a transaction while iterating over them.
- *
- * @return bool Whether buffering is required.
+ * {@inheritDoc}
*/
function sql_buffer_nested_transactions()
{
@@ -319,15 +418,14 @@ class driver
}
/**
- * SQL Transaction
- * @access private
+ * {@inheritDoc}
*/
function sql_transaction($status = 'begin')
{
switch ($status)
{
case 'begin':
- // If we are within a transaction we will not open another one, but enclose the current one to not loose data (prevening auto commit)
+ // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
if ($this->transaction)
{
$this->transactions++;
@@ -345,14 +443,16 @@ class driver
break;
case 'commit':
- // If there was a previously opened transaction we do not commit yet... but count back the number of inner transactions
+ // If there was a previously opened transaction we do not commit yet...
+ // but count back the number of inner transactions
if ($this->transaction && $this->transactions)
{
$this->transactions--;
return true;
}
- // Check if there is a transaction (no transaction can happen if there was an error, with a combined rollback and error returning enabled)
+ // Check if there is a transaction (no transaction can happen if
+ // there was an error, with a combined rollback and error returning enabled)
// This implies we have transaction always set for autocommit db's
if (!$this->transaction)
{
@@ -385,11 +485,7 @@ class driver
}
/**
- * Build sql statement from array for insert/update/select statements
- *
- * Idea for this from Ikonboard
- * Possible query values: INSERT, INSERT_SELECT, UPDATE, SELECT
- *
+ * {@inheritDoc}
*/
function sql_build_array($query, $assoc_ary = false)
{
@@ -423,7 +519,7 @@ class driver
{
trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
}
- else if ($query == 'UPDATE' || $query == 'SELECT')
+ else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
{
$values = array();
foreach ($assoc_ary as $key => $var)
@@ -437,14 +533,7 @@ class driver
}
/**
- * Build IN or NOT IN sql comparison string, uses <> or = on single element
- * arrays to improve comparison speed
- *
- * @access public
- * @param string $field name of the sql column that shall be compared
- * @param array $array array of values that are allowed (IN) or not allowed (NOT IN)
- * @param bool $negate true for NOT IN (), false for IN () (default)
- * @param bool $allow_empty_set If true, allow $array to be empty, this function will return 1=1 or 1=0 then. Default to false.
+ * {@inheritDoc}
*/
function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
{
@@ -489,12 +578,7 @@ class driver
}
/**
- * Run binary AND operator on DB column.
- * Results in sql statement: "{$column_name} & (1 << {$bit}) {$compare}"
- *
- * @param string $column_name The column name to use
- * @param int $bit The value to use for the AND operator, will be converted to (1 << $bit). Is used by options, using the number schema... 0, 1, 2...29
- * @param string $compare Any custom SQL code after the check (for example "= 0")
+ * {@inheritDoc}
*/
function sql_bit_and($column_name, $bit, $compare = '')
{
@@ -507,12 +591,7 @@ class driver
}
/**
- * Run binary OR operator on DB column.
- * Results in sql statement: "{$column_name} | (1 << {$bit}) {$compare}"
- *
- * @param string $column_name The column name to use
- * @param int $bit The value to use for the OR operator, will be converted to (1 << $bit). Is used by options, using the number schema... 0, 1, 2...29
- * @param string $compare Any custom SQL code after the check (for example "= 0")
+ * {@inheritDoc}
*/
function sql_bit_or($column_name, $bit, $compare = '')
{
@@ -525,10 +604,7 @@ class driver
}
/**
- * Returns SQL string to cast a string expression to an int.
- *
- * @param string $expression An expression evaluating to string
- * @return string Expression returning an int
+ * {@inheritDoc}
*/
function cast_expr_to_bigint($expression)
{
@@ -536,10 +612,7 @@ class driver
}
/**
- * Returns SQL string to cast an integer expression to a string.
- *
- * @param string $expression An expression evaluating to int
- * @return string Expression returning a string
+ * {@inheritDoc}
*/
function cast_expr_to_string($expression)
{
@@ -547,11 +620,7 @@ class driver
}
/**
- * Run LOWER() on DB column of type text (i.e. neither varchar nor char).
- *
- * @param string $column_name The column name to use
- *
- * @return string A SQL statement like "LOWER($column_name)"
+ * {@inheritDoc}
*/
function sql_lower_text($column_name)
{
@@ -559,13 +628,7 @@ class driver
}
/**
- * Run more than one insert statement.
- *
- * @param string $table table name to run the statements on
- * @param array $sql_ary multi-dimensional array holding the statement data.
- *
- * @return bool false if no statements were executed.
- * @access public
+ * {@inheritDoc}
*/
function sql_multi_insert($table, $sql_ary)
{
@@ -637,9 +700,7 @@ class driver
}
/**
- * Build sql statement from array for select and select distinct statements
- *
- * Possible query values: SELECT, SELECT_DISTINCT
+ * {@inheritDoc}
*/
function sql_build_query($query, $array)
{
@@ -722,7 +783,18 @@ class driver
if (!empty($array['WHERE']))
{
- $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']);
+ $sql .= ' WHERE ';
+
+ if (is_array($array['WHERE']))
+ {
+ $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
+ }
+ else
+ {
+ $sql_where = $array['WHERE'];
+ }
+
+ $sql .= $this->_sql_custom_build('WHERE', $sql_where);
}
if (!empty($array['GROUP_BY']))
@@ -741,8 +813,132 @@ class driver
return $sql;
}
+
+ protected function _process_boolean_tree_first($operations_ary)
+ {
+ // In cases where an array exists but there is no head condition,
+ // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
+ if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
+ $operations_ary[self::LOGICAL_OP] !== 'OR')
+ {
+ $operations_ary = array('AND', array($operations_ary));
+ }
+ return $this->_process_boolean_tree($operations_ary) . "\n";
+ }
+
+ protected function _process_boolean_tree($operations_ary)
+ {
+ $operation = $operations_ary[self::LOGICAL_OP];
+
+ foreach ($operations_ary[self::STATEMENTS] as &$condition)
+ {
+ switch ($condition[self::LOGICAL_OP])
+ {
+ case 'AND':
+ case 'OR':
+
+ $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
+
+ break;
+ case 'NOT':
+
+ $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
+
+ break;
+
+ default:
+
+ switch (sizeof($condition))
+ {
+ case 3:
+
+ // Typical 3 element clause with {left hand} {operator} {right hand}
+ switch ($condition[self::COMPARE_OP])
+ {
+ case 'IN':
+ case 'NOT_IN':
+
+ // As this is used with an IN, assume it is a set of elements for sql_in_set()
+ $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
+
+ break;
+
+ case 'LIKE':
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
+
+ break;
+
+ case 'NOT_LIKE':
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
+
+ break;
+
+ case 'IS_NOT':
+
+ $condition[self::COMPARE_OP] = 'IS NOT';
+
+ // no break
+ case 'IS':
+
+ // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
+ // this should solve that
+ if ($condition[self::RIGHT_STMT] === null)
+ {
+ $condition[self::RIGHT_STMT] = 'NULL';
+ }
+
+ $condition = implode(' ', $condition);
+
+ break;
+
+ default:
+
+ $condition = implode(' ', $condition);
+
+ break;
+ }
+
+ break;
+
+ case 5:
+
+ // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
+ $condition .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
+ $condition .= ' )';
+
+ break;
+
+ default:
+ // This is an unpredicted clause setup. Just join all elements.
+ $condition = implode(' ', $condition);
+
+ break;
+ }
+
+ break;
+ }
+
+ }
+
+ if ($operation === 'NOT')
+ {
+ $operations_ary = implode("", $operations_ary[self::STATEMENTS]);
+ }
+ else
+ {
+ $operations_ary = implode(" \n $operation ", $operations_ary[self::STATEMENTS]);
+ }
+
+ return $operations_ary;
+ }
+
+
/**
- * display sql error page
+ * {@inheritDoc}
*/
function sql_error($sql = '')
{
@@ -812,11 +1008,11 @@ class driver
}
/**
- * Explain queries
+ * {@inheritDoc}
*/
function sql_report($mode, $query = '')
{
- global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper, $user;
+ global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
global $request;
if (is_object($request) && !$request->variable('explain', false))
@@ -872,7 +1068,7 @@ class driver
</div>
</div>
<div id="page-footer">
- Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Group
+ Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
</div>
</div>
</body>
@@ -910,7 +1106,7 @@ class driver
{
if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
{
- $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
}
$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>';
}
@@ -1002,14 +1198,7 @@ class driver
}
/**
- * Gets the estimated number of rows in a specified table.
- *
- * @param string $table_name Table name
- *
- * @return string Number of rows in $table_name.
- * Prefixed with ~ if estimated (otherwise exact).
- *
- * @access public
+ * {@inheritDoc}
*/
function get_estimated_row_count($table_name)
{
@@ -1017,13 +1206,7 @@ class driver
}
/**
- * Gets the exact number of rows in a specified table.
- *
- * @param string $table_name Table name
- *
- * @return string Exact number of rows in $table_name.
- *
- * @access public
+ * {@inheritDoc}
*/
function get_row_count($table_name)
{