Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: BaseBuilder. Added methods that implement UNION. #4291

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 214 additions & 11 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,34 @@ class BaseBuilder
*/
protected $QBWhereGroupCount = 0;

/**
* Collection of union queries
*
* @var string[]
*/
protected $QBUnion = [];

/**
* UNION ORDER BY data
*
* @var array
*/
protected $QBUnionOrderBy = [];

/**
* QB UNION LIMIT data
*
* @var integer|boolean
*/
protected $QBUnionLimit = false;

/**
* QB UNION OFFSET data
*
* @var integer|boolean
*/
protected $QBUnionOffset = false;

/**
* Ignore data that cause certain
* exceptions, for example in case of
Expand Down Expand Up @@ -1620,6 +1648,11 @@ public function orHaving($key, $value = null, bool $escape = null)
*/
public function orderBy(string $orderBy, string $direction = '', bool $escape = null)
{
if (count($this->QBUnion))
{
return $this->unionOrderBy($orderBy, $direction, $escape);
}

$direction = strtoupper(trim($direction));

if ($direction === 'RANDOM')
Expand Down Expand Up @@ -1686,6 +1719,11 @@ public function orderBy(string $orderBy, string $direction = '', bool $escape =
*/
public function limit(?int $value = null, ?int $offset = 0)
{
if (count($this->QBUnion))
{
return $this->unionLimit($value, $offset);
}

if (! is_null($value))
{
$this->QBLimit = $value;
Expand Down Expand Up @@ -3022,7 +3060,17 @@ protected function compileSelect($selectOverride = false): string
// LIMIT
if ($this->QBLimit)
{
return $this->_limit($sql . "\n");
$sql = $this->_limit($sql . "\n");
}

if (count($this->QBUnion))
{
if ($this->QBOrderBy || $this->QBLimit)
{
$sql = 'SELECT * FROM (' . $sql . ') as wrapper_alias';
}

$sql .= $this->compileUnion() . $this->compileUnionFilter();
}

return $sql;
Expand Down Expand Up @@ -3358,16 +3406,20 @@ protected function resetRun(array $qbResetItems)
protected function resetSelect()
{
$this->resetRun([
'QBSelect' => [],
'QBJoin' => [],
'QBWhere' => [],
'QBGroupBy' => [],
'QBHaving' => [],
'QBOrderBy' => [],
'QBNoEscape' => [],
'QBDistinct' => false,
'QBLimit' => false,
'QBOffset' => false,
'QBSelect' => [],
'QBJoin' => [],
'QBWhere' => [],
'QBGroupBy' => [],
'QBHaving' => [],
'QBOrderBy' => [],
'QBNoEscape' => [],
'QBUnion' => [],
'QBUnionOrderBy' => [],
'QBDistinct' => false,
'QBLimit' => false,
'QBOffset' => false,
'QBUnionLimit' => false,
'QBUnionOffset' => false,
]);

if (! empty($this->db))
Expand Down Expand Up @@ -3492,6 +3544,157 @@ protected function setBind(string $key, $value = null, bool $escape = true): str
return $key . $count;
}

/**
* Union queries
*
* @param Closure $closure Contains a query to combine
*
* @return static
*/
public function union(Closure $closure)
{
return $this->unionBuilder($closure);
}

/**
* Union queries
*
* @param Closure $closure Contains a query to combine
*
* @return static
*/
public function unionAll(Closure $closure)
{
return $this->unionBuilder($closure, true);
}

/**
* Union query builder
*
* @param Closure $closure Contains a query to combine
* @param boolean $all Using UNION ALL construction
*
* @return static
*/
protected function unionBuilder(Closure $closure, bool $all = false)
{
$builder = $closure($this->cleanClone());

if (! ($builder instanceof BaseBuilder))
{
throw new DatabaseException(
'BaseBuilder::union(). The closure must return an instance of the BaseBuilder class'
);
}

$sql = $builder->getCompiledSelect(false);

$all = $all ? 'ALL ' : '';

if (($builder->QBOrderBy || $builder->QBLimit || $builder->QBUnionOrderBy || $builder->QBUnionLimit)
&& strpos($sql, 'wrapper_alias UNION') === false)
{
$sql = 'SELECT * FROM (' . $sql . ') as wrapper_alias';
}

$builder->resetSelect();

$this->QBUnion[] = ' UNION ' . $all . $sql;

return $this;
}

/**
* Compile UNION queries
*
* @return string
*/
protected function compileUnion() : string
{
return count($this->QBUnion) ? join('', $this->QBUnion) : '';
}

/**
* UNION ORDER BY global
*
* @param string $orderBy Field
* @param string $direction ASC, DESC or RANDOM
* @param boolean $escape Escape
*
* @return $this
*/
protected function unionOrderBy(string $orderBy, string $direction = '', bool $escape = null)
{
$this->QBUnionOrderBy[] = [
$orderBy,
$direction,
$escape,
];

return $this;
}

/**
* UNION LIMIT global
*
* @param integer|null $value LIMIT value
* @param integer|null $offset OFFSET value
*
* @return $this
*/
protected function unionLimit(?int $value = null, ?int $offset = 0)
{
if (! is_null($value))
{
$this->QBUnionLimit = $value;
}

return $this->unionOffset($offset);
}

/**
* Sets the UNION OFFSET value
*
* @param integer $offset OFFSET value
*
* @return $this
*/
protected function unionOffset(int $offset)
{
if (! empty($offset))
{
$this->QBUnionOffset = (int) $offset;
}

return $this;
}

/**
* Compile UNION Filter as ORDER BY and LIMIT
*
* @return string
*/
protected function compileUnionFilter() : string
{
$builder = $this->cleanClone();

foreach ($this->QBUnionOrderBy as $order)
{
$builder->orderBy(...$order);
}

$filter = $builder->compileOrderBy();

$builder->limit($this->QBUnionLimit, $this->QBUnionOffset);

if ($this->QBUnionLimit)
{
$filter = $builder->_limit($filter . "\n");
}

return $filter;
}

//--------------------------------------------------------------------

/**
Expand Down
12 changes: 11 additions & 1 deletion system/Database/SQLSRV/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,17 @@ protected function compileSelect($selectOverride = false): string
// LIMIT
if ($this->QBLimit)
{
return $sql = $this->_limit($sql . "\n");
$sql = $this->_limit($sql . "\n");
}

if ($this->QBUnion)
{
if ($this->QBOrderBy || $this->QBLimit)
{
$sql = 'SELECT * FROM (' . $sql . ') as wrapper_alias';
}

$sql .= $this->compileUnion() . $this->compileUnionFilter();
}

return $sql;
Expand Down
Loading