diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java index 476741c487e2..e28ca50ff232 100644 --- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java +++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java @@ -21,95 +21,158 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; +import java.util.Locale; +import java.util.stream.Stream; import org.apache.ignite.internal.sql.engine.util.QueryChecker; +import org.apache.ignite.internal.testframework.WithSystemProperty; import org.apache.ignite.lang.IgniteException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * Group of tests to verify aggregation functions. */ public class ItAggregatesTest extends AbstractBasicIntegrationTest { + private static String[] disabledRules = {"MapReduceHashAggregateConverterRule", "MapReduceSortAggregateConverterRule", + "ColocatedHashAggregateConverterRule", "ColocatedSortAggregateConverterRule"}; + + private static final int ROWS = 103; + /** * Before all. */ @BeforeAll - static void initTestData() { + static void initTestData() throws InterruptedException { createAndPopulateTable(); + + sql("CREATE TABLE test (id INT PRIMARY KEY, grp0 INT, grp1 INT, val0 INT, val1 INT) WITH replicas=2,partitions=10"); + sql("CREATE TABLE test_one_col_idx (pk INT PRIMARY KEY, col0 INT)"); + + sql("CREATE INDEX test_idx ON test(grp0, grp1)"); + sql("CREATE INDEX test_one_col_idx_idx ON test_one_col_idx(col0)"); + + // FIXME: https://issues.apache.org/jira/browse/IGNITE-18203 + waitForIndex("test_idx"); + waitForIndex("test_one_col_idx_idx"); + + for (int i = 0; i < ROWS; i++) { + sql("INSERT INTO test (id, grp0, grp1, val0, val1) VALUES (?, ?, ?, ?, ?)", i, i / 10, i / 100, 1, 2); + sql("INSERT INTO test_one_col_idx (pk, col0) VALUES (?, ?)", i, i); + } } - @Test - public void countOfNonNumericField() { - assertQuery("select count(name) from person").returns(4L).check(); - assertQuery("select count(*) from person").returns(5L).check(); - assertQuery("select count(1) from person").returns(5L).check(); - assertQuery("select count(null) from person").returns(0L).check(); + @ParameterizedTest + @MethodSource("provideRules") + public void aggregateWithSumAndHaving(String[] rules) { + var res = sql( + appendDisabledRules("SELECT SUM(val0), SUM(val1), grp0 FROM TEST GROUP BY grp0 HAVING SUM(val1) > 10", rules)); + + assertEquals(ROWS / 10, res.size()); - assertQuery("select count(*) from person where salary < 0").returns(0L).check(); - assertQuery("select count(*) from person where salary < 0 and salary > 0").returns(0L).check(); + res.forEach(r -> { + long s0 = (Long) r.get(0); + long s1 = (Long) r.get(1); - assertQuery("select count(case when name like 'R%' then 1 else null end) from person").returns(2L).check(); - assertQuery("select count(case when name not like 'I%' then 1 else null end) from person").returns(2L).check(); + assertEquals(s0 * 2, s1); + }); + } + + @ParameterizedTest + @MethodSource("provideRules") + public void correctCollationsOnAgg(String[] rules) { + var cursors = sql( + appendDisabledRules("SELECT PK FROM TEST_ONE_COL_IDX WHERE col0 IN (SELECT col0 FROM TEST_ONE_COL_IDX)", rules)); - assertQuery("select count(name) from person where salary > 10").returns(1L).check(); - assertQuery("select count(*) from person where salary > 10").returns(2L).check(); - assertQuery("select count(1) from person where salary > 10").returns(2L).check(); - assertQuery("select count(*) from person where name is not null").returns(4L).check(); + assertEquals(ROWS, cursors.size()); + } - assertQuery("select count(name) filter (where salary > 10) from person").returns(1L).check(); - assertQuery("select count(*) filter (where salary > 10) from person").returns(2L).check(); - assertQuery("select count(1) filter (where salary > 10) from person").returns(2L).check(); + @ParameterizedTest + @MethodSource("provideRules") + public void countOfNonNumericField(String[] rules) { + assertQuery("select count(name) from person").disableRules(rules).returns(4L).check(); + assertQuery("select count(*) from person").disableRules(rules).returns(5L).check(); + assertQuery("select count(1) from person").disableRules(rules).returns(5L).check(); + assertQuery("select count(null) from person").disableRules(rules).returns(0L).check(); + + assertQuery("select count(*) from person where salary < 0").disableRules(rules).returns(0L).check(); + assertQuery("select count(*) from person where salary < 0 and salary > 0").disableRules(rules).returns(0L).check(); + + assertQuery("select count(case when name like 'R%' then 1 else null end) from person").disableRules(rules).returns(2L).check(); + assertQuery("select count(case when name not like 'I%' then 1 else null end) from person").disableRules(rules).returns(2L).check(); + + assertQuery("select count(name) from person where salary > 10").disableRules(rules).returns(1L).check(); + assertQuery("select count(*) from person where salary > 10").disableRules(rules).returns(2L).check(); + assertQuery("select count(1) from person where salary > 10").disableRules(rules).returns(2L).check(); + assertQuery("select count(*) from person where name is not null").disableRules(rules).returns(4L).check(); + + assertQuery("select count(name) filter (where salary > 10) from person").disableRules(rules).returns(1L).check(); + assertQuery("select count(*) filter (where salary > 10) from person").disableRules(rules).returns(2L).check(); + assertQuery("select count(1) filter (where salary > 10) from person").disableRules(rules).returns(2L).check(); assertQuery("select salary, count(name) from person group by salary order by salary") + .disableRules(rules) .returns(10d, 3L) .returns(15d, 1L) .check(); // same query, but grouping by alias assertQuery("select salary as sal, count(name) from person group by sal order by sal") + .disableRules(rules) .returns(10d, 3L) .returns(15d, 1L) .check(); // same query, but grouping by ordinal assertQuery("select salary, count(name) from person group by 1 order by 1") + .disableRules(rules) .returns(10d, 3L) .returns(15d, 1L) .check(); assertQuery("select salary * salary / 5, count(name) from person group by (salary * salary / 5) order by (salary * salary / 5)") + .disableRules(rules) .returns(20d, 3L) .returns(45d, 1L) .check(); // same query, but grouping by alias assertQuery("select (salary * salary / 5) as sal, count(name) from person group by sal order by sal") + .disableRules(rules) .returns(20d, 3L) .returns(45d, 1L) .check(); // same query, but grouping by ordinal assertQuery("select salary * salary / 5, count(name) from person group by 1 order by 1") + .disableRules(rules) .returns(20d, 3L) .returns(45d, 1L) .check(); assertQuery("select salary, count(*) from person group by salary order by salary") + .disableRules(rules) .returns(10d, 3L) .returns(15d, 2L) .check(); assertQuery("select salary, count(1) from person group by salary order by salary") + .disableRules(rules) .returns(10d, 3L) .returns(15d, 2L) .check(); assertQuery("select salary, count(1), sum(1) from person group by salary order by salary") + .disableRules(rules) .returns(10d, 3L, 3L) .returns(15d, 2L, 2L) .check(); assertQuery("select salary, name, count(1), sum(salary) from person group by salary, name order by salary") + .disableRules(rules) .returns(10d, "Igor", 1L, 10d) .returns(10d, "Roma", 2L, 20d) .returns(15d, "Ilya", 1L, 15d) @@ -117,9 +180,11 @@ public void countOfNonNumericField() { .check(); assertQuery("select salary, count(name) from person group by salary having salary < 10 order by salary") + .disableRules(rules) .check(); assertQuery("select count(name), name from person group by name") + .disableRules(rules) .returns(1L, "Igor") .returns(1L, "Ilya") .returns(2L, "Roma") @@ -127,43 +192,48 @@ public void countOfNonNumericField() { .check(); assertQuery("select avg(salary) from person") + .disableRules(rules) .returns(12.0) .check(); assertQuery("select name, salary from person where person.salary > (select avg(person.salary) from person)") + .disableRules(rules) .returns(null, 15d) .returns("Ilya", 15d) .check(); assertQuery("select avg(salary) from (select avg(salary) as salary from person union all select salary from person)") + .disableRules(rules) .returns(12d) .check(); } - @Test - public void testMultipleRowsFromSingleAggr() { + @ParameterizedTest + @MethodSource("provideRules") + public void testMultipleRowsFromSingleAggr(String[] rules) { assertThrows( IgniteException.class, - () -> assertQuery("SELECT (SELECT name FROM person)").check() + () -> assertQuery("SELECT (SELECT name FROM person)").disableRules(rules).check() ); assertThrows( IgniteException.class, - () -> assertQuery("SELECT t.id, (SELECT x FROM TABLE(system_range(1, 5))) FROM person t").check() + () -> assertQuery("SELECT t.id, (SELECT x FROM TABLE(system_range(1, 5))) FROM person t").disableRules(rules).check() ); assertThrows( IgniteException.class, () -> assertQuery("SELECT t.id, (SELECT x FROM " - + "TABLE(system_range(t.id, t.id + 1))) FROM person t").check() + + "TABLE(system_range(t.id, t.id + 1))) FROM person t").disableRules(rules).check() ); - assertQuery("SELECT t.id, (SELECT x FROM TABLE(system_range(t.id, t.id))) FROM person t").check(); + assertQuery("SELECT t.id, (SELECT x FROM TABLE(system_range(t.id, t.id))) FROM person t").disableRules(rules).check(); } - @Test - public void testAnyValAggr() { - var res = sql("select any_value(name) from person"); + @ParameterizedTest + @MethodSource("provideRules") + public void testAnyValAggr(String[] rules) { + var res = sql(appendDisabledRules("select any_value(name) from person", rules)); assertEquals(1, res.size()); @@ -172,7 +242,7 @@ public void testAnyValAggr() { assertTrue("Igor".equals(val) || "Roma".equals(val) || "Ilya".equals(val), "Unexpected value: " + val); // Test with grouping. - res = sql("select any_value(name), salary from person group by salary order by salary"); + res = sql(appendDisabledRules("select any_value(name), salary from person group by salary order by salary", rules)); assertEquals(2, res.size()); @@ -187,34 +257,77 @@ public void testAnyValAggr() { @Test public void testColocatedAggregate() { - sql("CREATE TABLE t1(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " - + "COLOCATE BY (val1)"); - - sql("CREATE TABLE t2(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " - + "COLOCATE BY (val1)"); - - for (int i = 0; i < 100; i++) { - sql("INSERT INTO t1 VALUES (?, ?, ?, ?)", i, "val" + i, "val" + i % 2, "val" + i); + try { + sql("CREATE TABLE t1(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " + + "COLOCATE BY (val1)"); + + sql("CREATE TABLE t2(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " + + "COLOCATE BY (val1)"); + + for (int i = 0; i < 100; i++) { + sql("INSERT INTO t1 VALUES (?, ?, ?, ?)", i, "val" + i, "val" + i % 2, "val" + i); + } + + sql("INSERT INTO t2 VALUES (0, 'val0', 'val0', 'val0'), (1, 'val1', 'val1', 'val1')"); + + String sql = "SELECT val1, count(val2) FROM t1 GROUP BY val1"; + + assertQuery(sql) + .matches(QueryChecker.matches(".*Exchange.*Colocated.*Aggregate.*")) + .returns("val0", 50L) + .returns("val1", 50L) + .check(); + + sql = "SELECT t2.val1, agg.cnt " + + "FROM t2 JOIN (SELECT val1, COUNT(val2) AS cnt FROM t1 GROUP BY val1) AS agg ON t2.val1 = agg.val1"; + + assertQuery(sql) + .matches(QueryChecker.matches(".*Exchange.*Join.*Colocated.*Aggregate.*")) + .returns("val0", 50L) + .returns("val1", 50L) + .check(); + } finally { + sql("DROP TABLE IF EXISTS t1"); + sql("DROP TABLE IF EXISTS t2"); } + } - sql("INSERT INTO t2 VALUES (0, 'val0', 'val0', 'val0'), (1, 'val1', 'val1', 'val1')"); - - String sql = "SELECT val1, count(val2) FROM t1 GROUP BY val1"; - - assertQuery(sql) - .matches(QueryChecker.matches(".*Exchange.*Colocated.*Aggregate.*")) - .returns("val0", 50L) - .returns("val1", 50L) - .check(); - - sql = "SELECT t2.val1, agg.cnt " - + "FROM t2 JOIN (SELECT val1, COUNT(val2) AS cnt FROM t1 GROUP BY val1) AS agg ON t2.val1 = agg.val1"; - - assertQuery(sql) - .matches(QueryChecker.matches(".*Exchange.*Join.*Colocated.*Aggregate.*")) - .returns("val0", 50L) - .returns("val1", 50L) - .check(); + @ParameterizedTest + @MethodSource("provideRules") + public void testColocatedAggregate(String[] rules) { + try { + sql("CREATE TABLE t1(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " + + "COLOCATE BY (val1)"); + + sql("CREATE TABLE t2(id INT, val0 VARCHAR, val1 VARCHAR, val2 VARCHAR, PRIMARY KEY(id, val1)) " + + "COLOCATE BY (val1)"); + + for (int i = 0; i < 100; i++) { + sql("INSERT INTO t1 VALUES (?, ?, ?, ?)", i, "val" + i, "val" + i % 2, "val" + i); + } + + sql("INSERT INTO t2 VALUES (0, 'val0', 'val0', 'val0'), (1, 'val1', 'val1', 'val1')"); + + String sql = "SELECT val1, count(val2) FROM t1 GROUP BY val1"; + + assertQuery(sql) + .disableRules(rules) + .returns("val0", 50L) + .returns("val1", 50L) + .check(); + + sql = "SELECT t2.val1, agg.cnt " + + "FROM t2 JOIN (SELECT val1, COUNT(val2) AS cnt FROM t1 GROUP BY val1) AS agg ON t2.val1 = agg.val1"; + + assertQuery(sql) + .disableRules(rules) + .returns("val0", 50L) + .returns("val1", 50L) + .check(); + } finally { + sql("DROP TABLE IF EXISTS t1"); + sql("DROP TABLE IF EXISTS t2"); + } } @Test @@ -245,4 +358,148 @@ public void distinctAggregateWithoutAggregateFunction() { .returns(null) .check(); } + + @ParameterizedTest + @MethodSource("provideRules") + @WithSystemProperty(key = "IMPLICIT_PK_ENABLED", value = "true") + public void testDifferentAgg(String[] rules) { + try { + sql("CREATE TABLE testMe (a INTEGER, b INTEGER, s VARCHAR);"); + sql("INSERT INTO testMe VALUES (11, 1, 'hello'), (12, 2, 'world'), (11, 3, NULL)"); + sql("INSERT INTO testMe VALUES (11, 3, 'hello'), (12, 2, 'world'), (10, 5, 'ahello'), (13, 6, 'world')"); + + assertQuery("SELECT DISTINCT(a) as a FROM testMe ORDER BY a") + .disableRules(rules) + .returns(10) + .returns(11) + .returns(12) + .returns(13) + .check(); + + assertQuery("SELECT COUNT(*) FROM testMe") + .disableRules(rules) + .returns(7L) + .check(); + + // Such kind of queries can`t be processed with + if (Arrays.stream(rules).anyMatch(r -> r.contains("MapReduceSortAggregateConverterRule"))) { + assertQuery("SELECT COUNT(a), COUNT(DISTINCT(b)) FROM testMe") + .disableRules(rules) + .returns(7L, 5L) + .check(); + } + + assertQuery("SELECT COUNT(a) as a, s FROM testMe GROUP BY s ORDER BY a, s") + .disableRules(rules) + .returns(1L, "ahello") + .returns(1L, null) + .returns(2L, "hello") + .returns(3L, "world") + .check(); + + assertQuery("SELECT COUNT(a) as a, AVG(a) as b, MIN(a), MIN(b), s FROM testMe GROUP BY s ORDER BY a, b") + .disableRules(rules) + .returns(1L, 10, 10, 5, "ahello") + .returns(1L, 11, 11, 3, null) + .returns(2L, 11, 11, 1, "hello") + .returns(3L, 12, 12, 2, "world") + .check(); + + assertQuery("SELECT COUNT(a) as a, AVG(a) as bb, MIN(a), MIN(b), s FROM testMe GROUP BY s, b ORDER BY a, s") + .disableRules(rules) + .returns(1L, 10, 10, 5, "ahello") + .returns(1L, 11, 11, 1, "hello") + .returns(1L, 11, 11, 3, "hello") + .returns(1L, 13, 13, 6, "world") + .returns(1L, 11, 11, 3, null) + .returns(2L, 12, 12, 2, "world") + .check(); + + assertQuery("SELECT COUNT(a) FROM testMe") + .disableRules(rules) + .returns(7L) + .check(); + + assertQuery("SELECT COUNT(DISTINCT(a)) FROM testMe") + .disableRules(rules) + .returns(4L) + .check(); + + assertQuery("SELECT COUNT(a), COUNT(s), COUNT(*) FROM testMe") + .disableRules(rules) + .returns(7L, 6L, 7L) + .check(); + + assertQuery("SELECT AVG(a) FROM testMe") + .disableRules(rules) + .returns(11) + .check(); + + assertQuery("SELECT MIN(a) FROM testMe") + .disableRules(rules) + .returns(10) + .check(); + + assertQuery("SELECT COUNT(a), COUNT(DISTINCT(a)) FROM testMe") + .disableRules(rules) + .returns(7L, 4L) + .check(); + + assertQuery("SELECT COUNT(a), COUNT(DISTINCT a), SUM(a), SUM(DISTINCT a) FROM testMe") + .disableRules(rules) + .returns(7L, 4L, 80L, 46L) + .check(); + } finally { + sql("DROP TABLE IF EXISTS testMe"); + } + } + + @ParameterizedTest + @MethodSource("provideRules") + @WithSystemProperty(key = "IMPLICIT_PK_ENABLED", value = "true") + public void checkEmptyTable(String[] rules) { + sql("CREATE TABLE t (a INTEGER, b INTEGER)"); + + try { + assertQuery("SELECT min(b) FROM t GROUP BY a") + .disableRules(rules) + .returnNothing().check(); + } finally { + sql("DROP TABLE t"); + } + } + + static String[][] makePermutations(String[] rules) { + String[][] out = new String[rules.length][rules.length - 1]; + + for (int i = 0; i < disabledRules.length; ++i) { + int pos = 0; + for (int ruleIdx = 0; ruleIdx < disabledRules.length; ++ruleIdx) { + if (ruleIdx == i) { + continue; + } + out[i][pos++] = disabledRules[ruleIdx]; + } + } + + return out; + } + + private static Stream provideRules() { + return Arrays.stream(makePermutations(disabledRules)).map(k -> Arguments.of((Object) k)); + } + + private String appendDisabledRules(String sql, String[] rules) { + sql = sql.toLowerCase(Locale.ENGLISH); + int pos = sql.indexOf("select"); + + assert pos >= 0; + + String newSql = sql.substring(0, pos + "select".length() + 1); + newSql += " /*+ DISABLE_RULE( '"; + newSql += String.join("' ,'", rules); + newSql += "') */ "; + newSql += sql.substring(pos + "select".length() + 1); + return newSql; + } } diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java deleted file mode 100644 index eaafb69e1dbc..000000000000 --- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ignite.internal.sql.engine; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.List; -import java.util.Locale; -import org.apache.ignite.internal.testframework.WithSystemProperty; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -/** - * Sort aggregate integration test. - */ -public class ItSortAggregateTest extends AbstractBasicIntegrationTest { - private static final int ROWS = 103; - - /** - * Before all. - */ - @BeforeAll - static void initTestData() throws InterruptedException { - sql("CREATE TABLE test (id INT PRIMARY KEY, grp0 INT, grp1 INT, val0 INT, val1 INT) WITH replicas=2,partitions=10"); - sql("CREATE TABLE test_one_col_idx (pk INT PRIMARY KEY, col0 INT)"); - - sql("CREATE INDEX test_idx ON test(grp0, grp1)"); - sql("CREATE INDEX test_one_col_idx_idx ON test_one_col_idx(col0)"); - - // FIXME: https://issues.apache.org/jira/browse/IGNITE-18203 - waitForIndex("test_idx"); - waitForIndex("test_one_col_idx_idx"); - - for (int i = 0; i < ROWS; i++) { - sql("INSERT INTO test (id, grp0, grp1, val0, val1) VALUES (?, ?, ?, ?, ?)", i, i / 10, i / 100, 1, 2); - sql("INSERT INTO test_one_col_idx (pk, col0) VALUES (?, ?)", i, i); - } - } - - @Test - public void mapReduceAggregate() { - String disabledRules = " /*+ DISABLE_RULE('MapReduceHashAggregateConverterRule', 'ColocatedHashAggregateConverterRule', " - + "'ColocatedSortAggregateConverterRule') */ "; - - var res = sql( - appendDisabledRules("SELECT SUM(val0), SUM(val1), grp0 FROM TEST GROUP BY grp0 HAVING SUM(val1) > 10", disabledRules)); - - assertEquals(ROWS / 10, res.size()); - - res.forEach(r -> { - long s0 = (Long) r.get(0); - long s1 = (Long) r.get(1); - - assertEquals(s0 * 2, s1); - }); - } - - @Test - public void correctCollationsOnMapReduceSortAgg() { - String disabledRules = " /*+ DISABLE_RULE('MapReduceHashAggregateConverterRule', 'ColocatedHashAggregateConverterRule', " - + "'ColocatedSortAggregateConverterRule') */ "; - - var cursors = sql( - appendDisabledRules("SELECT PK FROM TEST_ONE_COL_IDX WHERE col0 IN (SELECT col0 FROM TEST_ONE_COL_IDX)", disabledRules)); - - assertEquals(ROWS, cursors.size()); - } - - @Test - @WithSystemProperty(key = "IMPLICIT_PK_ENABLED", value = "true") - public void testDifferentCollocatedSortAgg() { - try { - sql("CREATE TABLE testMe (a INTEGER, b INTEGER, s VARCHAR);"); - sql("INSERT INTO testMe VALUES (11, 1, 'hello'), (12, 2, 'world'), (11, 3, NULL)"); - sql("INSERT INTO testMe VALUES (11, 3, 'hello'), (12, 2, 'world'), (10, 5, 'ahello'), (13, 6, 'world')"); - - String[] disabledRules = {"MapReduceHashAggregateConverterRule", "MapReduceSortAggregateConverterRule", - "ColocatedHashAggregateConverterRule"}; - - assertQuery("SELECT DISTINCT(a) as a FROM testMe ORDER BY a") - .disableRules(disabledRules) - .returns(10) - .returns(11) - .returns(12) - .returns(13) - .check(); - - assertQuery("SELECT COUNT(*) FROM testMe") - .disableRules(disabledRules) - .returns(7L) - .check(); - - assertQuery("SELECT COUNT(a), COUNT(DISTINCT(b)) FROM testMe") - .disableRules(disabledRules) - .returns(7L, 5L) - .check(); - - assertQuery("SELECT COUNT(a) as a, s FROM testMe GROUP BY s ORDER BY a, s") - .disableRules(disabledRules) - .returns(1L, "ahello") - .returns(1L, null) - .returns(2L, "hello") - .returns(3L, "world") - .check(); - - assertQuery("SELECT COUNT(a) as a, AVG(a) as b, MIN(a), MIN(b), s FROM testMe GROUP BY s ORDER BY a, b") - .disableRules(disabledRules) - .returns(1L, 10, 10, 5, "ahello") - .returns(1L, 11, 11, 3, null) - .returns(2L, 11, 11, 1, "hello") - .returns(3L, 12, 12, 2, "world") - .check(); - - assertQuery("SELECT COUNT(a) as a, AVG(a) as bb, MIN(a), MIN(b), s FROM testMe GROUP BY s, b ORDER BY a, s") - .disableRules(disabledRules) - .returns(1L, 10, 10, 5, "ahello") - .returns(1L, 11, 11, 1, "hello") - .returns(1L, 11, 11, 3, "hello") - .returns(1L, 13, 13, 6, "world") - .returns(1L, 11, 11, 3, null) - .returns(2L, 12, 12, 2, "world") - .check(); - - assertQuery("SELECT COUNT(a) FROM testMe") - .disableRules(disabledRules) - .returns(7L) - .check(); - - assertQuery("SELECT COUNT(DISTINCT(a)) FROM testMe") - .disableRules(disabledRules) - .returns(4L) - .check(); - - assertQuery("SELECT COUNT(a), COUNT(s), COUNT(*) FROM testMe") - .disableRules(disabledRules) - .returns(7L, 6L, 7L) - .check(); - - assertQuery("SELECT AVG(a) FROM testMe") - .disableRules(disabledRules) - .returns(11) - .check(); - - assertQuery("SELECT MIN(a) FROM testMe") - .disableRules(disabledRules) - .returns(10) - .check(); - - assertQuery("SELECT COUNT(a), COUNT(DISTINCT(a)) FROM testMe") - .disableRules(disabledRules) - .returns(7L, 4L) - .check(); - } finally { - sql("DROP TABLE testMe"); - } - } - - @WithSystemProperty(key = "IMPLICIT_PK_ENABLED", value = "true") - @Test - public void checkEmptyTable() { - sql("CREATE TABLE t (a INTEGER, b INTEGER)"); - - // Check ColocatedSortAggregate - String[] disabledRules1 = {"MapReduceHashAggregateConverterRule", "MapReduceSortAggregateConverterRule", - "ColocatedHashAggregateConverterRule"}; - - // Check MapReduceSortAggregate - String[] disabledRules2 = {"MapReduceHashAggregateConverterRule", "ColocatedSortAggregateConverterRule", - "ColocatedHashAggregateConverterRule"}; - - try { - for (String[] disabledRules : List.of(disabledRules1, disabledRules2)) { - assertQuery("SELECT min(b) FROM t GROUP BY a") - .disableRules(disabledRules) - .returnNothing().check(); - } - } finally { - sql("DROP TABLE t"); - } - } - - private String appendDisabledRules(String sql, String rules) { - sql = sql.toLowerCase(Locale.ENGLISH); - int pos = sql.indexOf("select"); - - assert pos >= 0; - - String newSql = sql.substring(0, pos + "select".length() + 1); - newSql += rules; - newSql += sql.substring(pos + "select".length() + 1); - return newSql; - } -} diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java index e60e6b8fc736..5cb79e1cce0b 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java @@ -712,13 +712,19 @@ public Node visit(IgniteMapSortAggregate rel) { RowFactory rowFactory = ctx.rowHandler().factory(ctx.getTypeFactory(), rowType); + Comparator comp = expressionFactory.comparator(rel.collation()); + + if (rel.getGroupSet().isEmpty() && comp == null) { + comp = (k1, k2) -> 0; + } + SortAggregateNode node = new SortAggregateNode<>( ctx, type, rel.getGroupSet(), accFactory, rowFactory, - expressionFactory.comparator(rel.collation()) + comp ); Node input = visit(rel.getInput()); @@ -743,13 +749,19 @@ public Node visit(IgniteReduceSortAggregate rel) { RowFactory rowFactory = ctx.rowHandler().factory(ctx.getTypeFactory(), rowType); + Comparator comp = expressionFactory.comparator(rel.collation()); + + if (rel.getGroupSet().isEmpty() && comp == null) { + comp = (k1, k2) -> 0; + } + SortAggregateNode node = new SortAggregateNode<>( ctx, type, rel.getGroupSet(), accFactory, rowFactory, - expressionFactory.comparator(rel.collation()) + comp ); Node input = visit(rel.getInput()); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java index 6b6de6f43eff..4b88ae6c3d97 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java @@ -66,7 +66,13 @@ public class SortAggregateNode extends AbstractNode implements Singl /** * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * + * @param ctx Execution context. + * @param type Aggregation operation (phase) type. + * @param grpSet Bit set of grouping fields. + * @param accFactory Accumulators. + * @param rowFactory Row factory. + * @param comp Comparator. */ public SortAggregateNode( ExecutionContext ctx, diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/ColocationGroup.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/ColocationGroup.java index 21f8f08bfb8b..4b153cffabd0 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/ColocationGroup.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/ColocationGroup.java @@ -198,7 +198,7 @@ public ColocationGroup colocate(ColocationGroup other) throws ColocationMappingE * Constructor. * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 */ - public ColocationGroup finalaze() { + public ColocationGroup complete() { if (assignments == null && nodeNames == null) { return this; } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/FragmentMapping.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/FragmentMapping.java index 6951bb0fe739..c564c67fd977 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/FragmentMapping.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/FragmentMapping.java @@ -156,7 +156,7 @@ public FragmentMapping finalize(Supplier> nodesSource) { List colocationGroups = this.colocationGroups; - colocationGroups = Commons.transform(colocationGroups, ColocationGroup::finalaze); + colocationGroups = Commons.transform(colocationGroups, ColocationGroup::complete); List nodes = nodeNames(); List nodes0 = nodes.isEmpty() ? nodesSource.get() : nodes; diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteHashIndexSpool.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteHashIndexSpool.java index 945194154c03..fc26b0b00567 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteHashIndexSpool.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteHashIndexSpool.java @@ -38,7 +38,7 @@ /** * Relational operator that returns the hashed contents of a table and allow to lookup rows by specified keys. */ -public class IgniteHashIndexSpool extends AbstractIgniteSpool implements InternalIgniteRel { +public class IgniteHashIndexSpool extends AbstractIgniteSpool { /** Search row. */ private final List searchRow; diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedAggregateBase.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedAggregateBase.java index 4835796e6655..78a50351e66a 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedAggregateBase.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedAggregateBase.java @@ -40,7 +40,7 @@ */ public abstract class IgniteColocatedAggregateBase extends IgniteAggregate implements TraitsAwareIgniteRel { /** {@inheritDoc} */ - protected IgniteColocatedAggregateBase( + IgniteColocatedAggregateBase( RelOptCluster cluster, RelTraitSet traitSet, RelNode input, @@ -51,8 +51,12 @@ protected IgniteColocatedAggregateBase( super(cluster, traitSet, input, groupSet, groupSets, aggCalls); } - /** {@inheritDoc} */ - protected IgniteColocatedAggregateBase(RelInput input) { + /** + * Constructor used for deserialization. + * + * @param input Serialized representation. + */ + IgniteColocatedAggregateBase(RelInput input) { super(TraitUtils.changeTraits(input, IgniteConvention.INSTANCE)); } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedHashAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedHashAggregate.java index b6386893b8a9..6ac3795c90e9 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedHashAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedHashAggregate.java @@ -41,6 +41,11 @@ public IgniteColocatedHashAggregate(RelOptCluster cluster, RelTraitSet traitSet, super(cluster, traitSet, input, groupSet, groupSets, aggCalls); } + /** + * Constructor used for deserialization. + * + * @param input Serialized representation. + */ public IgniteColocatedHashAggregate(RelInput input) { super(input); } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedSortAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedSortAggregate.java index 6c71a11d11c2..a39e382dc675 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedSortAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteColocatedSortAggregate.java @@ -61,8 +61,9 @@ public IgniteColocatedSortAggregate( } /** - * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * Constructor used for deserialization. + * + * @param input Serialized representation. */ public IgniteColocatedSortAggregate(RelInput input) { super(input); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapAggregateBase.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapAggregateBase.java index fa8526dd4c42..2fbd3d836ddc 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapAggregateBase.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapAggregateBase.java @@ -53,17 +53,6 @@ protected IgniteMapAggregateBase(RelInput input) { super(TraitUtils.changeTraits(input, IgniteConvention.INSTANCE)); } - /** {@inheritDoc} */ - @Override - public Pair> passThroughDistribution(RelTraitSet nodeTraits, - List inTraits) { - if (TraitUtils.distribution(nodeTraits).satisfies(IgniteDistributions.single())) { - return null; - } else { - return TraitsAwareIgniteRel.super.passThroughDistribution(nodeTraits, inTraits); - } - } - /** {@inheritDoc} */ @Override public List>> deriveRewindability( diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapHashAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapHashAggregate.java index bc31dd4ef01a..51d9314c8db7 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapHashAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapHashAggregate.java @@ -58,8 +58,9 @@ public IgniteMapHashAggregate( } /** - * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * Constructor used for deserialization. + * + * @param input Serialized representation. */ public IgniteMapHashAggregate(RelInput input) { super(input); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapSortAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapSortAggregate.java index cf3ab3ac5748..b6c4ad4fd497 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapSortAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteMapSortAggregate.java @@ -64,14 +64,14 @@ public IgniteMapSortAggregate( super(cluster, traitSet, input, groupSet, groupSets, aggCalls); assert Objects.nonNull(collation); - assert !collation.isDefault(); this.collation = collation; } /** - * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * Constructor used for deserialization. + * + * @param input Serialized representation. */ public IgniteMapSortAggregate(RelInput input) { super(TraitUtils.changeTraits(input, IgniteConvention.INSTANCE)); @@ -79,7 +79,6 @@ public IgniteMapSortAggregate(RelInput input) { collation = input.getCollation(); assert Objects.nonNull(collation); - assert !collation.isDefault(); } /** {@inheritDoc} */ diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceAggregateBase.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceAggregateBase.java index 334630eb9604..f806b6eadb85 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceAggregateBase.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceAggregateBase.java @@ -77,8 +77,9 @@ protected IgniteReduceAggregateBase( } /** - * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * Constructor used for deserialization. + * + * @param input Serialized representation. */ protected IgniteReduceAggregateBase(RelInput input) { this( diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceHashAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceHashAggregate.java index 712e81042bb8..6ce52bf1eb5f 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceHashAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceHashAggregate.java @@ -62,8 +62,9 @@ public IgniteReduceHashAggregate( } /** - * Constructor. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * Constructor used for deserialization. + * + * @param input Serialized representation. */ public IgniteReduceHashAggregate(RelInput input) { super(input); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceSortAggregate.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceSortAggregate.java index da3df59fafef..cf60c766c1f4 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceSortAggregate.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteReduceSortAggregate.java @@ -62,7 +62,6 @@ public IgniteReduceSortAggregate( super(cluster, traits, input, groupSet, groupSets, aggCalls, rowType); assert Objects.nonNull(collation); - assert !collation.isDefault(); this.collation = collation; } @@ -77,7 +76,6 @@ public IgniteReduceSortAggregate(RelInput input) { collation = input.getCollation(); assert Objects.nonNull(collation); - assert !collation.isDefault(); } /** {@inheritDoc} */ diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java index ce88baaa0ee5..aa1ec3e062a1 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java @@ -96,10 +96,8 @@ private static class MapReduceSortAggregateConverterRule extends AbstractIgniteC /** {@inheritDoc} */ @Override - protected PhysicalNode convert(RelOptPlanner planner, RelMetadataQuery mq, - LogicalAggregate agg) { - // Applicable only for GROUP BY or SELECT DISTINCT - if (nullOrEmpty(agg.getGroupSet()) || agg.getGroupSets().size() > 1) { + protected @Nullable PhysicalNode convert(RelOptPlanner planner, RelMetadataQuery mq, LogicalAggregate agg) { + if ((nullOrEmpty(agg.getGroupSet()) && agg.getGroupSets().isEmpty()) || agg.getGroupSets().size() > 1) { return null; } diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java index 885246fb9f35..0c882f1c4d52 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java @@ -189,7 +189,7 @@ public void mapReduceGroupBy(AggregateAlgorithm algo) throws Exception { } /** - * Test that aggregate has single distribution output even if parent node accept random distibution inputs. + * Test that aggregate has single distribution output even if parent node accept random distribution inputs. * * @throws Exception If failed. */ diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java index bf4b8be04495..51b86e455c71 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java @@ -63,7 +63,8 @@ public void subqueryWithAggregate() throws Exception { IgniteDistributions.affinity(0, UUID.randomUUID(), DEFAULT_ZONE_ID) ); - String sql = "SELECT * FROM emps WHERE emps.salary = (SELECT AVG(emps.salary) FROM emps)"; + String sql = "SELECT /*+ DISABLE_RULE('MapReduceSortAggregateConverterRule', 'ColocatedHashAggregateConverterRule', " + + "'ColocatedSortAggregateConverterRule') */ * FROM emps WHERE emps.salary = (SELECT AVG(emps.salary) FROM emps)"; IgniteRel phys = physicalPlan( sql, @@ -101,7 +102,8 @@ public void noGroupByAggregate() throws Exception { publicSchema.addTable(tbl); - String sqlCount = "SELECT COUNT(*) FROM test"; + String sqlCount = "SELECT /*+ DISABLE_RULE('MapReduceSortAggregateConverterRule', 'ColocatedHashAggregateConverterRule', " + + "'ColocatedSortAggregateConverterRule') */ COUNT(*) FROM test"; IgniteRel phys = physicalPlan( sqlCount,