From 6b60153ab4ac036bcd4eaaa90806d9898fc1e9a2 Mon Sep 17 00:00:00 2001 From: Geolim4 Date: Fri, 8 Aug 2014 15:06:12 +0200 Subject: [PATCH 1/5] [ticket/12671] Possibility to use NOT LIKE expression PHPBB3-12671 --- phpBB/phpbb/db/driver/driver.php | 11 ++++++ phpBB/phpbb/db/driver/driver_interface.php | 10 ++++++ phpBB/phpbb/db/driver/factory.php | 8 +++++ phpBB/phpbb/db/driver/mssql.php | 9 +++++ phpBB/phpbb/db/driver/mssql_base.php | 9 +++++ phpBB/phpbb/db/driver/mysql_base.php | 9 +++++ phpBB/phpbb/db/driver/oracle.php | 9 +++++ phpBB/phpbb/db/driver/postgres.php | 9 +++++ phpBB/phpbb/db/driver/sqlite.php | 17 +++++++++ tests/dbal/select_test.php | 42 ++++++++++++++++++++++ tests/di/create_container_test.php | 4 +++ 11 files changed, 137 insertions(+) diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php index 3e9110d8bc..ed650bd6a9 100644 --- a/phpBB/phpbb/db/driver/driver.php +++ b/phpBB/phpbb/db/driver/driver.php @@ -369,6 +369,17 @@ abstract class driver implements driver_interface return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\''); } + /** + * {@inheritDoc} + */ + function sql_not_like_expression($expression) + { + $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression); + $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression); + + return $this->_sql_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\''); + } + /** * {@inheritDoc} */ diff --git a/phpBB/phpbb/db/driver/driver_interface.php b/phpBB/phpbb/db/driver/driver_interface.php index 6722d059a5..8b487c5d42 100644 --- a/phpBB/phpbb/db/driver/driver_interface.php +++ b/phpBB/phpbb/db/driver/driver_interface.php @@ -418,6 +418,16 @@ interface driver_interface */ public function sql_like_expression($expression); + /** + * Correctly adjust NOT 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 A SQL statement like: "NOT LIKE 'bertie_%'" + */ + public function sql_not_like_expression($expression); + /** * Explain queries * diff --git a/phpBB/phpbb/db/driver/factory.php b/phpBB/phpbb/db/driver/factory.php index f0fa18051b..fb3a826254 100644 --- a/phpBB/phpbb/db/driver/factory.php +++ b/phpBB/phpbb/db/driver/factory.php @@ -417,6 +417,14 @@ class factory implements driver_interface return $this->get_driver()->sql_like_expression($expression); } + /** + * {@inheritdoc} + */ + public function sql_not_like_expression($expression) + { + return $this->get_driver()->sql_not_like_expression($expression); + } + /** * {@inheritdoc} */ diff --git a/phpBB/phpbb/db/driver/mssql.php b/phpBB/phpbb/db/driver/mssql.php index 268463a151..f9ea884ce2 100644 --- a/phpBB/phpbb/db/driver/mssql.php +++ b/phpBB/phpbb/db/driver/mssql.php @@ -350,6 +350,15 @@ class mssql extends \phpbb\db\driver\driver return $expression . " ESCAPE '\\'"; } + /** + * Build NOT LIKE expression + * @access private + */ + function _sql_not_like_expression($expression) + { + return $expression . " ESCAPE '\\'"; + } + /** * return sql error array * @access private diff --git a/phpBB/phpbb/db/driver/mssql_base.php b/phpBB/phpbb/db/driver/mssql_base.php index e7101903b8..514df9eaca 100644 --- a/phpBB/phpbb/db/driver/mssql_base.php +++ b/phpBB/phpbb/db/driver/mssql_base.php @@ -51,6 +51,15 @@ abstract class mssql_base extends \phpbb\db\driver\driver return $expression . " ESCAPE '\\'"; } + /** + * Build NOT LIKE expression + * @access private + */ + function _sql_not_like_expression($expression) + { + return $expression . " ESCAPE '\\'"; + } + /** * Build db-specific query data * @access private diff --git a/phpBB/phpbb/db/driver/mysql_base.php b/phpBB/phpbb/db/driver/mysql_base.php index e7c9b63f20..5e0b359134 100644 --- a/phpBB/phpbb/db/driver/mysql_base.php +++ b/phpBB/phpbb/db/driver/mysql_base.php @@ -111,6 +111,15 @@ abstract class mysql_base extends \phpbb\db\driver\driver return $expression; } + /** + * Build NOT LIKE expression + * @access private + */ + function _sql_not_like_expression($expression) + { + return $expression; + } + /** * Build db-specific query data * @access private diff --git a/phpBB/phpbb/db/driver/oracle.php b/phpBB/phpbb/db/driver/oracle.php index d1a186f1ba..6dcab5dd7d 100644 --- a/phpBB/phpbb/db/driver/oracle.php +++ b/phpBB/phpbb/db/driver/oracle.php @@ -645,6 +645,15 @@ class oracle extends \phpbb\db\driver\driver return $expression . " ESCAPE '\\'"; } + /** + * Build NOT LIKE expression + * @access private + */ + function _sql_not_like_expression($expression) + { + return $expression . " ESCAPE '\\'"; + } + function _sql_custom_build($stage, $data) { return $data; diff --git a/phpBB/phpbb/db/driver/postgres.php b/phpBB/phpbb/db/driver/postgres.php index 83e9fa51f6..a3b9aa4c6b 100644 --- a/phpBB/phpbb/db/driver/postgres.php +++ b/phpBB/phpbb/db/driver/postgres.php @@ -370,6 +370,15 @@ class postgres extends \phpbb\db\driver\driver return $expression; } + /** + * Build NOT LIKE expression + * @access private + */ + function _sql_not_like_expression($expression) + { + return $expression; + } + /** * {@inheritDoc} */ diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php index 2112e5ba2f..841662c1ed 100644 --- a/phpBB/phpbb/db/driver/sqlite.php +++ b/phpBB/phpbb/db/driver/sqlite.php @@ -287,6 +287,23 @@ class sqlite extends \phpbb\db\driver\driver return 'GLOB \'' . $this->sql_escape($expression) . '\''; } + /** + * {@inheritDoc} + * + * For SQLite an underscore is a not-known character... this may change with SQLite3 + */ + function sql_not_like_expression($expression) + { + // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // We only catch * and ? here, not the character map possible on file globbing. + $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); + + $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); + $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); + + return 'GLOB \'' . $this->sql_escape($expression) . '\''; + } + /** * return sql error array * @access private diff --git a/tests/dbal/select_test.php b/tests/dbal/select_test.php index e480716a49..ce07d7d0fb 100644 --- a/tests/dbal/select_test.php +++ b/tests/dbal/select_test.php @@ -233,6 +233,48 @@ class phpbb_dbal_select_test extends phpbb_database_test_case $db->sql_freeresult($result); } + public function not_like_expression_data() + { + // * = any_char; # = one_char + return array( + array('barfoo', array(array('username_clean' => 'bertie'), + array('username_clean' => 'foobar'))), + array('bar', array(array('username_clean' => 'bertie'),)), + array('bar*', array(array('username_clean' => 'bertie'), + array('username_clean' => 'foobar'))), + array('*bar*', array(array('username_clean' => 'bertie'))), + array('b*r', array(array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar'))), + array('b*e', array(array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar'))), + array('#b*e', array(array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar'))), + array('b####e', array(array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar'))), + ); + } + + /** + * @dataProvider not_like_expression_data + */ + public function test_not_like_expression($like_expression, $expected) + { + $db = $this->new_dbal(); + + $like_expression = str_replace('*', $db->get_any_char(), $like_expression); + $like_expression = str_replace('#', $db->get_one_char(), $like_expression); + $where = ($like_expression) ? 'username_clean ' . $db->sql_not_like_expression($like_expression) : ''; + + $result = $db->sql_query('SELECT username_clean + FROM phpbb_users + ' . (($where) ? ' WHERE ' . $where : '') . ' + ORDER BY user_id ASC'); + + $this->assertEquals($expected, $db->sql_fetchrowset($result)); + + $db->sql_freeresult($result); + } + public function in_set_data() { return array( diff --git a/tests/di/create_container_test.php b/tests/di/create_container_test.php index 559c0b122c..4ae6017989 100644 --- a/tests/di/create_container_test.php +++ b/tests/di/create_container_test.php @@ -191,6 +191,10 @@ namespace phpbb\db\driver { } + function sql_not_like_expression($expression) + { + } + function sql_fetchrowset($query_id = false) { return array( From 01943adbf72e137b9dfbda3ac85120c21a30fab8 Mon Sep 17 00:00:00 2001 From: Geolim4 Date: Fri, 8 Aug 2014 18:32:17 +0200 Subject: [PATCH 2/5] [ticket/12671] Possibility to use NOT LIKE expression PHPBB3-12671 --- phpBB/phpbb/db/driver/driver.php | 2 +- phpBB/phpbb/db/driver/sqlite3.php | 17 ++++++++++++++ tests/dbal/select_test.php | 38 ++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php index ed650bd6a9..9fc04d47a1 100644 --- a/phpBB/phpbb/db/driver/driver.php +++ b/phpBB/phpbb/db/driver/driver.php @@ -377,7 +377,7 @@ abstract class driver implements driver_interface $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression); $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression); - return $this->_sql_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\''); + return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\''); } /** diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php index 0922229e0a..f79245afb1 100644 --- a/phpBB/phpbb/db/driver/sqlite3.php +++ b/phpBB/phpbb/db/driver/sqlite3.php @@ -274,6 +274,23 @@ class sqlite3 extends \phpbb\db\driver\driver return 'GLOB \'' . $this->sql_escape($expression) . '\''; } + /** + * {@inheritDoc} + * + * For SQLite an underscore is a not-known character... + */ + public function sql_not_like_expression($expression) + { + // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // We only catch * and ? here, not the character map possible on file globbing. + $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); + + $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); + $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); + + return 'GLOB \'' . $this->sql_escape($expression) . '\''; + } + /** * return sql error array * diff --git a/tests/dbal/select_test.php b/tests/dbal/select_test.php index ce07d7d0fb..50ac5eb4d5 100644 --- a/tests/dbal/select_test.php +++ b/tests/dbal/select_test.php @@ -237,20 +237,32 @@ class phpbb_dbal_select_test extends phpbb_database_test_case { // * = any_char; # = one_char return array( - array('barfoo', array(array('username_clean' => 'bertie'), - array('username_clean' => 'foobar'))), - array('bar', array(array('username_clean' => 'bertie'),)), - array('bar*', array(array('username_clean' => 'bertie'), - array('username_clean' => 'foobar'))), + array('barfoo', array( + array('username_clean' => 'foobar'), + array('username_clean' => 'bertie')) + ), + array('bar', array(array('username_clean' => 'bertie'))), + array('bar*', array( + array('username_clean' => 'foobar'), + array('username_clean' => 'bertie')) + ), array('*bar*', array(array('username_clean' => 'bertie'))), - array('b*r', array(array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar'))), - array('b*e', array(array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar'))), - array('#b*e', array(array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar'))), - array('b####e', array(array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar'))), + array('b*r', array( + array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar')) + ), + array('b*e', array( + array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar')) + ), + array('#b*e', array( + array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar')) + ), + array('b####e', array( + array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar')) + ), ); } From 4c06467777547d72a6ea936d024d85cae701189b Mon Sep 17 00:00:00 2001 From: Geolim4 Date: Sat, 9 Aug 2014 12:26:11 +0200 Subject: [PATCH 3/5] [ticket/12671] Possibility to use NOT LIKE expression PHPBB3-12671 --- tests/dbal/select_test.php | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/dbal/select_test.php b/tests/dbal/select_test.php index 50ac5eb4d5..b7074552ba 100644 --- a/tests/dbal/select_test.php +++ b/tests/dbal/select_test.php @@ -239,9 +239,13 @@ class phpbb_dbal_select_test extends phpbb_database_test_case return array( array('barfoo', array( array('username_clean' => 'foobar'), - array('username_clean' => 'bertie')) - ), - array('bar', array(array('username_clean' => 'bertie'))), + array('username_clean' => 'bertie') + )), + array('bar', array( + array('username_clean' => 'barfoo'), + array('username_clean' => 'foobar'), + array('username_clean' => 'bertie'), + )), array('bar*', array( array('username_clean' => 'foobar'), array('username_clean' => 'bertie')) @@ -249,20 +253,22 @@ class phpbb_dbal_select_test extends phpbb_database_test_case array('*bar*', array(array('username_clean' => 'bertie'))), array('b*r', array( array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar')) - ), + array('username_clean' => 'foobar'), + array('username_clean' => 'bertie') + )), array('b*e', array( array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar')) - ), + array('username_clean' => 'foobar') + )), array('#b*e', array( array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar')) - ), + array('username_clean' => 'foobar'), + array('username_clean' => 'bertie') + )), array('b####e', array( array('username_clean' => 'barfoo'), - array('username_clean' => 'foobar')) - ), + array('username_clean' => 'foobar') + )), ); } From 635cde218c786f76397c3354e59f96a8a151c132 Mon Sep 17 00:00:00 2001 From: Geolim4 Date: Sat, 9 Aug 2014 15:04:39 +0200 Subject: [PATCH 4/5] [ticket/12671] Possibility to use NOT LIKE expression PHPBB3-12671 --- phpBB/phpbb/db/driver/sqlite.php | 4 ++-- phpBB/phpbb/db/driver/sqlite3.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php index 841662c1ed..68372200b6 100644 --- a/phpBB/phpbb/db/driver/sqlite.php +++ b/phpBB/phpbb/db/driver/sqlite.php @@ -294,14 +294,14 @@ class sqlite extends \phpbb\db\driver\driver */ function sql_not_like_expression($expression) { - // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike NOT LIKE, NOT GLOB is case sensitive (unfortunatly). SQLite users need to live with it! // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); - return 'GLOB \'' . $this->sql_escape($expression) . '\''; + return 'NOT GLOB \'' . $this->sql_escape($expression) . '\''; } /** diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php index f79245afb1..2bf4ed51aa 100644 --- a/phpBB/phpbb/db/driver/sqlite3.php +++ b/phpBB/phpbb/db/driver/sqlite3.php @@ -281,14 +281,14 @@ class sqlite3 extends \phpbb\db\driver\driver */ public function sql_not_like_expression($expression) { - // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike NOT LIKE,NOT GLOB is case sensitive (unfortunatly). SQLite users need to live with it! // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); - return 'GLOB \'' . $this->sql_escape($expression) . '\''; + return 'NOT GLOB \'' . $this->sql_escape($expression) . '\''; } /** From db0815f680d4ed9da845facb6e8e3975ac14621e Mon Sep 17 00:00:00 2001 From: Geolim4 Date: Sat, 9 Aug 2014 19:11:21 +0200 Subject: [PATCH 5/5] [ticket/12671] Possibility to use NOT LIKE expression PHPBB3-12671 --- phpBB/phpbb/db/driver/sqlite.php | 6 +++--- phpBB/phpbb/db/driver/sqlite3.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php index 68372200b6..d5da0e2438 100644 --- a/phpBB/phpbb/db/driver/sqlite.php +++ b/phpBB/phpbb/db/driver/sqlite.php @@ -277,7 +277,7 @@ class sqlite extends \phpbb\db\driver\driver */ function sql_like_expression($expression) { - // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike LIKE, GLOB is unfortunately case sensitive. // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); @@ -290,11 +290,11 @@ class sqlite extends \phpbb\db\driver\driver /** * {@inheritDoc} * - * For SQLite an underscore is a not-known character... this may change with SQLite3 + * For SQLite an underscore is a not-known character... */ function sql_not_like_expression($expression) { - // Unlike NOT LIKE, NOT GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike NOT LIKE, NOT GLOB is unfortunately case sensitive. // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php index 2bf4ed51aa..4e3e0d3329 100644 --- a/phpBB/phpbb/db/driver/sqlite3.php +++ b/phpBB/phpbb/db/driver/sqlite3.php @@ -260,11 +260,11 @@ class sqlite3 extends \phpbb\db\driver\driver /** * {@inheritDoc} * - * For SQLite an underscore is a not-known character... + * For SQLite an underscore is an unknown character. */ public function sql_like_expression($expression) { - // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike LIKE, GLOB is unfortunately case sensitive. // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); @@ -277,11 +277,11 @@ class sqlite3 extends \phpbb\db\driver\driver /** * {@inheritDoc} * - * For SQLite an underscore is a not-known character... + * For SQLite an underscore is an unknown character. */ public function sql_not_like_expression($expression) { - // Unlike NOT LIKE,NOT GLOB is case sensitive (unfortunatly). SQLite users need to live with it! + // Unlike NOT LIKE, NOT GLOB is unfortunately case sensitive // We only catch * and ? here, not the character map possible on file globbing. $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);