Vladsz83 commented on code in PR #12042: URL: https://github.com/apache/ignite/pull/12042#discussion_r2064694975
########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; + + for (int size : sizes) { + log.info("Check: size=" + size + ", colCnt=" + colCnt + ", withOrdinality=" + withOrdinality); + + Function<Integer, Object>[] funcs = new Function[colCnt]; + + for (int i = 0; i < colCnt; i++) { + int mul = 2 << i; + funcs[i] = r -> F.asList(mul * r, mul * r + 1); + } + + RootRewindable<Object[]> root = createNodes(colCnt, withOrdinality, new TestTable(size, colCnt, funcs)); + + long[] colsSum = new long[withOrdinality ? colCnt + 1 : colCnt]; + int cnt = 0; + + while (root.hasNext()) { + cnt++; + Object[] row = root.next(); + + for (int i = 0; i < row.length; i++) + colsSum[i] += (int)row[i]; + } + + assertEquals(size * (1 << colCnt), cnt); + + for (int i = 0; i < colCnt; i++) { + long expSum = (size * (size - 1L) * (2L << i) + size) * (1L << (colCnt - 1)); + assertEquals("Unexpected sum: size=" + size + ", colCnt=" + colCnt + Review Comment: Quite long. Lets put on the next line ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; + + for (int size : sizes) { + log.info("Check: size=" + size + ", colCnt=" + colCnt + ", withOrdinality=" + withOrdinality); + + Function<Integer, Object>[] funcs = new Function[colCnt]; + + for (int i = 0; i < colCnt; i++) { + int mul = 2 << i; + funcs[i] = r -> F.asList(mul * r, mul * r + 1); + } + + RootRewindable<Object[]> root = createNodes(colCnt, withOrdinality, new TestTable(size, colCnt, funcs)); + + long[] colsSum = new long[withOrdinality ? colCnt + 1 : colCnt]; + int cnt = 0; + + while (root.hasNext()) { + cnt++; + Object[] row = root.next(); + + for (int i = 0; i < row.length; i++) + colsSum[i] += (int)row[i]; + } + + assertEquals(size * (1 << colCnt), cnt); + + for (int i = 0; i < colCnt; i++) { + long expSum = (size * (size - 1L) * (2L << i) + size) * (1L << (colCnt - 1)); Review Comment: Can we explain this? Several variables? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; + + for (int size : sizes) { + log.info("Check: size=" + size + ", colCnt=" + colCnt + ", withOrdinality=" + withOrdinality); + + Function<Integer, Object>[] funcs = new Function[colCnt]; + + for (int i = 0; i < colCnt; i++) { + int mul = 2 << i; + funcs[i] = r -> F.asList(mul * r, mul * r + 1); + } + + RootRewindable<Object[]> root = createNodes(colCnt, withOrdinality, new TestTable(size, colCnt, funcs)); + + long[] colsSum = new long[withOrdinality ? colCnt + 1 : colCnt]; + int cnt = 0; + + while (root.hasNext()) { + cnt++; + Object[] row = root.next(); + + for (int i = 0; i < row.length; i++) + colsSum[i] += (int)row[i]; + } + + assertEquals(size * (1 << colCnt), cnt); + + for (int i = 0; i < colCnt; i++) { + long expSum = (size * (size - 1L) * (2L << i) + size) * (1L << (colCnt - 1)); + assertEquals("Unexpected sum: size=" + size + ", colCnt=" + colCnt + + ", withOrdinality=" + withOrdinality + ", i=" + i, expSum, colsSum[i]); + } + + long ordSum[] = {1, 3, 10, 36}; // sum(1 .. (1 << colCnt)) Review Comment: Can be placed out of the cycles. ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; + + for (int size : sizes) { + log.info("Check: size=" + size + ", colCnt=" + colCnt + ", withOrdinality=" + withOrdinality); + + Function<Integer, Object>[] funcs = new Function[colCnt]; + + for (int i = 0; i < colCnt; i++) { + int mul = 2 << i; Review Comment: Why exactly 2? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { Review Comment: Can we clarify which sizes. ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UnnestIntegrationTest.java: ########## @@ -0,0 +1,179 @@ +/* + * 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.processors.query.calcite.integration; + +import java.util.Collections; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Integration test for UNNEST operator. + */ +public class UnnestIntegrationTest extends AbstractBasicIntegrationTest { + /** */ + @Test + public void testUnnestSingleCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3])").returns(1).returns(2).returns(3).check(); + assertQuery("SELECT * FROM UNNEST(MAP['a', 1, 'b', 2])").returns("a", 1).returns("b", 2).check(); + assertQuery("SELECT * FROM UNNEST(ARRAY[ROW(1, 2), ROW(3, 4)])").returns(1, 2).returns(3, 4).check(); + + // Dynamic parameters. + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asList(1, 2)).returns(1).returns(2).check(); + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asMap("a", 1, "b", 2)) + .returns("a", 1).returns("b", 2).check(); + // Can't check dynamic parameters with ROW, since generic type of List can't be obtained in runtime. + // SQL type of parameter F.asList(new Object[] {1, 2}) will be derived as array of scalars + // (instead of array of rows) and UNNEST can only produce rows based on type derived on planner phase. + + // Subquery. + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(a) FROM (VALUES (1), (2)) t(a))") + .returns(1).returns(2).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(ROW(t.a, t.b)) FROM (VALUES (1, 2), (3, 4)) t(a, b))") + .returns(1, 2).returns(3, 4).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[1, 2, 3]), (ARRAY[4, 5])))") + .returns(1).returns(2).returns(3).returns(4).returns(5).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (MAP[1, 2, 3, 4]), (MAP[5, 6])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[ROW(1, 2), ROW(3, 4)]), (ARRAY[ROW(5, 6)])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + } + + /** */ + @Test + public void testUnnestMultiCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1], ARRAY[2])").returns(1, 2).check(); Review Comment: Should it return Output: unnest | unnest --------+-------- 1 | 2 | 3 ? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UnnestIntegrationTest.java: ########## @@ -0,0 +1,179 @@ +/* + * 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.processors.query.calcite.integration; + +import java.util.Collections; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Integration test for UNNEST operator. + */ +public class UnnestIntegrationTest extends AbstractBasicIntegrationTest { + /** */ + @Test + public void testUnnestSingleCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3])").returns(1).returns(2).returns(3).check(); + assertQuery("SELECT * FROM UNNEST(MAP['a', 1, 'b', 2])").returns("a", 1).returns("b", 2).check(); + assertQuery("SELECT * FROM UNNEST(ARRAY[ROW(1, 2), ROW(3, 4)])").returns(1, 2).returns(3, 4).check(); + + // Dynamic parameters. + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asList(1, 2)).returns(1).returns(2).check(); + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asMap("a", 1, "b", 2)) + .returns("a", 1).returns("b", 2).check(); + // Can't check dynamic parameters with ROW, since generic type of List can't be obtained in runtime. + // SQL type of parameter F.asList(new Object[] {1, 2}) will be derived as array of scalars + // (instead of array of rows) and UNNEST can only produce rows based on type derived on planner phase. + + // Subquery. + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(a) FROM (VALUES (1), (2)) t(a))") + .returns(1).returns(2).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(ROW(t.a, t.b)) FROM (VALUES (1, 2), (3, 4)) t(a, b))") + .returns(1, 2).returns(3, 4).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[1, 2, 3]), (ARRAY[4, 5])))") + .returns(1).returns(2).returns(3).returns(4).returns(5).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (MAP[1, 2, 3, 4]), (MAP[5, 6])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[ROW(1, 2), ROW(3, 4)]), (ARRAY[ROW(5, 6)])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + } + + /** */ + @Test + public void testUnnestMultiCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1], ARRAY[2])").returns(1, 2).check(); + assertQuery("SELECT * FROM UNNEST(ARRAY[1], ARRAY[2, 3])").returns(1, 2).returns(1, 3).check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], ARRAY[4, 5])") + .returns(1, 4).returns(1, 5) + .returns(2, 4).returns(2, 5) + .returns(3, 4).returns(3, 5) + .check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], ?)").withParams(F.asList(4, 5)) + .returns(1, 4).returns(1, 5) + .returns(2, 4).returns(2, 5) + .returns(3, 4).returns(3, 5) + .check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], MAP[4, 5, 6, 7])") + .returns(1, 4, 5).returns(1, 6, 7) + .returns(2, 4, 5).returns(2, 6, 7) + .returns(3, 4, 5).returns(3, 6, 7) + .check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[ROW(1, 2), ROW(3, 4)], MAP[5, 6, 7, 8], ARRAY[9, 10])") + .returns(1, 2, 5, 6, 9).returns(1, 2, 5, 6, 10) + .returns(1, 2, 7, 8, 9).returns(1, 2, 7, 8, 10) + .returns(3, 4, 5, 6, 9).returns(3, 4, 5, 6, 10) + .returns(3, 4, 7, 8, 9).returns(3, 4, 7, 8, 10) + .check(); + } + + /** */ + @Test + public void testUnnestMultiLineMultiCollection() { + assertQuery("SELECT c, d FROM (VALUES (ARRAY[1, 2], ARRAY[3, 4]), (ARRAY[5, 6], ARRAY[7])) v(a, b), " + Review Comment: Should it return 1 | 3 -- | -- 2 | 4 5 | 7 6 ? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UnnestIntegrationTest.java: ########## @@ -0,0 +1,179 @@ +/* + * 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.processors.query.calcite.integration; + +import java.util.Collections; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Integration test for UNNEST operator. + */ +public class UnnestIntegrationTest extends AbstractBasicIntegrationTest { + /** */ + @Test + public void testUnnestSingleCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3])").returns(1).returns(2).returns(3).check(); + assertQuery("SELECT * FROM UNNEST(MAP['a', 1, 'b', 2])").returns("a", 1).returns("b", 2).check(); + assertQuery("SELECT * FROM UNNEST(ARRAY[ROW(1, 2), ROW(3, 4)])").returns(1, 2).returns(3, 4).check(); + + // Dynamic parameters. + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asList(1, 2)).returns(1).returns(2).check(); + assertQuery("SELECT * FROM UNNEST(?)").withParams(F.asMap("a", 1, "b", 2)) + .returns("a", 1).returns("b", 2).check(); + // Can't check dynamic parameters with ROW, since generic type of List can't be obtained in runtime. + // SQL type of parameter F.asList(new Object[] {1, 2}) will be derived as array of scalars + // (instead of array of rows) and UNNEST can only produce rows based on type derived on planner phase. + + // Subquery. + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(a) FROM (VALUES (1), (2)) t(a))") + .returns(1).returns(2).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT ARRAY_AGG(ROW(t.a, t.b)) FROM (VALUES (1, 2), (3, 4)) t(a, b))") + .returns(1, 2).returns(3, 4).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[1, 2, 3]), (ARRAY[4, 5])))") + .returns(1).returns(2).returns(3).returns(4).returns(5).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (MAP[1, 2, 3, 4]), (MAP[5, 6])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + + assertQuery("SELECT * FROM UNNEST(SELECT * FROM (VALUES (ARRAY[ROW(1, 2), ROW(3, 4)]), (ARRAY[ROW(5, 6)])))") + .returns(1, 2).returns(3, 4).returns(5, 6).check(); + } + + /** */ + @Test + public void testUnnestMultiCollection() { + assertQuery("SELECT * FROM UNNEST(ARRAY[1], ARRAY[2])").returns(1, 2).check(); + assertQuery("SELECT * FROM UNNEST(ARRAY[1], ARRAY[2, 3])").returns(1, 2).returns(1, 3).check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], ARRAY[4, 5])") + .returns(1, 4).returns(1, 5) + .returns(2, 4).returns(2, 5) + .returns(3, 4).returns(3, 5) + .check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], ?)").withParams(F.asList(4, 5)) + .returns(1, 4).returns(1, 5) + .returns(2, 4).returns(2, 5) + .returns(3, 4).returns(3, 5) + .check(); + + assertQuery("SELECT * FROM UNNEST(ARRAY[1, 2, 3], MAP[4, 5, 6, 7])") Review Comment: Can we use UNNEST(ARRAY, MAP, MAP)? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/UncollectPlannerTest.java: ########## @@ -0,0 +1,141 @@ +/* + * 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.processors.query.calcite.planner; + +import org.apache.calcite.rel.core.Join; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.ImmutableIntList; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTrimExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteUncollect; +import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.broadcast; +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.single; + +/** + * Test uncollect operator. + */ +public class UncollectPlannerTest extends AbstractPlannerTest { + /** Public schema. */ + private IgniteSchema publicSchema; + + /** {@inheritDoc} */ + @Before + @Override public void setup() { + super.setup(); + + publicSchema = createSchema( + createTable("HASH_TBL", IgniteDistributions.hash(ImmutableIntList.of(0)), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("RANDOM_TBL", IgniteDistributions.random(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("BROADCAST_TBL", broadcast(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR) + ); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testSimpleUncollect() throws Exception { + String sql = "SELECT * FROM UNNEST(ARRAY[1, 2, 3])"; + + assertPlan(sql, publicSchema, isInstanceOf(Uncollect.class)); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testBroadcastTableAndUnnestJoin() throws Exception { + String sql = "SELECT * FROM broadcast_tbl t JOIN UNNEST(ARRAY[1, 2, 3]) AS r(x) ON (t.id = r.x)"; + + assertPlan(sql, publicSchema, nodeOrAnyChild(isInstanceOf(IgniteExchange.class)).negate() + .and(nodeOrAnyChild(isInstanceOf(Join.class) + .and(nodeOrAnyChild(isTableScan("broadcast_tbl"))) + .and(nodeOrAnyChild(isInstanceOf(IgniteUncollect.class) Review Comment: Duplicated `nodeOrAnyChild(isInstanceOf(IgniteUncollect.class)` ? Or nodeOrAnyChild(isInstanceOf(IgniteExchange.class).negate*()` ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/UncollectPlannerTest.java: ########## @@ -0,0 +1,141 @@ +/* + * 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.processors.query.calcite.planner; + +import org.apache.calcite.rel.core.Join; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.ImmutableIntList; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTrimExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteUncollect; +import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.broadcast; +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.single; + +/** + * Test uncollect operator. + */ +public class UncollectPlannerTest extends AbstractPlannerTest { + /** Public schema. */ + private IgniteSchema publicSchema; + + /** {@inheritDoc} */ + @Before + @Override public void setup() { + super.setup(); + + publicSchema = createSchema( + createTable("HASH_TBL", IgniteDistributions.hash(ImmutableIntList.of(0)), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("RANDOM_TBL", IgniteDistributions.random(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("BROADCAST_TBL", broadcast(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR) + ); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testSimpleUncollect() throws Exception { + String sql = "SELECT * FROM UNNEST(ARRAY[1, 2, 3])"; + + assertPlan(sql, publicSchema, isInstanceOf(Uncollect.class)); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testBroadcastTableAndUnnestJoin() throws Exception { + String sql = "SELECT * FROM broadcast_tbl t JOIN UNNEST(ARRAY[1, 2, 3]) AS r(x) ON (t.id = r.x)"; + + assertPlan(sql, publicSchema, nodeOrAnyChild(isInstanceOf(IgniteExchange.class)).negate() + .and(nodeOrAnyChild(isInstanceOf(Join.class) + .and(nodeOrAnyChild(isTableScan("broadcast_tbl"))) + .and(nodeOrAnyChild(isInstanceOf(IgniteUncollect.class) + .and(nodeOrAnyChild(isInstanceOf(IgniteUncollect.class) + .and(distributionSatisfies(single())) + )) + )) + )) + ); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testRandomTableAndUnnestJoin() throws Exception { + String sql = "SELECT * FROM random_tbl t JOIN UNNEST(ARRAY[1, 2, 3]) AS r(x) ON (t.id = r.x)"; + + assertPlan(sql, publicSchema, nodeOrAnyChild(isInstanceOf(Join.class) + .and(nodeOrAnyChild(isInstanceOf(IgniteExchange.class) + .and(nodeOrAnyChild(isTableScan("random_tbl"))))) + .and(nodeOrAnyChild(isInstanceOf(IgniteExchange.class).negate()) + .and(nodeOrAnyChild(isInstanceOf(IgniteUncollect.class) + .and(distributionSatisfies(single())) + )) + ) + )); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testHashTableAndUnnestJoin() throws Exception { + String sql = "SELECT * FROM hash_tbl t JOIN UNNEST(ARRAY[1, 2, 3]) AS r(x) ON (t.id = r.x)"; + + assertPlan(sql, publicSchema, nodeOrAnyChild(isInstanceOf(Join.class) + .and(nodeOrAnyChild(isInstanceOf(IgniteExchange.class).negate()) + .and(nodeOrAnyChild(isTableScan("hash_tbl")))) + .and(nodeOrAnyChild(isInstanceOf(IgniteTrimExchange.class)) Review Comment: Do we need TrimExchange here? Why not just IgniteUncollect? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java: ########## @@ -85,6 +85,9 @@ public class AbstractExecutionTest extends GridCommonAbstractTest { /** Params string. */ protected static final String PARAMS_STRING = "Task executor = {0}, Execution strategy = {1}"; + /** */ + protected static final int IN_BUFFER_SIZE = AbstractNode.IN_BUFFER_SIZE; Review Comment: Might be put in a dedicated ticket/ ########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteUncollect.java: ########## @@ -0,0 +1,121 @@ +/* + * 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.processors.query.calcite.rel; + +import java.util.Collections; +import java.util.List; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelInput; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.util.Pair; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; + +import static org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits; + +/** + * Implementation of {@link org.apache.calcite.rel.core.Uncollect} in {@link IgniteConvention Ignite calling convention}. + */ +public class IgniteUncollect extends Uncollect implements IgniteRel { + /** + * Creates an Uncollect relational operator. + */ + public IgniteUncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, boolean withOrdinality) { + super(cluster, traitSet, child, withOrdinality, Collections.emptyList()); + } + + /** */ + public IgniteUncollect(RelInput input) { + super(changeTraits(input, IgniteConvention.INSTANCE)); + } + + /** + * Creates an IgniteUncollect. + * + * <p>Each field of the input relational expression must be an array or multiset. + * + * @param traitSet Trait set + * @param input Input relational expression + * @param withOrdinality Whether output should contain an ORDINALITY column + */ + public static IgniteUncollect create(RelTraitSet traitSet, RelNode input, boolean withOrdinality) { + RelOptCluster cluster = input.getCluster(); + + return new IgniteUncollect(cluster, traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public RelNode copy(RelTraitSet traitSet, RelNode input) { + return new IgniteUncollect(getCluster(), traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public <T> T accept(IgniteRelVisitor<T> visitor) { + return visitor.visit(this); + } + + /** {@inheritDoc} */ + @Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) { + return new IgniteUncollect(cluster, getTraitSet(), sole(inputs), withOrdinality); + } + + /** {@inheritDoc} */ + @Override public double estimateRowCount(RelMetadataQuery mq) { + int fieldCnt = getInput().getRowType().getFieldCount(); + + // Assume every field contains collection of 4 elements (to simplify calculation). + long rowsMultiplier = fieldCnt > 15 ? Integer.MAX_VALUE : (1L << (fieldCnt * 2)); + + return rowsMultiplier * mq.getRowCount(getInput()); + } + + /** {@inheritDoc} */ + @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits(RelTraitSet required) { + if (required.getConvention() != IgniteConvention.INSTANCE) + return null; + + if (TraitUtils.collation(required) != RelCollations.EMPTY) + return null; + + if (TraitUtils.distribution(required).getType() == RelDistribution.Type.HASH_DISTRIBUTED) + return null; + + return Pair.of(required, ImmutableList.of(required)); + } + + /** {@inheritDoc} */ + @Override public Pair<RelTraitSet, List<RelTraitSet>> deriveTraits(RelTraitSet childTraits, int childId) { + assert childId == 0; + + if (childTraits.getConvention() != IgniteConvention.INSTANCE) + return null; + + RelTraitSet traits = childTraits.replace(RelCollations.EMPTY); Review Comment: Why change collation? Does any test check this? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; Review Comment: Can be placed out of the cycles. ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/UncollectExecutionTest.java: ########## @@ -0,0 +1,188 @@ +/* + * 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.processors.query.calcite.exec.rel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Test for UNCOLLECT node execution. + */ +public class UncollectExecutionTest extends AbstractExecutionTest { + /** */ + @SuppressWarnings("unchecked") + @Test + public void testSizes() { + for (boolean withOrdinality : new boolean[] {false, true}) { + for (int colCnt : new int[] {1, 2, 3}) { + int[] sizes = {1, inBufSize / 2 - 1, inBufSize / 2, inBufSize / 2 + 1, inBufSize, inBufSize + 1, inBufSize * 4}; + + for (int size : sizes) { + log.info("Check: size=" + size + ", colCnt=" + colCnt + ", withOrdinality=" + withOrdinality); + + Function<Integer, Object>[] funcs = new Function[colCnt]; + + for (int i = 0; i < colCnt; i++) { + int mul = 2 << i; + funcs[i] = r -> F.asList(mul * r, mul * r + 1); Review Comment: Naming: r->row? ########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteUncollect.java: ########## @@ -0,0 +1,121 @@ +/* + * 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.processors.query.calcite.rel; + +import java.util.Collections; +import java.util.List; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelInput; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.util.Pair; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; + +import static org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits; + +/** + * Implementation of {@link org.apache.calcite.rel.core.Uncollect} in {@link IgniteConvention Ignite calling convention}. + */ +public class IgniteUncollect extends Uncollect implements IgniteRel { + /** + * Creates an Uncollect relational operator. + */ + public IgniteUncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, boolean withOrdinality) { Review Comment: private? ########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteUncollect.java: ########## @@ -0,0 +1,121 @@ +/* + * 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.processors.query.calcite.rel; + +import java.util.Collections; +import java.util.List; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelInput; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.util.Pair; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; + +import static org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits; + +/** + * Implementation of {@link org.apache.calcite.rel.core.Uncollect} in {@link IgniteConvention Ignite calling convention}. + */ +public class IgniteUncollect extends Uncollect implements IgniteRel { + /** + * Creates an Uncollect relational operator. + */ + public IgniteUncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, boolean withOrdinality) { + super(cluster, traitSet, child, withOrdinality, Collections.emptyList()); + } + + /** */ + public IgniteUncollect(RelInput input) { Review Comment: Is it in use anywhere? ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/UncollectPlannerTest.java: ########## @@ -0,0 +1,141 @@ +/* + * 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.processors.query.calcite.planner; + +import org.apache.calcite.rel.core.Join; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.ImmutableIntList; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTrimExchange; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteUncollect; +import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.broadcast; +import static org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.single; + +/** + * Test uncollect operator. + */ +public class UncollectPlannerTest extends AbstractPlannerTest { + /** Public schema. */ + private IgniteSchema publicSchema; + + /** {@inheritDoc} */ + @Before + @Override public void setup() { + super.setup(); + + publicSchema = createSchema( + createTable("HASH_TBL", IgniteDistributions.hash(ImmutableIntList.of(0)), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("RANDOM_TBL", IgniteDistributions.random(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR), + createTable("BROADCAST_TBL", broadcast(), + "ID", SqlTypeName.INTEGER, "NAME", SqlTypeName.VARCHAR) + ); + } + + /** + * @throws Exception If failed. + */ + @Test + public void testSimpleUncollect() throws Exception { Review Comment: Maybe also test for two unnests like`SELECT * FROM UNNEST(...), UNNEST(...)` ########## modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UnnestIntegrationTest.java: ########## @@ -0,0 +1,179 @@ +/* + * 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.processors.query.calcite.integration; + +import java.util.Collections; +import org.apache.ignite.internal.util.typedef.F; +import org.junit.Test; + +/** + * Integration test for UNNEST operator. + */ +public class UnnestIntegrationTest extends AbstractBasicIntegrationTest { Review Comment: UcollectIntegrationTest like the other tests? ########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteUncollect.java: ########## @@ -0,0 +1,121 @@ +/* + * 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.processors.query.calcite.rel; + +import java.util.Collections; +import java.util.List; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelInput; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.util.Pair; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; + +import static org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits; + +/** + * Implementation of {@link org.apache.calcite.rel.core.Uncollect} in {@link IgniteConvention Ignite calling convention}. + */ +public class IgniteUncollect extends Uncollect implements IgniteRel { + /** + * Creates an Uncollect relational operator. + */ + public IgniteUncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, boolean withOrdinality) { + super(cluster, traitSet, child, withOrdinality, Collections.emptyList()); + } + + /** */ + public IgniteUncollect(RelInput input) { + super(changeTraits(input, IgniteConvention.INSTANCE)); + } + + /** + * Creates an IgniteUncollect. + * + * <p>Each field of the input relational expression must be an array or multiset. + * + * @param traitSet Trait set + * @param input Input relational expression + * @param withOrdinality Whether output should contain an ORDINALITY column + */ + public static IgniteUncollect create(RelTraitSet traitSet, RelNode input, boolean withOrdinality) { + RelOptCluster cluster = input.getCluster(); + + return new IgniteUncollect(cluster, traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public RelNode copy(RelTraitSet traitSet, RelNode input) { + return new IgniteUncollect(getCluster(), traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public <T> T accept(IgniteRelVisitor<T> visitor) { + return visitor.visit(this); + } + + /** {@inheritDoc} */ + @Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) { + return new IgniteUncollect(cluster, getTraitSet(), sole(inputs), withOrdinality); + } + + /** {@inheritDoc} */ + @Override public double estimateRowCount(RelMetadataQuery mq) { + int fieldCnt = getInput().getRowType().getFieldCount(); + + // Assume every field contains collection of 4 elements (to simplify calculation). + long rowsMultiplier = fieldCnt > 15 ? Integer.MAX_VALUE : (1L << (fieldCnt * 2)); + + return rowsMultiplier * mq.getRowCount(getInput()); + } + + /** {@inheritDoc} */ + @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits(RelTraitSet required) { + if (required.getConvention() != IgniteConvention.INSTANCE) + return null; + + if (TraitUtils.collation(required) != RelCollations.EMPTY) + return null; + + if (TraitUtils.distribution(required).getType() == RelDistribution.Type.HASH_DISTRIBUTED) + return null; + + return Pair.of(required, ImmutableList.of(required)); + } + + /** {@inheritDoc} */ + @Override public Pair<RelTraitSet, List<RelTraitSet>> deriveTraits(RelTraitSet childTraits, int childId) { + assert childId == 0; + + if (childTraits.getConvention() != IgniteConvention.INSTANCE) + return null; + + RelTraitSet traits = childTraits.replace(RelCollations.EMPTY); + + if (TraitUtils.distribution(traits).getType() == RelDistribution.Type.HASH_DISTRIBUTED) Review Comment: I commented these lines and none of the new tests failed. ########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteUncollect.java: ########## @@ -0,0 +1,121 @@ +/* + * 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.processors.query.calcite.rel; + +import java.util.Collections; +import java.util.List; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelInput; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Uncollect; +import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.util.Pair; +import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; +import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; + +import static org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits; + +/** + * Implementation of {@link org.apache.calcite.rel.core.Uncollect} in {@link IgniteConvention Ignite calling convention}. + */ +public class IgniteUncollect extends Uncollect implements IgniteRel { + /** + * Creates an Uncollect relational operator. + */ + public IgniteUncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, boolean withOrdinality) { + super(cluster, traitSet, child, withOrdinality, Collections.emptyList()); + } + + /** */ + public IgniteUncollect(RelInput input) { + super(changeTraits(input, IgniteConvention.INSTANCE)); + } + + /** + * Creates an IgniteUncollect. + * + * <p>Each field of the input relational expression must be an array or multiset. + * + * @param traitSet Trait set + * @param input Input relational expression + * @param withOrdinality Whether output should contain an ORDINALITY column + */ + public static IgniteUncollect create(RelTraitSet traitSet, RelNode input, boolean withOrdinality) { + RelOptCluster cluster = input.getCluster(); + + return new IgniteUncollect(cluster, traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public RelNode copy(RelTraitSet traitSet, RelNode input) { + return new IgniteUncollect(getCluster(), traitSet, input, withOrdinality); + } + + /** {@inheritDoc} */ + @Override public <T> T accept(IgniteRelVisitor<T> visitor) { + return visitor.visit(this); + } + + /** {@inheritDoc} */ + @Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) { + return new IgniteUncollect(cluster, getTraitSet(), sole(inputs), withOrdinality); + } + + /** {@inheritDoc} */ + @Override public double estimateRowCount(RelMetadataQuery mq) { + int fieldCnt = getInput().getRowType().getFieldCount(); + + // Assume every field contains collection of 4 elements (to simplify calculation). + long rowsMultiplier = fieldCnt > 15 ? Integer.MAX_VALUE : (1L << (fieldCnt * 2)); + + return rowsMultiplier * mq.getRowCount(getInput()); + } + + /** {@inheritDoc} */ + @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits(RelTraitSet required) { + if (required.getConvention() != IgniteConvention.INSTANCE) + return null; + + if (TraitUtils.collation(required) != RelCollations.EMPTY) + return null; + + if (TraitUtils.distribution(required).getType() == RelDistribution.Type.HASH_DISTRIBUTED) Review Comment: I commented these lines and none of the new tests failed. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@ignite.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org