Replace all wildcard columns with '1' in count-only subquery #1575

This commit is contained in:
Kijin Sung 2021-01-29 14:40:28 +09:00
parent 96eae68105
commit 957be16a25
4 changed files with 44 additions and 20 deletions

View file

@ -408,7 +408,7 @@ class DB
// Get the COUNT(*) query string and parameters. // Get the COUNT(*) query string and parameters.
try try
{ {
$query_string = $query->getQueryString($this->_prefix, $args, [], true); $query_string = $query->getQueryString($this->_prefix, $args, [], 1);
$query_params = $query->getQueryParams(); $query_params = $query->getQueryParams();
} }
catch (Exceptions\QueryError $e) catch (Exceptions\QueryError $e)

View file

@ -50,10 +50,10 @@ class Query extends VariableBase
* @param string $prefix * @param string $prefix
* @param array $args * @param array $args
* @param array $column_list * @param array $column_list
* @param bool $count_only * @param int $count_only
* @return string * @return string
*/ */
public function getQueryString(string $prefix = '', array $args, array $column_list = [], bool $count_only = false): string public function getQueryString(string $prefix = '', array $args, array $column_list = [], int $count_only = 0): string
{ {
// Save the query information. // Save the query information.
$this->_prefix = $prefix; $this->_prefix = $prefix;
@ -110,10 +110,10 @@ class Query extends VariableBase
/** /**
* Generate a SELECT query string. * Generate a SELECT query string.
* *
* @param bool $count_only * @param int $count_only
* @return string * @return string
*/ */
protected function _getSelectQueryString(bool $count_only = false): string protected function _getSelectQueryString(int $count_only = 0): string
{ {
// Initialize the query string. // Initialize the query string.
$result = 'SELECT'; $result = 'SELECT';
@ -133,18 +133,23 @@ class Query extends VariableBase
{ {
if ($column instanceof self) if ($column instanceof self)
{ {
$subquery = $column->getQueryString($this->_prefix, $this->_args); $has_subquery_columns = true;
$subquery_count_only = $count_only ? $count_only + 1 : 0;
$subquery = $column->getQueryString($this->_prefix, $this->_args, [], $subquery_count_only);
foreach ($column->getQueryParams() as $param) foreach ($column->getQueryParams() as $param)
{ {
$this->_params[] = $param; $this->_params[] = $param;
} }
$columns[] = sprintf('(%s) AS %s', $subquery, self::quoteName($column->alias)); $columns[] = sprintf('(%s) AS %s', $subquery, self::quoteName($column->alias));
$has_subquery_columns = true;
} }
elseif ($column->is_expression && !$column->is_wildcard) elseif ($column->is_expression && !$column->is_wildcard)
{ {
$columns[] = $column->name . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : ''); $columns[] = $column->name . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : '');
} }
elseif ($column->is_wildcard && $count_only >= 1 && !$this->select_distinct)
{
$columns[] = '1';
}
else else
{ {
$columns[] = self::quoteName($column->name) . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : ''); $columns[] = self::quoteName($column->name) . ($column->alias ? (' AS ' . self::quoteName($column->alias)) : '');
@ -154,19 +159,12 @@ class Query extends VariableBase
} }
// Replace the column list if this is a count-only query. // Replace the column list if this is a count-only query.
if ($count_only) if ($count_only == 1)
{ {
$count_wrap = ($this->groupby || $this->select_distinct || $has_subquery_columns || preg_match('/\bDISTINCT\b/i', $column_list)); $count_wrap = ($this->groupby || $this->select_distinct || $has_subquery_columns || preg_match('/\bDISTINCT\b/i', $column_list));
if ($count_wrap) if ($count_wrap)
{ {
if ($column_list === '*' || preg_match('/\\.\\*/', $column_list)) $result .= ($this->select_distinct ? ' DISTINCT ' : ' ') . $column_list;
{
$result .= ' 1';
}
else
{
$result .= ($this->select_distinct ? ' DISTINCT ' : ' ') . $column_list;
}
} }
else else
{ {
@ -234,13 +232,13 @@ class Query extends VariableBase
} }
// Compose the ORDER BY clause. // Compose the ORDER BY clause.
if ($this->navigation && count($this->navigation->orderby) && !$count_only) if ($this->navigation && count($this->navigation->orderby) && ($count_only != 1 || $count_wrap))
{ {
$result .= ' ORDER BY ' . $this->_arrangeOrderBy($this->navigation); $result .= ' ORDER BY ' . $this->_arrangeOrderBy($this->navigation);
} }
// Compose the LIMIT/OFFSET clause. // Compose the LIMIT/OFFSET clause.
if ($this->navigation && $this->navigation->list_count && !$count_only) if ($this->navigation && $this->navigation->list_count && ($count_only != 1 || $count_wrap))
{ {
$result .= ' LIMIT ' . $this->_arrangeLimitOffset($this->navigation); $result .= ' LIMIT ' . $this->_arrangeLimitOffset($this->navigation);
} }

View file

@ -9,6 +9,8 @@
<table name="documents" /> <table name="documents" />
</tables> </tables>
<columns> <columns>
<column name="documents.document_srl" />
<column name="documents.*" />
<column name="COUNT(*)" alias="count" /> <column name="COUNT(*)" alias="count" />
</columns> </columns>
<conditions> <conditions>

View file

@ -56,6 +56,14 @@ class DBQueryParserTest extends \Codeception\TestCase\Test
'WHERE `member_srl` IN (?) AND (`regdate` >= ? OR `status` = ?) ' . 'WHERE `member_srl` IN (?) AND (`regdate` >= ? OR `status` = ?) ' .
'ORDER BY `list_order` ASC LIMIT 40, 20', $sql); 'ORDER BY `list_order` ASC LIMIT 40, 20', $sql);
$this->assertEquals(['1234', '20200707120000', 'PUBLIC'], $params); $this->assertEquals(['1234', '20200707120000', 'PUBLIC'], $params);
$sql = $query->getQueryString('rx_', $args, [], 1);
$params = $query->getQueryParams();
$this->assertEquals('SELECT COUNT(*) AS `count` FROM (SELECT DISTINCT * FROM `rx_documents` AS `documents` ' .
'WHERE `member_srl` IN (?) AND (`regdate` >= ? OR `status` = ?) ' .
'ORDER BY `list_order` ASC LIMIT 40, 20) AS `subquery`', $sql);
$this->assertEquals(['1234', '20200707120000', 'PUBLIC'], $params);
} }
public function testSelectWithExpressions() public function testSelectWithExpressions()
@ -220,18 +228,34 @@ class DBQueryParserTest extends \Codeception\TestCase\Test
$this->assertTrue($query->tables['member'] instanceof Rhymix\Framework\Parsers\DBQuery\Table); $this->assertTrue($query->tables['member'] instanceof Rhymix\Framework\Parsers\DBQuery\Table);
$this->assertEquals(2, count($query->columns)); $this->assertEquals(2, count($query->columns));
$this->assertTrue($query->columns[0] instanceof Rhymix\Framework\Parsers\DBQuery\ColumnRead); $this->assertTrue($query->columns[0] instanceof Rhymix\Framework\Parsers\DBQuery\ColumnRead);
$this->assertTrue($query->columns[0]->is_expression);
$this->assertTrue($query->columns[0]->is_wildcard);
$this->assertTrue($query->columns[1] instanceof Rhymix\Framework\Parsers\DBQuery\Query); $this->assertTrue($query->columns[1] instanceof Rhymix\Framework\Parsers\DBQuery\Query);
$this->assertTrue($query->columns[1]->tables['documents'] instanceof Rhymix\Framework\Parsers\DBQuery\Table); $this->assertTrue($query->columns[1]->tables['documents'] instanceof Rhymix\Framework\Parsers\DBQuery\Table);
$this->assertTrue($query->columns[1]->columns[0] instanceof Rhymix\Framework\Parsers\DBQuery\ColumnRead); $this->assertTrue($query->columns[1]->columns[0] instanceof Rhymix\Framework\Parsers\DBQuery\ColumnRead);
$this->assertTrue($query->columns[1]->columns[0]->is_expression); $this->assertFalse($query->columns[1]->columns[0]->is_expression);
$this->assertFalse($query->columns[1]->columns[0]->is_wildcard); $this->assertFalse($query->columns[1]->columns[0]->is_wildcard);
$this->assertTrue($query->columns[1]->columns[1]->is_expression);
$this->assertTrue($query->columns[1]->columns[1]->is_wildcard);
$this->assertTrue($query->columns[1]->columns[2]->is_expression);
$this->assertFalse($query->columns[1]->columns[2]->is_wildcard);
$sql = $query->getQueryString('rx_', []); $sql = $query->getQueryString('rx_', []);
$params = $query->getQueryParams(); $params = $query->getQueryParams();
$this->assertEquals('SELECT `member`.*, (SELECT COUNT(*) AS `count` FROM `rx_documents` AS `documents` WHERE `member`.`member_srl` = `documents`.`member_srl`) AS `document_count` ' . $this->assertEquals('SELECT `member`.*, (SELECT `documents`.`document_srl`, `documents`.*, COUNT(*) AS `count` FROM `rx_documents` AS `documents` ' .
'WHERE `member`.`member_srl` = `documents`.`member_srl`) AS `document_count` ' .
'FROM `rx_member` AS `member`', $sql); 'FROM `rx_member` AS `member`', $sql);
$this->assertEquals([], $params); $this->assertEquals([], $params);
// Test count-only query (#1575)
$sql = $query->getQueryString('rx_', [], [], 1);
$params = $query->getQueryParams();
$this->assertEquals('SELECT COUNT(*) AS `count` FROM (SELECT 1, (SELECT `documents`.`document_srl`, 1, COUNT(*) AS `count` FROM `rx_documents` AS `documents` ' .
'WHERE `member`.`member_srl` = `documents`.`member_srl`) AS `document_count` ' .
'FROM `rx_member` AS `member`) AS `subquery`', $sql);
$this->assertEquals([], $params);
} }
public function testSubquery3() public function testSubquery3()