Skip to content

Commit 6399bda

Browse files
authored
Merge pull request #7155 from kenjis/fix-QB-where-CustomString
fix: [QueryBuilder] getOperatorFromWhereKey() misses EXISTS, BETWEEN
2 parents 629eae1 + c160153 commit 6399bda

2 files changed

Lines changed: 78 additions & 18 deletions

File tree

system/Database/BaseBuilder.php

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -761,9 +761,9 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type
761761
$k = trim($k);
762762

763763
end($op);
764-
765764
$op = trim(current($op));
766765

766+
// Does the key end with operator?
767767
if (substr($k, -strlen($op)) === $op) {
768768
$k = rtrim(substr($k, 0, -strlen($op)));
769769
$op = " {$op}";
@@ -783,7 +783,15 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type
783783
} elseif (! $this->hasOperator($k) && $qbKey !== 'QBHaving') {
784784
// value appears not to have been set, assign the test to IS NULL
785785
$op = ' IS NULL';
786-
} elseif (preg_match('/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE)) {
786+
} elseif (
787+
// The key ends with !=, =, <>, IS, IS NOT
788+
preg_match(
789+
'/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i',
790+
$k,
791+
$match,
792+
PREG_OFFSET_CAPTURE
793+
)
794+
) {
787795
$k = substr($k, 0, $match[0][1]);
788796
$op = $match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL';
789797
} else {
@@ -3377,16 +3385,16 @@ protected function getOperator(string $str, bool $list = false)
33773385
: '';
33783386
$this->pregOperators = [
33793387
'\s*(?:<|>|!)?=\s*', // =, <=, >=, !=
3380-
'\s*<>?\s*', // <, <>
3381-
'\s*>\s*', // >
3382-
'\s+IS NULL', // IS NULL
3383-
'\s+IS NOT NULL', // IS NOT NULL
3384-
'\s+EXISTS\s*\(.*\)', // EXISTS(sql)
3388+
'\s*<>?\s*', // <, <>
3389+
'\s*>\s*', // >
3390+
'\s+IS NULL', // IS NULL
3391+
'\s+IS NOT NULL', // IS NOT NULL
3392+
'\s+EXISTS\s*\(.*\)', // EXISTS (sql)
33853393
'\s+NOT EXISTS\s*\(.*\)', // NOT EXISTS(sql)
3386-
'\s+BETWEEN\s+', // BETWEEN value AND value
3387-
'\s+IN\s*\(.*\)', // IN(list)
3388-
'\s+NOT IN\s*\(.*\)', // NOT IN (list)
3389-
'\s+LIKE\s+\S.*(' . $_les . ')?', // LIKE 'expr'[ ESCAPE '%s']
3394+
'\s+BETWEEN\s+', // BETWEEN value AND value
3395+
'\s+IN\s*\(.*\)', // IN (list)
3396+
'\s+NOT IN\s*\(.*\)', // NOT IN (list)
3397+
'\s+LIKE\s+\S.*(' . $_les . ')?', // LIKE 'expr'[ ESCAPE '%s']
33903398
'\s+NOT LIKE\s+\S.*(' . $_les . ')?', // NOT LIKE 'expr'[ ESCAPE '%s']
33913399
];
33923400
}
@@ -3409,13 +3417,18 @@ private function getOperatorFromWhereKey(string $whereKey)
34093417
$whereKey = trim($whereKey);
34103418

34113419
$pregOperators = [
3412-
'\s*(?:<|>|!)?=', // =, <=, >=, !=
3413-
'\s*<>?', // <, <>
3414-
'\s*>', // >
3415-
'\s+IS NULL', // IS NULL
3416-
'\s+IS NOT NULL', // IS NOT NULL
3417-
'\s+LIKE', // LIKE
3418-
'\s+NOT LIKE', // NOT LIKE
3420+
'\s*(?:<|>|!)?=', // =, <=, >=, !=
3421+
'\s*<>?', // <, <>
3422+
'\s*>', // >
3423+
'\s+IS NULL', // IS NULL
3424+
'\s+IS NOT NULL', // IS NOT NULL
3425+
'\s+EXISTS\s*\(.*\)', // EXISTS (sql)
3426+
'\s+NOT EXISTS\s*\(.*\)', // NOT EXISTS (sql)
3427+
'\s+BETWEEN\s+', // BETWEEN value AND value
3428+
'\s+IN\s*\(.*\)', // IN (list)
3429+
'\s+NOT IN\s*\(.*\)', // NOT IN (list)
3430+
'\s+LIKE', // LIKE
3431+
'\s+NOT LIKE', // NOT LIKE
34193432
];
34203433

34213434
return preg_match_all(

tests/system/Database/Builder/WhereTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,53 @@ public function testWhereCustomString()
160160
$this->assertSame($expectedBinds, $builder->getBinds());
161161
}
162162

163+
public function testWhereCustomStringWithOperatorEscapeFalse()
164+
{
165+
$builder = $this->db->table('jobs');
166+
167+
$where = 'CURRENT_TIMESTAMP() = DATE_ADD(column, INTERVAL 2 HOUR)';
168+
$builder->where($where, null, false);
169+
170+
$expectedSQL = 'SELECT * FROM "jobs" WHERE CURRENT_TIMESTAMP() = DATE_ADD(column, INTERVAL 2 HOUR)';
171+
$this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect()));
172+
173+
$expectedBinds = [];
174+
$this->assertSame($expectedBinds, $builder->getBinds());
175+
}
176+
177+
public function testWhereCustomStringWithoutOperatorEscapeFalse()
178+
{
179+
$builder = $this->db->table('jobs');
180+
181+
$where = "REPLACE(column, 'somestring', '')";
182+
$builder->where($where, "''", false);
183+
184+
$expectedSQL = "SELECT * FROM \"jobs\" WHERE REPLACE(column, 'somestring', '') = ''";
185+
$this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect()));
186+
187+
$expectedBinds = [
188+
"REPLACE(column, 'somestring', '')" => [
189+
0 => "''",
190+
1 => false,
191+
],
192+
];
193+
$this->assertSame($expectedBinds, $builder->getBinds());
194+
}
195+
196+
public function testWhereCustomStringWithBetweenEscapeFalse()
197+
{
198+
$builder = $this->db->table('jobs');
199+
200+
$where = "created_on BETWEEN '2022-07-01 00:00:00' AND '2022-12-31 23:59:59'";
201+
$builder->where($where, null, false);
202+
203+
$expectedSQL = "SELECT * FROM \"jobs\" WHERE created_on BETWEEN '2022-07-01 00:00:00' AND '2022-12-31 23:59:59'";
204+
$this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect()));
205+
206+
$expectedBinds = [];
207+
$this->assertSame($expectedBinds, $builder->getBinds());
208+
}
209+
163210
public function testWhereRawSql()
164211
{
165212
$builder = $this->db->table('jobs');

0 commit comments

Comments
 (0)