diff options
| -rw-r--r-- | phpBB/phpbb/db/driver/driver.php | 137 | ||||
| -rw-r--r-- | tests/dbal/boolean_processor_test.php | 306 | ||||
| -rw-r--r-- | tests/dbal/fixtures/boolean_processor.xml | 84 | 
3 files changed, 526 insertions, 1 deletions
| diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php index 2925765e94..e497e6dda1 100644 --- a/phpBB/phpbb/db/driver/driver.php +++ b/phpBB/phpbb/db/driver/driver.php @@ -774,7 +774,18 @@ abstract class driver implements driver_interface  				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'])) @@ -793,6 +804,130 @@ abstract class driver implements driver_interface  		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[0] !== 'AND' && +			$operations_ary[0] !== 'OR') +		{ +			$operations_ary = array('AND', $operations_ary); +		} +		return $this->_process_boolean_tree($operations_ary) . "\n"; +	} + +	protected function _process_boolean_tree($operations_ary) +	{ +		$operation = array_shift($operations_ary); + +		foreach ($operations_ary as &$condition) +		{ +			switch ($condition[0]) +			{ +				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[1]) +							{ +								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[0], $condition[2], $condition[1] === 'NOT_IN', true); + +								break; + +								case 'LIKE': + +									$condition = $condition[0] . ' ' . $this->sql_like_expression($condition[2]) . ' '; + +								break; + +								case 'NOT_LIKE': + +									$condition = $condition[0] . ' ' . $this->sql_not_like_expression($condition[2]) . ' '; + +								break; + +								case 'IS_NOT': + +									$condition[1] = '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[2] === null) +									{ +										$condition[2] = '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[0] . ' ' . $condition[1] . ' ' . $condition[2] . ' ( '; +							$condition .= $this->sql_build_query($condition[3], $condition[4]); +							$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); +		} +		else +		{ +			$operations_ary = implode(" \n	$operation ", $operations_ary); +		} + +		return $operations_ary; +	} + +  	/**  	* {@inheritDoc}  	*/ diff --git a/tests/dbal/boolean_processor_test.php b/tests/dbal/boolean_processor_test.php new file mode 100644 index 0000000000..8662485ac8 --- /dev/null +++ b/tests/dbal/boolean_processor_test.php @@ -0,0 +1,306 @@ +<?php +/** +* +* 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. +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/../../phpBB/includes/utf/utf_tools.php'; + +class phpbb_boolean_processor_test extends phpbb_database_test_case +{ +	public function getDataSet() +	{ +		return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/boolean_processor.xml'); +	} + +	public function test_single_not_like() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +			), +			'WHERE'		=> array('u.username_clean', 'NOT_LIKE', 'gr' . $db->get_any_char()), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '1'), +			array('user_id' => '2'), +			array('user_id' => '3'), +			array('user_id' => '6'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_single_like() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +			), +			'WHERE'		=> array('u.username_clean', 'LIKE', 'gr' . $db->get_any_char()), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '4'), +			array('user_id' => '5'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_single_not_in() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +			), +			'WHERE'		=> array('u.user_id', 'NOT_IN', array(3,4,5)), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '1'), +			array('user_id' => '2'), +			array('user_id' => '6'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_single_in() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +			), +			'WHERE'		=> array('u.user_id', 'IN', array(3,4,5)), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '3'), +			array('user_id' => '4'), +			array('user_id' => '5'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_and_of_or_of_and() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +				'phpbb_user_group'	=> 'ug', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array( +						'phpbb_banlist'	=> 'b', +					), +					'ON'	=> 'u.user_id = b.ban_userid', +				), +			), +			'WHERE'		=> array('AND', +				array('OR', +					array('AND', +						array('ug.user_id', 'IN', array(1, 2, 3, 4)), +						array('ug.group_id', '=', 2), +					), +					array('AND', +						array('ug.group_id', '=', 1), +						array('b.ban_id', 'IS_NOT', NULL), +					), +				), +				array('u.user_id', '=', 'ug.user_id'), +			), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '2'), +			array('user_id' => '4'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_triple_and_with_in() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +				'phpbb_user_group'	=> 'ug', +			), +			'WHERE'		=> array('AND', +				array('ug.user_id', 'IN', array(1, 2, 3, 4)), +				array('ug.group_id', '=', 1), +				array('u.user_id', '=', 'ug.user_id'), +			), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('user_id' => '1'), +			array('user_id' => '2'), +			array('user_id' => '3'), +			), $db->sql_fetchrowset($result), +			($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +		); + +	} + +	public function test_double_and_with_not_of_or() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.user_id', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +				'phpbb_user_group'	=> 'ug', +			), +			'WHERE'		=> array('AND', +				array('NOT', +					array('OR', +						array('ug.group_id', '=', 1), +						array('ug.group_id', '=', 2), +					), +				), +				array('u.user_id', '=', 'ug.user_id'), +			), +			'ORDER_BY'	=> 'u.user_id', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array(), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} + +	public function test_triple_and_with_is_null() +	{ +		$db = $this->new_dbal(); + +		$db->sql_return_on_error(true); + +		$sql_ary = array( +			'SELECT'	=> 'u.username', +			'FROM'		=> array( +				'phpbb_users'		=> 'u', +				'phpbb_user_group'	=> 'ug', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array( +						'phpbb_banlist'	=> 'b', +					), +					'ON'	=> 'u.user_id = b.ban_userid', +				), +			), +			'WHERE'		=> array('AND', +				array('ug.group_id', '=', 1), +				array('u.user_id', '=', 'ug.user_id'), +				array('b.ban_id', 'IS', NULL), +			), +			'ORDER_BY'	=> 'u.username', +		); +		$sql = $db->sql_build_query('SELECT', $sql_ary); +		$result = $db->sql_query($sql); + +		$db->sql_return_on_error(false); + +		$this->assertEquals(array( +			array('username' => 'helper'), +			array('username' => 'mass email'), +			), $db->sql_fetchrowset($result), +				($result === false) ? +				"SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : +				var_export($sql, true) . '   ' . var_export($result, true) +			); +	} +} diff --git a/tests/dbal/fixtures/boolean_processor.xml b/tests/dbal/fixtures/boolean_processor.xml new file mode 100644 index 0000000000..c5da677116 --- /dev/null +++ b/tests/dbal/fixtures/boolean_processor.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> +	<table name="phpbb_banlist"> +		<column>ban_id</column> +		<column>ban_userid</column> +		<row> +			<value>1</value> +			<value>2</value> +		</row> +	</table> +	<table name="phpbb_users"> +		<column>user_id</column> +		<column>username</column> +		<column>username_clean</column> +		<column>user_permissions</column> +		<column>user_sig</column> +		<row> +			<value>1</value> +			<value>mass email</value> +			<value>mass email</value> +			<value></value> +			<value></value> +		</row> +		<row> +			<value>2</value> +			<value>banned</value> +			<value>banned</value> +			<value></value> +			<value></value> +		</row> +		<row> +			<value>3</value> +			<value>helper</value> +			<value>helper</value> +			<value></value> +			<value></value> +		</row> +		<row> +			<value>4</value> +			<value>GroupBPal</value> +			<value>groupbpal</value> +			<value></value> +			<value></value> +		</row> +		<row> +			<value>5</value> +			<value>GroupBPal2</value> +			<value>groupBPal2</value> +			<value></value> +			<value></value> +		</row> +		<row> +			<value>6</value> +			<value>not in group</value> +			<value>not in group</value> +			<value></value> +			<value></value> +		</row> +	</table> +	<table name="phpbb_user_group"> +		<column>user_id</column> +		<column>group_id</column> +		<row> +			<value>1</value> +			<value>1</value> +		</row> +		<row> +			<value>2</value> +			<value>1</value> +		</row> +		<row> +			<value>3</value> +			<value>1</value> +		</row> +		<row> +			<value>4</value> +			<value>2</value> +		</row> +		<row> +			<value>5</value> +			<value>2</value> +		</row> +	</table> +</dataset> | 
