Skip to content

Commit

Permalink
Translate HAVING without GROUP BY to "GROUP BY 1 HAVING"
Browse files Browse the repository at this point in the history
The previous method of translating ungrouped HAVING to "AND" had some problems:

1. If no WHERE was in the query, the query would fail with: `near "AND": syntax error`
2. When an aggregate function was used in having (e.g, COUNT(*) > 1), the query
    would fail with: `misuse of aggregate function COUNT()`

This commit fixes both of these issues.
  • Loading branch information
JanJakes committed Aug 12, 2024
1 parent e910d7f commit 3bc55c5
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 3 deletions.
50 changes: 50 additions & 0 deletions tests/WP_SQLite_Translator_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -3077,6 +3077,56 @@ public function testGroupByHaving() {
);
}

public function testHavingWithoutGroupBy() {
$this->assertQuery(
'CREATE TABLE _tmp_table (
name varchar(20)
);'
);

$this->assertQuery(
"INSERT INTO _tmp_table VALUES ('a'), ('b'), ('b'), ('c'), ('c'), ('c')"
);

// HAVING condition satisfied
$result = $this->assertQuery(
"SELECT 'T' FROM _tmp_table HAVING COUNT(*) > 1"
);
$this->assertEquals(
array(
(object) array(
':param0' => 'T',
),
),
$result
);

// HAVING condition not satisfied
$result = $this->assertQuery(
"SELECT 'T' FROM _tmp_table HAVING COUNT(*) > 100"
);
$this->assertEquals(
array(),
$result
);

// DISTINCT ... HAVING, where only some results meet the HAVING condition
$result = $this->assertQuery(
'SELECT DISTINCT name FROM _tmp_table HAVING COUNT(*) > 1'
);
$this->assertEquals(
array(
(object) array(
'name' => 'b',
),
(object) array(
'name' => 'c',
),
),
$result
);
}

/**
* @dataProvider mysqlVariablesToTest
*/
Expand Down
13 changes: 10 additions & 3 deletions wp-includes/sqlite/class-wp-sqlite-translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2619,7 +2619,7 @@ private function capture_group_by( $token ) {
}

/**
* Translate WHERE something HAVING something to WHERE something AND something.
* Translate HAVING without GROUP BY to GROUP BY 1 HAVING.
*
* @param WP_SQLite_Token $token The token to translate.
*
Expand All @@ -2638,8 +2638,15 @@ private function translate_ungrouped_having( $token ) {
if ( $this->has_group_by ) {
return false;
}
$this->rewriter->skip();
$this->rewriter->add( new WP_SQLite_Token( 'AND', WP_SQLite_Token::TYPE_KEYWORD ) );

// GROUP BY is missing, add "GROUP BY 1" before the HAVING clause.
$having = $this->rewriter->skip();
$this->rewriter->add( new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_DELIMITER ) );
$this->rewriter->add( new WP_SQLite_Token( 'GROUP BY', WP_SQLite_Token::TYPE_KEYWORD ) );
$this->rewriter->add( new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_DELIMITER ) );
$this->rewriter->add( new WP_SQLite_Token( '1', WP_SQLite_Token::TYPE_NUMBER ) );
$this->rewriter->add( new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_DELIMITER ) );
$this->rewriter->add( $having );

return true;
}
Expand Down

0 comments on commit 3bc55c5

Please sign in to comment.