diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index be1aee7e1a60..24939d03e607 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -579,9 +579,11 @@ public function fromSubquery(BaseBuilder $from, string $alias): self /** * Generates the JOIN portion of the query * + * @param RawSql|string $cond + * * @return $this */ - public function join(string $table, string $cond, string $type = '', ?bool $escape = null) + public function join(string $table, $cond, string $type = '', ?bool $escape = null) { if ($type !== '') { $type = strtoupper(trim($type)); @@ -601,6 +603,17 @@ public function join(string $table, string $cond, string $type = '', ?bool $esca $escape = $this->db->protectIdentifiers; } + // Do we want to escape the table name? + if ($escape === true) { + $table = $this->db->protectIdentifiers($table, true, null, false); + } + + if ($cond instanceof RawSql) { + $this->QBJoin[] = $type . 'JOIN ' . $table . ' ON ' . $cond; + + return $this; + } + if (! $this->hasOperator($cond)) { $cond = ' USING (' . ($escape ? $this->db->escapeIdentifiers($cond) : $cond) . ')'; } elseif ($escape === false) { @@ -634,11 +647,6 @@ public function join(string $table, string $cond, string $type = '', ?bool $esca } } - // Do we want to escape the table name? - if ($escape === true) { - $table = $this->db->protectIdentifiers($table, true, null, false); - } - // Assemble the JOIN statement $this->QBJoin[] = $type . 'JOIN ' . $table . $cond; diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 80e9f278cf50..1d80f687568c 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -13,6 +13,7 @@ use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\RawSql; /** * Builder for Postgre @@ -300,9 +301,11 @@ protected function _like_statement(?string $prefix, string $column, ?string $not /** * Generates the JOIN portion of the query * + * @param RawSql|string $cond + * * @return BaseBuilder */ - public function join(string $table, string $cond, string $type = '', ?bool $escape = null) + public function join(string $table, $cond, string $type = '', ?bool $escape = null) { if (! in_array('FULL OUTER', $this->joinTypes, true)) { $this->joinTypes = array_merge($this->joinTypes, ['FULL OUTER']); diff --git a/system/Database/SQLSRV/Builder.php b/system/Database/SQLSRV/Builder.php index 55da29b7bf85..f84d1dfddd07 100755 --- a/system/Database/SQLSRV/Builder.php +++ b/system/Database/SQLSRV/Builder.php @@ -14,6 +14,7 @@ use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Exceptions\DataException; +use CodeIgniter\Database\RawSql; use CodeIgniter\Database\ResultInterface; /** @@ -88,9 +89,11 @@ protected function _truncate(string $table): string /** * Generates the JOIN portion of the query * + * @param RawSql|string $cond + * * @return $this */ - public function join(string $table, string $cond, string $type = '', ?bool $escape = null) + public function join(string $table, $cond, string $type = '', ?bool $escape = null) { if ($type !== '') { $type = strtoupper(trim($type)); diff --git a/tests/system/Database/Builder/JoinTest.php b/tests/system/Database/Builder/JoinTest.php index 7ebb188adf6d..59857c25ab98 100644 --- a/tests/system/Database/Builder/JoinTest.php +++ b/tests/system/Database/Builder/JoinTest.php @@ -13,6 +13,7 @@ use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Database\Postgre\Builder as PostgreBuilder; +use CodeIgniter\Database\RawSql; use CodeIgniter\Database\SQLSRV\Builder as SQLSRVBuilder; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\Mock\MockConnection; @@ -75,6 +76,28 @@ public function testJoinMultipleConditions() $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); } + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/3832 + */ + public function testJoinRawSql() + { + $builder = new BaseBuilder('device', $this->db); + + $sql = 'user.id = device.user_id + AND ( + (1=1 OR 1=1) + OR + (1=1 OR 1=1) + )'; + $builder->join('user', new RawSql($sql), 'LEFT'); + + $expectedSQL = 'SELECT * FROM "device" LEFT JOIN "user" ON user.id = device.user_id AND ( (1=1 OR 1=1) OR (1=1 OR 1=1) )'; + + $output = str_replace("\n", ' ', $builder->getCompiledSelect()); + $output = preg_replace('/\s+/', ' ', $output); + $this->assertSame($expectedSQL, $output); + } + public function testFullOuterJoin() { $builder = new PostgreBuilder('jobs', $this->db);