From 3bc55c5642254be5b3e679719fadb6b75689ddff Mon Sep 17 00:00:00 2001 From: Jan Jakes Date: Mon, 12 Aug 2024 07:14:22 +0200 Subject: [PATCH] Translate HAVING without GROUP BY to "GROUP BY 1 HAVING" 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. --- tests/WP_SQLite_Translator_Tests.php | 50 +++++++++++++++++++ .../sqlite/class-wp-sqlite-translator.php | 13 +++-- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/tests/WP_SQLite_Translator_Tests.php b/tests/WP_SQLite_Translator_Tests.php index 188be93..33e6ed8 100644 --- a/tests/WP_SQLite_Translator_Tests.php +++ b/tests/WP_SQLite_Translator_Tests.php @@ -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 */ diff --git a/wp-includes/sqlite/class-wp-sqlite-translator.php b/wp-includes/sqlite/class-wp-sqlite-translator.php index 8329513..95c6f70 100644 --- a/wp-includes/sqlite/class-wp-sqlite-translator.php +++ b/wp-includes/sqlite/class-wp-sqlite-translator.php @@ -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. * @@ -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; }