This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new f18c63e788 IGNITE-18479 Sort out and merge Calcite tickets to Ignite
3.0 (step 9) (#1629)
f18c63e788 is described below
commit f18c63e7880d4b4e1ca5819c57a38679048653df
Author: ygerzhedovich <[email protected]>
AuthorDate: Thu Feb 23 15:13:15 2023 +0300
IGNITE-18479 Sort out and merge Calcite tickets to Ignite 3.0 (step 9)
(#1629)
---
.../ignite/internal/util/BoundedPriorityQueue.java | 70 +++++++++++
.../ignite/jdbc/ItJdbcConnectionSelfTest.java | 139 +--------------------
.../internal/sql/engine/ItAggregatesTest.java | 17 +++
.../internal/sql/engine/ItDataTypesTest.java | 129 +++++++++++++++++++
.../sql/engine/ItDynamicParameterTest.java | 17 +--
.../internal/sql/engine/ItSecondaryIndexTest.java | 25 ++++
.../internal/sql/engine/ItSqlOperatorsTest.java | 2 +
.../blob/{test_blob.test_ignore => test_blob.test} | 1 -
.../sql/types/blob/test_blob.test_ignore_old | 106 ----------------
...unction.test_ignore => test_blob_function.test} | 1 -
...perator.test_ignore => test_blob_operator.test} | 6 +-
.../types/blob/test_blob_operator.test_ignore_old | 65 ----------
...ob_string.test_ignore => test_blob_string.test} | 1 -
.../internal/sql/engine/exec/ExecutionContext.java | 8 ++
.../sql/engine/exec/LogicalRelImplementor.java | 6 +-
.../sql/engine/exec/exp/RexToLixTranslator.java | 13 +-
.../sql/engine/exec/exp/agg/Accumulators.java | 80 ++++++++++++
.../internal/sql/engine/exec/rel/SortNode.java | 70 ++++++++++-
.../sql/engine/metadata/IgniteMdRowCount.java | 7 ++
.../sql/engine/metadata/cost/IgniteCost.java | 6 +
.../internal/sql/engine/rel/IgniteLimit.java | 32 +----
.../ignite/internal/sql/engine/rel/IgniteSort.java | 74 ++++++++---
.../sql/engine/rule/SortConverterRule.java | 32 +++--
.../sql/engine/sql/fun/IgniteSqlOperatorTable.java | 3 +
.../internal/sql/engine/util/IgniteMethod.java | 4 +
.../ignite/internal/sql/engine/util/RexUtils.java | 44 ++++++-
.../ignite/internal/sql/engine/util/TypeUtils.java | 27 +++-
.../sql/engine/exec/rel/AbstractExecutionTest.java | 1 -
.../sql/engine/exec/rel/LimitExecutionTest.java | 110 +++++++++++++++-
.../sql/engine/planner/AbstractPlannerTest.java | 5 +
.../sql/engine/planner/LimitOffsetPlannerTest.java | 62 ++++++---
.../internal/sql/engine/planner/PlannerTest.java | 62 +++++++++
32 files changed, 811 insertions(+), 414 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/util/BoundedPriorityQueue.java
b/modules/core/src/main/java/org/apache/ignite/internal/util/BoundedPriorityQueue.java
new file mode 100644
index 0000000000..24618d06f9
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/util/BoundedPriorityQueue.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util;
+
+import java.util.Comparator;
+import java.util.PriorityQueue;
+
+/**
+ * Bounded variant of {@link PriorityQueue}.
+ *
+ * @param <E> Type of the queue element.
+ */
+public class BoundedPriorityQueue<E> extends PriorityQueue<E> {
+ private static final long serialVersionUID = 0L;
+
+ /** Queue max capacity. */
+ private final int maxCap;
+
+ /** Comparator. */
+ private final Comparator<? super E> cmp;
+
+ /**
+ * Creates a bounded priority queue with the specified maximum size. At
most {@code maxCap} elements would be kept in the queue.
+ *
+ * @param maxCap Maximum size of the queue.
+ * @param cmp Comparator that orders the elements.
+ */
+ public BoundedPriorityQueue(int maxCap, Comparator<? super E> cmp) {
+ super(maxCap, cmp);
+
+ if (cmp == null) {
+ throw new NullPointerException("comparator should not be null");
+ }
+
+ this.maxCap = maxCap;
+ this.cmp = cmp;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean offer(E e) {
+ if (size() >= maxCap) {
+ E head = peek();
+
+ if (cmp.compare(e, head) <= 0) {
+ return false;
+ }
+
+ poll();
+ }
+
+ return super.offer(e);
+ }
+}
+
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
index adc421f57c..af0229984a 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
@@ -28,14 +28,12 @@ import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
import static java.sql.Statement.NO_GENERATED_KEYS;
import static java.sql.Statement.RETURN_GENERATED_KEYS;
-import static
org.apache.ignite.internal.jdbc.proto.SqlStateCode.TRANSACTION_STATE_EXCEPTION;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@@ -513,7 +511,7 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15248")
public void testGetSetAutoCommit() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
boolean ac0 = conn.getAutoCommit();
@@ -574,43 +572,6 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
}
}
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testBeginFailsWhenMvccIsDisabled() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- conn.createStatement().execute("BEGIN");
-
- fail("Exception is expected");
- } catch (SQLException e) {
- assertEquals(TRANSACTION_STATE_EXCEPTION, e.getSQLState());
- }
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testCommitIgnoredWhenMvccIsDisabled() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- conn.setAutoCommit(false);
- conn.createStatement().execute("COMMIT");
-
- conn.commit();
- }
- // assert no exception
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testRollbackIgnoredWhenMvccIsDisabled() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- conn.setAutoCommit(false);
-
- conn.createStatement().execute("ROLLBACK");
-
- conn.rollback();
- }
- // assert no exception
- }
-
/**
* Test get metadata.
*
@@ -793,104 +754,6 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
}
}
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testSetSavepoint() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- assertFalse(conn.getMetaData().supportsSavepoints());
-
- // Disallowed in auto-commit mode
- assertThrows(
- SQLException.class,
- () -> conn.setSavepoint(),
- "Savepoint cannot be set in auto-commit mode"
- );
-
- conn.close();
-
- checkConnectionClosed(() -> conn.setSavepoint());
- }
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testSetSavepointName() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- assertFalse(conn.getMetaData().supportsSavepoints());
-
- // Invalid arg
- assertThrows(
- SQLException.class,
- () -> conn.setSavepoint(null),
- "Savepoint name cannot be null"
- );
-
- final String name = "savepoint";
-
- // Disallowed in auto-commit mode
- assertThrows(
- SQLException.class,
- () -> conn.setSavepoint(name),
- "Savepoint cannot be set in auto-commit mode"
- );
-
- conn.close();
-
- checkConnectionClosed(() -> conn.setSavepoint(name));
- }
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testRollbackSavePoint() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- assertFalse(conn.getMetaData().supportsSavepoints());
-
- // Invalid arg
- assertThrows(
- SQLException.class,
- () -> conn.rollback(null),
- "Invalid savepoint"
- );
-
- final Savepoint savepoint = getFakeSavepoint();
-
- // Disallowed in auto-commit mode
- assertThrows(
- SQLException.class,
- () -> conn.rollback(savepoint),
- "Auto-commit mode"
- );
-
- conn.close();
-
- checkConnectionClosed(() -> conn.rollback(savepoint));
- }
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
- public void testReleaseSavepoint() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- assertFalse(conn.getMetaData().supportsSavepoints());
-
- // Invalid arg
- assertThrows(
- SQLException.class,
- () -> conn.releaseSavepoint(null),
- "Savepoint cannot be null"
- );
-
- final Savepoint savepoint = getFakeSavepoint();
-
- checkNotSupported(() -> conn.releaseSavepoint(savepoint));
-
- conn.close();
-
- checkConnectionClosed(() -> conn.releaseSavepoint(savepoint));
- }
- }
-
@Test
public void testCreateClob() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
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 e28ca50ff2..5facfad006 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
@@ -330,6 +330,23 @@ public class ItAggregatesTest extends
AbstractBasicIntegrationTest {
}
}
+ @Test
+ public void testEverySomeAggregate() throws Exception {
+ sql("CREATE TABLE t(c0 INT PRIMARY KEY, c1 INT, c2 INT)");
+ sql("INSERT INTO t VALUES (1, null, 0)");
+ sql("INSERT INTO t VALUES (2, 0, null)");
+ sql("INSERT INTO t VALUES (3, null, null)");
+ sql("INSERT INTO t VALUES (4, 0, 1)");
+ sql("INSERT INTO t VALUES (5, 1, 1)");
+ sql("INSERT INTO t VALUES (6, 1, 2)");
+ sql("INSERT INTO t VALUES (7, 2, 2)");
+
+ assertQuery("SELECT EVERY(c1 < c2) FROM t").returns(false).check();
+ assertQuery("SELECT SOME(c1 < c2) FROM t").returns(true).check();
+ assertQuery("SELECT EVERY(c1 <= c2) FROM t").returns(true).check();
+ assertQuery("SELECT SOME(c1 > c2) FROM t").returns(false).check();
+ }
+
@Test
public void distinctAggregateWithoutAggregateFunction() {
var sql = "select distinct name from person";
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
index 064e991806..3da5dca2a0 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
@@ -19,9 +19,13 @@ package org.apache.ignite.internal.sql.engine;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.calcite.runtime.CalciteContextException;
@@ -210,6 +214,131 @@ public class ItDataTypesTest extends
AbstractBasicIntegrationTest {
.returns("2021-11-07 01:30:00").check();
}
+ /** Test Binary type. */
+ @Test
+ public void testBinarySql() {
+ sql("CREATE TABLE tbl(b BINARY(3) PRIMARY KEY, v VARBINARY)");
+ byte[] val = {1, 2, 3};
+
+ // From parameters to internal, from internal to store, from store to
internal and from internal to user.
+ sql("INSERT INTO tbl VALUES (?, ?)", val, val);
+
+ List<List<Object>> res = sql("SELECT b, v FROM tbl");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ assertTrue(Objects.deepEquals(val, res.get(0).get(0)));
+ assertTrue(Objects.deepEquals(val, res.get(0).get(1)));
+
+ sql("DELETE FROM tbl");
+
+ // From literal to internal, from internal to store, from store to
internal and from internal to user.
+ sql("INSERT INTO tbl VALUES (x'1A2B3C', x'AABBCC')");
+
+ res = sql("SELECT b, v FROM tbl");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ assertTrue(Objects.deepEquals(new byte[]{0x1A, 0x2B, 0x3C},
res.get(0).get(0)));
+ assertTrue(Objects.deepEquals(new byte[]{(byte) 0xAA, (byte) 0xBB,
(byte) 0xCC}, res.get(0).get(1)));
+ }
+
+ /** Test Aggregation functions for Binary type. */
+ @Test
+ public void testBinaryAggregation() {
+ sql("CREATE TABLE tbl(p int PRIMARY KEY,b varbinary)");
+ sql("INSERT INTO tbl VALUES (1, NULL)");
+ sql("INSERT INTO tbl VALUES (2, x'010203')");
+ sql("INSERT INTO tbl VALUES (3, x'040506')");
+ List<List<Object>> res = sql("SELECT MIN(b), MAX(b) FROM tbl");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ assertTrue(Objects.deepEquals(new byte[]{1, 2, 3}, res.get(0).get(0)));
+ assertTrue(Objects.deepEquals(new byte[]{4, 5, 6}, res.get(0).get(1)));
+
+ sql("INSERT INTO tbl VALUES (4, x'01'),(5, x'FF'), (6,
x'99aabbccddeeff00')");
+ res = sql("SELECT MIN(b), MAX(b) FROM tbl");
+
+ assertTrue(Objects.deepEquals(new byte[]{1}, res.get(0).get(0)));
+ assertTrue(Objects.deepEquals(new byte[]{-1}, res.get(0).get(1)));
+
+ sql("INSERT INTO tbl VALUES (7, x'FFFF')");
+ res = sql("SELECT MAX(b) FROM tbl");
+
+ assertTrue(Objects.deepEquals(new byte[]{-1, -1}, res.get(0).get(0)));
+ }
+
+ /** Test concatenation for Binary type. */
+ @Test
+ public void testBinaryConcat() {
+ sql("CREATE TABLE tbl(p int PRIMARY KEY, b varbinary)");
+ sql("INSERT INTO tbl VALUES (1, x'010203')");
+ List<List<Object>> res = sql("SELECT b || x'040506' FROM tbl");
+
+ assertEquals(1, res.size());
+ assertEquals(1, res.get(0).size());
+ assertTrue(Objects.deepEquals(new byte[]{1, 2, 3, 4, 5, 6},
res.get(0).get(0)));
+ }
+
+ /** Test decimal scale for dynamic parameters. */
+ @Test
+ public void testDecimalScale() {
+ sql("CREATE TABLE t (id INT PRIMARY KEY, val1 DECIMAL(5, 3), val2
DECIMAL(3), val3 DECIMAL)");
+
+ // Check literals scale.
+ sql("INSERT INTO t values (0, 0, 0, 0)");
+ sql("INSERT INTO t values (1.1, 1.1, 1.1, 1.1)");
+ sql("INSERT INTO t values (2.123, 2.123, 2.123, 2.123)");
+ sql("INSERT INTO t values (3.123456, 3.123456, 3.123456, 3.123456)");
+
+ // Check dynamic parameters scale.
+ List<Number> params = List.of(4, 5L, 6f, 7.25f, 8d, 9.03125d, new
BigDecimal("10"),
+ new BigDecimal("11.1"), new BigDecimal("12.123456"));
+
+ for (Object val : params) {
+ sql("INSERT INTO t values (?, ?, ?, ?)", val, val, val, val);
+ }
+
+ assertQuery("SELECT * FROM t")
+ .returns(0, new BigDecimal("0.000"), new BigDecimal("0"), new
BigDecimal("0"))
+ .returns(1, new BigDecimal("1.100"), new BigDecimal("1"), new
BigDecimal("1"))
+ .returns(2, new BigDecimal("2.123"), new BigDecimal("2"), new
BigDecimal("2"))
+ .returns(3, new BigDecimal("3.123"), new BigDecimal("3"), new
BigDecimal("3"))
+ .returns(4, new BigDecimal("4.000"), new BigDecimal("4"), new
BigDecimal("4"))
+ .returns(5, new BigDecimal("5.000"), new BigDecimal("5"), new
BigDecimal("5"))
+ .returns(6, new BigDecimal("6.000"), new BigDecimal("6"), new
BigDecimal("6"))
+ .returns(7, new BigDecimal("7.250"), new BigDecimal("7"), new
BigDecimal("7"))
+ .returns(8, new BigDecimal("8.000"), new BigDecimal("8"), new
BigDecimal("8"))
+ .returns(9, new BigDecimal("9.031"), new BigDecimal("9"), new
BigDecimal("9"))
+ .returns(10, new BigDecimal("10.000"), new BigDecimal("10"),
new BigDecimal("10"))
+ .returns(11, new BigDecimal("11.100"), new BigDecimal("11"),
new BigDecimal("11"))
+ .returns(12, new BigDecimal("12.123"), new BigDecimal("12"),
new BigDecimal("12"))
+ .check();
+ }
+
+ /** Tests conversion between numeric types. */
+ @Test
+ public void testNumericConversion() {
+ sql("CREATE TABLE t (v1 TINYINT PRIMARY KEY, v2 SMALLINT, v3 INT, v4
BIGINT, v5 DECIMAL, v6 FLOAT, v7 DOUBLE)");
+
+ List<Number> params = List.of((byte) 1, (short) 2, 3, 4L,
BigDecimal.valueOf(5), 6f, 7d);
+
+ for (Object val : params) {
+ sql("INSERT INTO t values (?, ?, ?, ?, ?, ?, ?)", val, val, val,
val, val, val, val);
+ }
+
+ assertQuery("SELECT * FROM t")
+ .returns((byte) 1, (short) 1, 1, 1L, BigDecimal.valueOf(1),
1f, 1d)
+ .returns((byte) 2, (short) 2, 2, 2L, BigDecimal.valueOf(2),
2f, 2d)
+ .returns((byte) 3, (short) 3, 3, 3L, BigDecimal.valueOf(3),
3f, 3d)
+ .returns((byte) 4, (short) 4, 4, 4L, BigDecimal.valueOf(4),
4f, 4d)
+ .returns((byte) 5, (short) 5, 5, 5L, BigDecimal.valueOf(5),
5f, 5d)
+ .returns((byte) 6, (short) 6, 6, 6L, BigDecimal.valueOf(6),
6f, 6d)
+ .returns((byte) 7, (short) 7, 7, 7L, BigDecimal.valueOf(7),
7f, 7d)
+ .check();
+ }
+
private LocalDate sqlDate(String str) {
return LocalDate.parse(str);
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
index f951ead249..fdc18de602 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
@@ -60,7 +60,7 @@ public class ItDynamicParameterTest extends
AbstractBasicIntegrationTest {
@ParameterizedTest
@EnumSource(value = ColumnType.class,
- // https://issues.apache.org/jira/browse/IGNITE-18258
+ // https://issues.apache.org/jira/browse/IGNITE-18789
// https://issues.apache.org/jira/browse/IGNITE-18414
// https://issues.apache.org/jira/browse/IGNITE-18415
// https://issues.apache.org/jira/browse/IGNITE-18345
@@ -78,7 +78,10 @@ public class ItDynamicParameterTest extends
AbstractBasicIntegrationTest {
public void testDynamicParameters() {
assertQuery("SELECT COALESCE(null,
?)").withParams(13).returns(13).check();
assertQuery("SELECT
LOWER(?)").withParams("ASD").returns("asd").check();
+ assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check();
+ assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check();
assertQuery("SELECT ?").withParams("asd").returns("asd").check();
+ assertQuery("SELECT ? % ?").withParams(11,
10).returns(BigDecimal.valueOf(1)).check();
assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2,
"TeSt").returns(4, "test").check();
assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2,
2).returns("test", 4).check();
@@ -88,17 +91,7 @@ public class ItDynamicParameterTest extends
AbstractBasicIntegrationTest {
assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT
?").withParams("I%", 1).returns(0).check();
assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT
? OFFSET ?").withParams("I%", 1, 1).returns(2).check();
- }
-
- // After fix the mute reason need to merge the test with above
testDynamicParameters
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-18258")
- @Test
- public void testDynamicParameters2() {
- assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8).check();
- assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check();
- assertQuery("SELECT ? % ?").withParams(11,
10).returns(BigDecimal.valueOf(1)).check();
-
- assertQuery("SELECT id from person where salary<? and
id>?").withParams(15, 1).returns(2).check();
+ assertQuery("SELECT id from person WHERE salary<? and
id<?").withParams(15, 3).returns(0).check();
}
// After fix the mute reason need to merge the test with above
testDynamicParameters
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSecondaryIndexTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSecondaryIndexTest.java
index 8ed83d4b16..63ec7ccd81 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSecondaryIndexTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSecondaryIndexTest.java
@@ -855,6 +855,31 @@ public class ItSecondaryIndexTest extends
AbstractBasicIntegrationTest {
.check();
}
+ /**
+ * Test index search bounds on complex index expression.
+ */
+ @Test
+ public void testComplexIndexExpression() {
+ assertQuery("SELECT id FROM Developer WHERE depId BETWEEN ? - 1 AND ?
+ 1")
+ .withParams(20, 20)
+ .matches(containsIndexScan("PUBLIC", "DEVELOPER", DEPID_IDX))
+ .returns(20)
+ .returns(21)
+ .returns(22)
+ .check();
+
+ assertQuery("SELECT id FROM Birthday WHERE name =
SUBSTRING(?::VARCHAR, 1, 4)")
+ .withParams("BachBach")
+ .matches(containsIndexScan("PUBLIC", "BIRTHDAY",
NAME_DATE_IDX))
+ .returns(3)
+ .check();
+
+ assertQuery("SELECT id FROM Birthday WHERE name = SUBSTRING(name, 1,
4)")
+ .matches(containsTableScan("PUBLIC", "BIRTHDAY"))
+ .returns(3)
+ .check();
+ }
+
@Test
@Disabled("https://issues.apache.org/jira/browse/IGNITE-18468")
public void testNullCondition1() {
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
index 73f2f51c4c..043961c3d6 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
@@ -98,6 +98,8 @@ public class ItSqlOperatorsTest extends
AbstractBasicIntegrationTest {
assertExpression("MAX(val)").returns(1).check();
assertExpression("ANY_VALUE(val)").returns(1).check();
assertExpression("COUNT(*) FILTER(WHERE val <>
1)").returns(0L).check();
+ assertExpression("EVERY(val = 1)").returns(true).check();
+ assertExpression("SOME(val = 1)").returns(true).check();
}
@Test
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore
b/modules/runner/src/integrationTest/sql/types/blob/test_blob.test
similarity index 96%
rename from
modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore
rename to modules/runner/src/integrationTest/sql/types/blob/test_blob.test
index 2e49350af9..ea38858017 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob.test
@@ -1,7 +1,6 @@
# name: test/sql/types/blob/test_blob.test
# description: BLOB tests
# group: [blob]
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-18373
statement ok
PRAGMA enable_verification
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore_old
b/modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore_old
deleted file mode 100644
index f0fca68199..0000000000
---
a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test_ignore_old
+++ /dev/null
@@ -1,106 +0,0 @@
-# name: test/sql/types/blob/test_blob.test
-# description: BLOB tests
-# group: [blob]
-# Ignore https://issues.apache.org/jira/browse/IGNITE-15123
-
-statement ok
-PRAGMA enable_verification
-
-statement ok
-CREATE TABLE blobs (b varbinary);
-
-# Insert valid hex strings
-statement ok
-INSERT INTO blobs VALUES(x'aaffaa'), (x'AAFFAAAAFFAA'), (x'AAFFAAAAFFAAAAFFAA')
-
-query T rowsort
-SELECT * FROM blobs
-----
-aaffaa
-aaffaaaaffaa
-aaffaaaaffaaaaffaa
-
-# Insert valid hex strings, lower case
-statement ok
-DELETE FROM blobs
-
-statement ok
-INSERT INTO blobs VALUES(x'aaffaa'), (x'aaffaaaaffaa'), (x'aaffaaaaffaaaaffaa')
-
-query T rowsort
-SELECT * FROM blobs
-----
-aaffaa
-aaffaaaaffaa
-aaffaaaaffaaaaffaa
-
-# Insert valid hex strings with number and letters
-statement ok
-DELETE FROM blobs
-
-statement ok
-INSERT INTO blobs VALUES(x'aa1199'), (x'aa1199aa1199'), (x'aa1199aa1199aa1199')
-
-query T rowsort
-SELECT * FROM blobs
-----
-aa1199
-aa1199aa1199
-aa1199aa1199aa1199
-
-# Insert invalid hex strings (invalid hex chars: G, H, I)
-statement error
-INSERT INTO blobs VALUES(x'GAFFAA')
-
-# Insert invalid hex strings (odd # of chars)
-statement error
-INSERT INTO blobs VALUES(x'A')
-
-statement error
-INSERT INTO blobs VALUES(x'AAA')
-
-statement ok
-DELETE FROM blobs
-
-# Implicit cast
-statement ok
-INSERT INTO blobs VALUES('blablabla')
-
-# BINARY with “non-printable” octets
-statement ok
-INSERT INTO blobs VALUES('abc �'::VARBINARY)
-
-query T
-SELECT b::varchar FROM blobs ORDER BY b
-----
-abc �
-blablabla
-
-# BINARY null and empty values
-query T
-SELECT ''::VARBINARY
-----
-(empty)
-
-query T
-SELECT NULL::VARBINARY
-----
-NULL
-
-statement ok
-CREATE TABLE blob_empty (b binary);
-
-statement ok
-INSERT INTO blob_empty VALUES(''), (''::VARBINARY)
-
-statement ok
-INSERT INTO blob_empty VALUES(NULL), (NULL::VARBINARY)
-
-query T rowsort
-SELECT * FROM blob_empty
-----
-NULL
-NULL
-(empty)
-(empty)
-
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test_ignore
b/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
similarity index 95%
rename from
modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test_ignore
rename to
modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
index 4a31af7431..80381607d6 100644
---
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test_ignore
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
@@ -1,7 +1,6 @@
# name: test/sql/types/blob/test_blob_function.test
# description: BLOB with Functions
# group: [blob]
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-18373
statement ok
PRAGMA enable_verification
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore
b/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
similarity index 93%
rename from
modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore
rename to
modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
index ec790415ac..3572d8e54a 100644
---
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
@@ -1,8 +1,6 @@
# name: test/sql/types/blob/test_blob_operator.test
# description: Test BLOBs with various SQL operators
# group: [blob]
-# Ignore https://issues.apache.org/jira/browse/IGNITE-15123
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-18393
statement ok
PRAGMA enable_verification
@@ -33,10 +31,10 @@ SELECT COUNT(*), COUNT(b), MIN(b), MAX(b) FROM blobs
query TI
SELECT * FROM blobs ORDER BY b
----
-NULL 0
00776861746973676f696e676f6e 1
68656c6c6f 0
fffefb 1
+NULL 0
# GROUP BY
statement ok
@@ -54,10 +52,10 @@ INSERT INTO blobs VALUES (x'FFFEFB', 19)
query II
SELECT b, SUM(g) FROM blobs GROUP BY b ORDER BY b
----
-NULL 0
00776861746973676f696e676f6e 10
68656c6c6f 3
fffefb 20
+NULL 0
# JOIN
statement ok
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore_old
b/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore_old
deleted file mode 100644
index 1fc938f9dd..0000000000
---
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test_ignore_old
+++ /dev/null
@@ -1,65 +0,0 @@
-# name: test/sql/types/blob/test_blob_operator.test
-# description: Test BLOBs with various SQL operators
-# group: [blob]
-# Ignore https://issues.apache.org/jira/browse/IGNITE-15123
-
-statement ok
-PRAGMA enable_verification
-
-statement ok
-CREATE TABLE blobs (b varbinary, g INTEGER);
-
-# strings: hello -> \x68656C6C6F, r -> \x72
-statement ok
-INSERT INTO blobs VALUES ('hello', 0), (x'00' || 'whatisgoingon'::varbinary,
1), (NULL, 0), (x'FFFEFB', 1)
-
-# simple aggregates only
-query IITT
-SELECT COUNT(*), COUNT(b), MIN(b), MAX(b) FROM blobs
-----
-4 3 00776861746973676f696e676f6e fffefb
-
-# ORDER BY
-query TI
-SELECT * FROM blobs ORDER BY b
-----
-NULL 0
-00776861746973676f696e676f6e 1
-68656c6c6f 0
-fffefb 1
-
-# GROUP BY
-statement ok
-INSERT INTO blobs VALUES ('hello', 3), (x'00' || 'whatisgoingon'::varbinary,
9), (NULL, 0), (x'FFFEFB', 19)
-
-query II
-SELECT b, SUM(g) FROM blobs GROUP BY b ORDER BY b
-----
-NULL 0
-00776861746973676f696e676f6e 10
-68656c6c6f 3
-fffefb 20
-
-# JOIN
-statement ok
-CREATE TABLE blobs2 (b VARBINARY, g INTEGER);
-
-statement ok
-INSERT INTO blobs2 VALUES ('hello', 0), (x'00' || 'whatisgoingon'::varbinary,
100), (NULL, 0), (x'FFFEFB', 200)
-
-# group by blobs.b, explicit JOIN
-query TR
-SELECT L.b, SUM(L.g) FROM blobs as L JOIN blobs2 AS R ON L.b=R.b GROUP BY L.b
ORDER BY L.b
-----
-00776861746973676f696e676f6e 10.000000
-68656c6c6f 3.000000
-fffefb 20.000000
-
-# group by blobs2.b, implicit JOIN
-query TR
-SELECT R.b, SUM(R.g) FROM blobs as L, blobs2 AS R WHERE L.b=R.b GROUP BY R.b
ORDER BY R.b
-----
-00776861746973676f696e676f6e 200.000000
-68656c6c6f 0.000000
-fffefb 400.000000
-
diff --git
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test_ignore
b/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
similarity index 94%
rename from
modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test_ignore
rename to
modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
index a9e741b89d..a4866ab117 100644
---
a/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test_ignore
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
@@ -1,7 +1,6 @@
# name: test/sql/types/blob/test_blob_string.test
# description: Insert BLOB values from normal strings
# group: [blob]
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-18373
statement ok
PRAGMA enable_verification
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
index cd56280d5b..72270990b2 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.sql.engine.exec;
import static org.apache.ignite.lang.ErrorGroups.Common.UNEXPECTED_ERR;
+import java.lang.reflect.Type;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -250,6 +251,13 @@ public class ExecutionContext<RowT> extends
AbstractQueryContext implements Data
return params.get(name);
}
+ /** Gets dynamic paremeters by name. */
+ public Object getParameter(String name, Type storageType) {
+ assert name.startsWith("?") : name;
+
+ return TypeUtils.toInternal(this, params.get(name), storageType);
+ }
+
/**
* Gets correlated value.
*
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 63ae7944e7..f07d992f4f 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
@@ -453,7 +453,11 @@ public class LogicalRelImplementor<RowT> implements
IgniteRelVisitor<Node<RowT>>
public Node<RowT> visit(IgniteSort rel) {
RelCollation collation = rel.getCollation();
- SortNode<RowT> node = new SortNode<>(ctx,
expressionFactory.comparator(collation));
+ Supplier<Integer> offset = (rel.offset == null) ? null :
expressionFactory.execute(rel.offset);
+ Supplier<Integer> fetch = (rel.fetch == null) ? null :
expressionFactory.execute(rel.fetch);
+
+ SortNode<RowT> node = new SortNode<>(ctx,
expressionFactory.comparator(collation), offset,
+ fetch);
Node<RowT> input = visit(rel.getInput());
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
index c78bf071a9..a55c8cc822 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
@@ -73,6 +73,7 @@ import org.apache.calcite.util.BuiltInMethod;
import org.apache.calcite.util.ControlFlowException;
import org.apache.calcite.util.Pair;
import org.apache.ignite.internal.sql.engine.type.IgniteCustomType;
+import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.type.UuidFunctions;
import org.apache.ignite.internal.sql.engine.type.UuidType;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
@@ -1261,10 +1262,14 @@ public class RexToLixTranslator implements
RexVisitor<RexToLixTranslator.Result>
final Type storageType = currentStorageType != null
? currentStorageType :
typeFactory.getJavaClass(dynamicParam.getType());
- final Expression valueExpression = ConverterUtils.convert(
- Expressions.call(root, BuiltInMethod.DATA_CONTEXT_GET.method,
- Expressions.constant("?" + dynamicParam.getIndex())),
- storageType);
+ final Type paramType = ((IgniteTypeFactory)
typeFactory).getResultClass(dynamicParam.getType());
+
+ final Expression ctxGet = Expressions.call(root,
IgniteMethod.CONTEXT_GET_PARAMETER_VALUE.method(),
+ Expressions.constant("?" + dynamicParam.getIndex()),
Expressions.constant(paramType));
+
+ final Expression valueExpression =
SqlTypeUtil.isDecimal(dynamicParam.getType())
+ ? ConverterUtils.convertToDecimal(ctxGet,
dynamicParam.getType())
+ : ConverterUtils.convert(ctxGet, storageType);
final ParameterExpression valueVariable =
Expressions.parameter(valueExpression.getType(),
list.newName("value_dynamic_param"));
list.add(Expressions.declare(Modifier.FINAL, valueVariable,
valueExpression));
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
index f6b875cfbc..2149ab3dbc 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
@@ -22,6 +22,7 @@ import static org.apache.calcite.sql.type.SqlTypeName.BIGINT;
import static org.apache.calcite.sql.type.SqlTypeName.DECIMAL;
import static org.apache.calcite.sql.type.SqlTypeName.DOUBLE;
import static org.apache.calcite.sql.type.SqlTypeName.INTEGER;
+import static org.apache.calcite.sql.type.SqlTypeName.VARBINARY;
import static org.apache.calcite.sql.type.SqlTypeName.VARCHAR;
import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
@@ -32,11 +33,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
+import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.ignite.internal.sql.engine.type.IgniteCustomType;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.apache.ignite.internal.util.ArrayUtils;
/**
* Accumulators.
@@ -80,8 +83,10 @@ public class Accumulators {
case "$SUM0":
return sumEmptyIsZeroFactory(call);
case "MIN":
+ case "EVERY":
return minMaxFactory(true, call);
case "MAX":
+ case "SOME":
return minMaxFactory(false, call);
case "SINGLE_VALUE":
return SingleVal.FACTORY;
@@ -164,6 +169,9 @@ public class Accumulators {
case CHAR:
case VARCHAR:
return min ? VarCharMinMax.MIN_FACTORY :
VarCharMinMax.MAX_FACTORY;
+ case BINARY:
+ case VARBINARY:
+ return min ? VarBinaryMinMax.MIN_FACTORY :
VarBinaryMinMax.MAX_FACTORY;
default:
if (type instanceof IgniteCustomType) {
return MinMaxAccumulator.newAccumulator(min, typeFactory,
type);
@@ -790,6 +798,78 @@ public class Accumulators {
}
}
+ private static class VarBinaryMinMax implements Accumulator {
+
+ public static final Supplier<Accumulator> MIN_FACTORY = () -> new
VarBinaryMinMax(true);
+
+
+ public static final Supplier<Accumulator> MAX_FACTORY = () -> new
VarBinaryMinMax(false);
+
+
+ private final boolean min;
+
+
+ private ByteString val;
+
+
+ private boolean empty = true;
+
+
+ private VarBinaryMinMax(boolean min) {
+ this.min = min;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void add(Object... args) {
+ ByteString in = (ByteString) args[0];
+
+ if (in == null) {
+ return;
+ }
+
+ val = empty ? in : min
+ ? (val.compareTo(in) < 0 ? val : in)
+ : (val.compareTo(in) < 0 ? in : val);
+
+ empty = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void apply(Accumulator other) {
+ VarBinaryMinMax other0 = (VarBinaryMinMax) other;
+
+ if (other0.empty) {
+ return;
+ }
+
+ val = empty ? other0.val : min
+ ? (val.compareTo(other0.val) < 0 ? val : other0.val)
+ : (val.compareTo(other0.val) < 0 ? other0.val : val);
+
+ empty = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object end() {
+ return empty ? null : val;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
+ return
ArrayUtils.asList(typeFactory.createTypeWithNullability(typeFactory.createSqlType(VARBINARY),
true));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RelDataType returnType(IgniteTypeFactory typeFactory) {
+ return
typeFactory.createTypeWithNullability(typeFactory.createSqlType(VARBINARY),
true);
+ }
+ }
+
private static class DistinctAccumulator implements Accumulator {
private final Accumulator acc;
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortNode.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortNode.java
index 079661662b..cf7ab16fe8 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortNode.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortNode.java
@@ -19,9 +19,14 @@ package org.apache.ignite.internal.sql.engine.exec.rel;
import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
+import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import java.util.PriorityQueue;
+import java.util.function.Supplier;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
+import org.apache.ignite.internal.util.BoundedPriorityQueue;
+import org.jetbrains.annotations.Nullable;
/**
* Sort node.
@@ -38,16 +43,45 @@ public class SortNode<RowT> extends AbstractNode<RowT>
implements SingleNode<Row
/** Rows buffer. */
private final PriorityQueue<RowT> rows;
+ /** SQL select limit. Negative if disabled. */
+ private final int limit;
+
+ /** Reverse-ordered rows in case of limited sort. */
+ private List<RowT> reversed;
+
/**
* Constructor.
*
- * @param ctx Execution context.
+ * @param ctx Execution context.
* @param comp Rows comparator.
+ * @param offset Offset.
+ * @param fetch Limit.
*/
- public SortNode(ExecutionContext<RowT> ctx, Comparator<RowT> comp) {
+ public SortNode(ExecutionContext<RowT> ctx,
+ Comparator<RowT> comp,
+ @Nullable Supplier<Integer> offset,
+ @Nullable Supplier<Integer> fetch) {
super(ctx);
+ assert fetch == null || fetch.get() >= 0;
+ assert offset == null || offset.get() >= 0;
- rows = comp == null ? new PriorityQueue<>() : new
PriorityQueue<>(comp);
+ limit = fetch == null ? -1 : fetch.get() + (offset == null ? 0 :
offset.get());
+
+ if (limit < 1) {
+ rows = new PriorityQueue<>(comp);
+ } else {
+ rows = new BoundedPriorityQueue<>(limit, comp == null ?
(Comparator<RowT>) Comparator.reverseOrder() : comp.reversed());
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param ctx Execution context.
+ * @param comp Rows comparator.
+ */
+ public SortNode(ExecutionContext<RowT> ctx, Comparator<RowT> comp) {
+ this(ctx, comp, null, null);
}
/** {@inheritDoc} */
@@ -56,6 +90,9 @@ public class SortNode<RowT> extends AbstractNode<RowT>
implements SingleNode<Row
requested = 0;
waiting = 0;
rows.clear();
+ if (reversed != null) {
+ reversed.clear();
+ }
}
/** {@inheritDoc} */
@@ -91,6 +128,7 @@ public class SortNode<RowT> extends AbstractNode<RowT>
implements SingleNode<Row
public void push(RowT row) throws Exception {
assert downstream() != null;
assert waiting > 0;
+ assert reversed == null || reversed.isEmpty();
checkState();
@@ -127,12 +165,32 @@ public class SortNode<RowT> extends AbstractNode<RowT>
implements SingleNode<Row
inLoop = true;
try {
- while (requested > 0 && !rows.isEmpty()) {
+ // Prepare final order (reversed).
+ if (limit > 0 && !rows.isEmpty()) {
+ if (reversed == null) {
+ reversed = new ArrayList<>(rows.size());
+ }
+
+ while (!rows.isEmpty()) {
+ reversed.add(rows.poll());
+
+ if (++processed >= inBufSize) {
+ // Allow the others to do their job.
+ context().execute(this::flush, this::onError);
+
+ return;
+ }
+ }
+
+ processed = 0;
+ }
+
+ while (requested > 0 && !(reversed == null ? rows.isEmpty() :
reversed.isEmpty())) {
checkState();
requested--;
- downstream().push(rows.poll());
+ downstream().push(reversed == null ? rows.poll() :
reversed.remove(reversed.size() - 1));
if (++processed >= inBufSize && requested > 0) {
// allow others to do their job
@@ -142,7 +200,7 @@ public class SortNode<RowT> extends AbstractNode<RowT>
implements SingleNode<Row
}
}
- if (rows.isEmpty()) {
+ if (reversed == null ? rows.isEmpty() : reversed.isEmpty()) {
if (requested > 0) {
downstream().end();
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/IgniteMdRowCount.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/IgniteMdRowCount.java
index 2a9b551657..e130a4f18f 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/IgniteMdRowCount.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/IgniteMdRowCount.java
@@ -25,6 +25,7 @@ import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.JoinInfo;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.core.Minus;
+import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMdRowCount;
import org.apache.calcite.rel.metadata.RelMdUtil;
@@ -56,6 +57,12 @@ public class IgniteMdRowCount extends RelMdRowCount {
return rel.estimateRowCount(mq);
}
+ /** {@inheritDoc} */
+ @Override
+ public Double getRowCount(Sort rel, RelMetadataQuery mq) {
+ return rel.estimateRowCount(mq);
+ }
+
/**
* RowCount of Spool equals to estimated row count of its child by
default, but IndexSpool has internal filter that
* could filter out some rows, hence we need to estimate it differently.
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/cost/IgniteCost.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/cost/IgniteCost.java
index 0533e44aa5..904836793f 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/cost/IgniteCost.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/cost/IgniteCost.java
@@ -42,6 +42,12 @@ public class IgniteCost implements RelOptCost {
/** Cost of a lookup at the hash. */
public static final double HASH_LOOKUP_COST = 10;
+ /** In case the fetch value is a DYNAMIC_PARAM. */
+ public static final double FETCH_IS_PARAM_FACTOR = 0.01;
+
+ /** In case the offset value is a DYNAMIC_PARAM. */
+ public static final double OFFSET_IS_PARAM_FACTOR = 0.5;
+
/**
* With broadcast distribution each row will be sent to the each
distination node, thus the total bytes amount will
* be multiplies of the destination nodes count. Right now it's just a
const.
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteLimit.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteLimit.java
index 2861de2f16..f6b4d4af5b 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteLimit.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteLimit.java
@@ -17,6 +17,10 @@
package org.apache.ignite.internal.sql.engine.rel;
+import static
org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCost.FETCH_IS_PARAM_FACTOR;
+import static
org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCost.OFFSET_IS_PARAM_FACTOR;
+import static
org.apache.ignite.internal.sql.engine.util.RexUtils.doubleFromRex;
+
import java.util.List;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
@@ -28,10 +32,8 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
-import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.util.Pair;
import org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCost;
import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
@@ -39,12 +41,6 @@ import
org.apache.ignite.internal.sql.engine.trait.TraitUtils;
/** Relational expression that applies a limit and/or offset to its input. */
public class IgniteLimit extends SingleRel implements IgniteRel {
- /** In case the fetch value is a DYNAMIC_PARAM. */
- private static final double FETCH_IS_PARAM_FACTOR = 0.01;
-
- /** In case the offset value is a DYNAMIC_PARAM. */
- private static final double OFFSET_IS_PARAM_FACTOR = 0.5;
-
/** Offset. */
private final RexNode offset;
@@ -180,26 +176,6 @@ public class IgniteLimit extends SingleRel implements
IgniteRel {
return Math.max(0, Math.min(lim, inputRowCount - off));
}
- /**
- * DoubleFromRex.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- *
- * @return Integer value of the literal expression.
- */
- private double doubleFromRex(RexNode n, double def) {
- try {
- if (n.isA(SqlKind.LITERAL)) {
- return ((RexLiteral) n).getValueAs(Integer.class);
- } else {
- return def;
- }
- } catch (Exception e) {
- assert false : "Unable to extract value: " + e.getMessage();
-
- return def;
- }
- }
-
/**
* Get offset.
*/
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteSort.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteSort.java
index e88bd27f44..6664df0709 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteSort.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/IgniteSort.java
@@ -17,7 +17,10 @@
package org.apache.ignite.internal.sql.engine.rel;
+import static
org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCost.FETCH_IS_PARAM_FACTOR;
+import static
org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCost.OFFSET_IS_PARAM_FACTOR;
import static
org.apache.ignite.internal.sql.engine.trait.TraitUtils.changeTraits;
+import static
org.apache.ignite.internal.sql.engine.util.RexUtils.doubleFromRex;
import java.util.List;
import org.apache.calcite.plan.RelOptCluster;
@@ -45,9 +48,30 @@ public class IgniteSort extends Sort implements IgniteRel {
/**
* Constructor.
*
- * @param cluster Cluster.
- * @param traits Trait set.
- * @param child Input node.
+ * @param cluster Cluster.
+ * @param traits Trait set.
+ * @param child Input node.
+ * @param collation Collation.
+ * @param offset Offset.
+ * @param fetch Limit.
+ */
+ public IgniteSort(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ RelNode child,
+ RelCollation collation,
+ RexNode offset,
+ RexNode fetch
+ ) {
+ super(cluster, traits, child, collation, offset, fetch);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cluster Cluster.
+ * @param traits Trait set.
+ * @param child Input node.
* @param collation Collation.
*/
public IgniteSort(
@@ -55,7 +79,7 @@ public class IgniteSort extends Sort implements IgniteRel {
RelTraitSet traits,
RelNode child,
RelCollation collation) {
- super(cluster, traits, child, collation);
+ this(cluster, traits, child, collation, null, null);
}
/**
@@ -75,9 +99,7 @@ public class IgniteSort extends Sort implements IgniteRel {
RexNode offset,
RexNode fetch
) {
- assert offset == null && fetch == null;
-
- return new IgniteSort(getCluster(), traitSet, newInput, newCollation);
+ return new IgniteSort(getCluster(), traitSet, newInput,
traitSet.getCollation(), offset, fetch);
}
/** {@inheritDoc} */
@@ -99,9 +121,14 @@ public class IgniteSort extends Sort implements IgniteRel {
return null;
}
- RelCollation collation = TraitUtils.collation(required);
+ RelCollation requiredCollation = TraitUtils.collation(required);
+ RelCollation relCollation = traitSet.getCollation();
- return Pair.of(required.replace(collation),
List.of(required.replace(RelCollations.EMPTY)));
+ if (!requiredCollation.satisfies(relCollation)) {
+ return null;
+ }
+
+ return Pair.of(required,
List.of(required.replace(RelCollations.EMPTY)));
}
/** {@inheritDoc} */
@@ -116,17 +143,26 @@ public class IgniteSort extends Sort implements IgniteRel
{
return Pair.of(childTraits.replace(collation()), List.of(childTraits));
}
+ /** {@inheritDoc} */
+ @Override
+ public double estimateRowCount(RelMetadataQuery mq) {
+ return memRows(mq.getRowCount(getInput()));
+ }
+
/** {@inheritDoc} */
@Override
public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery
mq) {
- double rows = mq.getRowCount(getInput());
+ double inputRows = mq.getRowCount(getInput());
+
+ double memRows = memRows(inputRows);
- double cpuCost = rows * IgniteCost.ROW_PASS_THROUGH_COST +
Util.nLogN(rows) * IgniteCost.ROW_COMPARISON_COST;
- double memory = rows * getRowType().getFieldCount() *
IgniteCost.AVERAGE_FIELD_SIZE;
+ double cpuCost = inputRows * IgniteCost.ROW_PASS_THROUGH_COST +
Util.nLogM(inputRows, memRows)
+ * IgniteCost.ROW_COMPARISON_COST;
+ double memory = memRows * getRowType().getFieldCount() *
IgniteCost.AVERAGE_FIELD_SIZE;
IgniteCostFactory costFactory = (IgniteCostFactory)
planner.getCostFactory();
- RelOptCost cost = costFactory.makeCost(rows, cpuCost, 0, memory, 0);
+ RelOptCost cost = costFactory.makeCost(inputRows, cpuCost, 0, memory,
0);
// Distributed sorting is more preferable than sorting on the single
node.
if (TraitUtils.distributionEnabled(this) &&
TraitUtils.distribution(traitSet).satisfies(IgniteDistributions.single())) {
@@ -139,6 +175,16 @@ public class IgniteSort extends Sort implements IgniteRel {
/** {@inheritDoc} */
@Override
public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) {
- return new IgniteSort(cluster, getTraitSet(), sole(inputs), collation);
+ return new IgniteSort(cluster, getTraitSet(), sole(inputs), collation,
offset, fetch);
+ }
+
+ /** Rows number to keep in memory and sort. */
+ private double memRows(double inputRows) {
+ double fetch = this.fetch != null ? doubleFromRex(this.fetch,
inputRows * FETCH_IS_PARAM_FACTOR)
+ : inputRows;
+ double offset = this.offset != null ? doubleFromRex(this.offset,
inputRows * OFFSET_IS_PARAM_FACTOR)
+ : 0;
+
+ return Math.min(inputRows, fetch + offset);
}
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortConverterRule.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortConverterRule.java
index b914f61232..37644d7535 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortConverterRule.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortConverterRule.java
@@ -17,11 +17,13 @@
package org.apache.ignite.internal.sql.engine.rule;
+import java.util.Map;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelRule;
import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.logical.LogicalSort;
@@ -68,15 +70,27 @@ public class SortConverterRule extends
RelRule<SortConverterRule.Config> {
.replace(sort.getCollation())
.replace(IgniteDistributions.single());
- call.transformTo(
- new IgniteLimit(
- cluster,
- traits,
- convert(sort.getInput(), traits),
- sort.offset,
- sort.fetch
- )
- );
+ if (sort.collation == RelCollations.EMPTY || sort.fetch == null) {
+ call.transformTo(new IgniteLimit(cluster, traits,
convert(sort.getInput(), traits), sort.offset,
+ sort.fetch));
+ } else {
+ RelNode igniteSort = new IgniteSort(
+ cluster,
+
cluster.traitSetOf(IgniteConvention.INSTANCE).replace(sort.getCollation()),
+ convert(sort.getInput(),
cluster.traitSetOf(IgniteConvention.INSTANCE)),
+ sort.getCollation(),
+ sort.offset,
+ sort.fetch
+ );
+
+ call.transformTo(
+ new IgniteLimit(cluster, traits, convert(igniteSort,
traits), sort.offset, sort.fetch),
+ Map.of(
+ new IgniteLimit(cluster, traits,
convert(sort.getInput(), traits), sort.offset, sort.fetch),
+ sort
+ )
+ );
+ }
} else {
RelTraitSet outTraits =
cluster.traitSetOf(IgniteConvention.INSTANCE).replace(sort.getCollation());
RelTraitSet inTraits =
cluster.traitSetOf(IgniteConvention.INSTANCE);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
index 12f451faad..59d39cd5bd 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
@@ -148,6 +148,9 @@ public class IgniteSqlOperatorTable extends
ReflectiveSqlOperatorTable {
register(SqlStdOperatorTable.SINGLE_VALUE);
register(SqlStdOperatorTable.FILTER);
+ register(SqlStdOperatorTable.EVERY);
+ register(SqlStdOperatorTable.SOME);
+
// IS ... operator.
register(SqlStdOperatorTable.IS_NULL);
register(SqlStdOperatorTable.IS_NOT_NULL);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
index 9bee587c82..043449d3ee 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.sql.engine.util;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
import java.util.Objects;
import org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
@@ -55,6 +56,9 @@ public enum IgniteMethod {
/** See {@link ExecutionContext#nullBound()}. */
CONTEXT_NULL_BOUND(ExecutionContext.class, "nullBound"),
+ /** See {@link ExecutionContext#getParameter(String, Type)}. */
+ CONTEXT_GET_PARAMETER_VALUE(ExecutionContext.class, "getParameter",
String.class, Type.class),
+
/** See {@link SingleScalar#execute(ExecutionContext, Object, Object)}. */
SCALAR_EXECUTE(SingleScalar.class, "execute", ExecutionContext.class,
Object.class, Object.class),
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
index 579ef1ad02..4af607fa83 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
@@ -744,7 +744,8 @@ public class RexUtils {
private static boolean idxOpSupports(RexNode op) {
return op instanceof RexLiteral
|| op instanceof RexDynamicParam
- || op instanceof RexFieldAccess;
+ || op instanceof RexFieldAccess
+ || !containsRef(op);
}
/**
@@ -905,6 +906,25 @@ public class RexUtils {
return cors;
}
+ /**
+ * Double value of the literal expression.
+ *
+ * @return Double value of the literal expression.
+ */
+ public static double doubleFromRex(RexNode n, double def) {
+ try {
+ if (n.isA(SqlKind.LITERAL)) {
+ return ((RexLiteral) n).getValueAs(Integer.class);
+ } else {
+ return def;
+ }
+ } catch (Exception e) {
+ assert false : "Unable to extract value: " + e.getMessage();
+
+ return def;
+ }
+ }
+
/**
* NotNullKeys.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
@@ -925,6 +945,28 @@ public class RexUtils {
return keys;
}
+ private static boolean containsRef(RexNode node) {
+ RexVisitor<Void> v = new RexVisitorImpl<>(true) {
+ @Override
+ public Void visitInputRef(RexInputRef inputRef) {
+ throw Util.FoundOne.NULL;
+ }
+
+ @Override
+ public Void visitLocalRef(RexLocalRef locRef) {
+ throw Util.FoundOne.NULL;
+ }
+ };
+
+ try {
+ node.accept(v);
+
+ return false;
+ } catch (Util.FoundOne e) {
+ return true;
+ }
+ }
+
/** Visitor for replacing scan local refs to input refs. */
private static class LocalRefReplacer extends RexShuttle {
private static final RexShuttle INSTANCE = new LocalRefReplacer();
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
index 5095e77570..0fe3988738 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.sql.engine.util;
import static org.apache.ignite.internal.sql.engine.util.Commons.transform;
import java.lang.reflect.Type;
+import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -42,6 +43,7 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.ignite.internal.schema.DecimalNativeType;
@@ -53,6 +55,7 @@ import
org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.type.IgniteCustomType;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.apache.ignite.internal.sql.engine.type.UuidFunctions;
import org.apache.ignite.internal.sql.engine.type.UuidType;
import org.apache.ignite.sql.ColumnType;
import org.jetbrains.annotations.NotNull;
@@ -265,9 +268,27 @@ public class TypeUtils {
return (int) ((Period) val).toTotalMonths();
} else if (storageType == byte[].class) {
return new ByteString((byte[]) val);
+ } else if (val instanceof Number && storageType != val.getClass()) {
+ // For dynamic parameters we don't know exact parameter type in
compile time. To avoid casting errors in
+ // runtime we should convert parameter value to expected type.
+ Number num = (Number) val;
+
+ return Byte.class.equals(storageType) ||
byte.class.equals(storageType) ? SqlFunctions.toByte(num) :
+ Short.class.equals(storageType) ||
short.class.equals(storageType) ? SqlFunctions.toShort(num) :
+ Integer.class.equals(storageType) ||
int.class.equals(storageType) ? SqlFunctions.toInt(num) :
+ Long.class.equals(storageType) ||
long.class.equals(storageType) ? SqlFunctions.toLong(num) :
+ Float.class.equals(storageType) ||
float.class.equals(storageType) ? SqlFunctions.toFloat(num) :
+
Double.class.equals(storageType) || double.class.equals(storageType)
+ ?
SqlFunctions.toDouble(num) :
+
BigDecimal.class.equals(storageType) ? SqlFunctions.toBigDecimal(num) : num;
} else if (storageType == UUID.class) {
- assert val instanceof UUID : storageTypeMismatch(val, UUID.class);
- return val;
+ //ToDo: This a quick fix of the issue
https://issues.apache.org/jira/browse/IGNITE-18831 and should be reworked.
+ if (val instanceof String) {
+ return UuidFunctions.cast(val);
+ } else {
+ assert val instanceof UUID : storageTypeMismatch(val,
UUID.class);
+ return val;
+ }
} else {
// IgniteCustomType: Add storageTypeMismatch assertion for your
type.
return val;
@@ -424,7 +445,7 @@ public class TypeUtils {
var varlen = (VarlenNativeType) nativeType;
- return factory.createSqlType(SqlTypeName.BINARY,
varlen.length());
+ return factory.createSqlType(SqlTypeName.VARBINARY,
varlen.length());
}
case BITMASK:
// TODO IGNITE-18431.
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
index 44e4462eaf..724c580ae8 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
@@ -61,7 +61,6 @@ public class AbstractExecutionTest extends IgniteAbstractTest
{
public static final Object[][] EMPTY = new Object[0][];
private Throwable lastE;
- private List<UUID> nodes;
private QueryTaskExecutorImpl taskExecutor;
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
index 061550601b..08de8e9400 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
@@ -17,13 +17,17 @@
package org.apache.ignite.internal.sql.engine.exec.rel;
+import static
org.apache.ignite.internal.sql.engine.util.Commons.IN_BUFFER_SIZE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.util.Commons;
import org.junit.jupiter.api.Test;
/**
@@ -33,7 +37,7 @@ public class LimitExecutionTest extends AbstractExecutionTest
{
/** Tests correct results fetched with Limit node. */
@Test
public void testLimit() {
- int bufSize = Commons.IN_BUFFER_SIZE;
+ int bufSize = IN_BUFFER_SIZE;
checkLimit(0, 1);
checkLimit(1, 0);
@@ -51,6 +55,58 @@ public class LimitExecutionTest extends
AbstractExecutionTest {
checkLimit(2000, 3000);
}
+ /** Tests Sort node can limit its output when fetch param is set. */
+ @Test
+ public void testSortLimit() throws Exception {
+ int bufSize = IN_BUFFER_SIZE;
+
+ checkLimitSort(0, 1);
+ checkLimitSort(1, 0);
+ checkLimitSort(1, 1);
+ checkLimitSort(0, bufSize);
+ checkLimitSort(bufSize, 0);
+ checkLimitSort(bufSize, bufSize);
+ checkLimitSort(bufSize - 1, 1);
+ checkLimitSort(2000, 0);
+ checkLimitSort(0, 3000);
+ checkLimitSort(2000, 3000);
+ }
+
+ /**
+ * Check limit sort.
+ *
+ * @param offset Rows offset.
+ * @param fetch Fetch rows count (zero means unlimited).
+ */
+ private void checkLimitSort(int offset, int fetch) {
+ assert offset >= 0;
+ assert fetch >= 0;
+
+ ExecutionContext<Object[]> ctx = executionContext();
+
+ RootNode<Object[]> rootNode = new RootNode<>(ctx);
+
+ SortNode<Object[]> sortNode = new SortNode<>(ctx,
LimitExecutionTest::compareArrays, () -> offset,
+ fetch == 0 ? null : () -> fetch);
+
+ List<Object[]> data = IntStream.range(0, IN_BUFFER_SIZE + fetch +
offset).boxed()
+ .map(i -> new Object[] {i}).collect(Collectors.toList());
+ Collections.shuffle(data);
+
+ ScanNode<Object[]> srcNode = new ScanNode<>(ctx, data);
+
+ rootNode.register(sortNode);
+
+ sortNode.register(srcNode);
+
+ for (int i = 0; i < offset + fetch; i++) {
+ assertTrue(rootNode.hasNext());
+ assertEquals(i, rootNode.next()[0]);
+ }
+
+ assertEquals(fetch == 0, rootNode.hasNext());
+ }
+
/**
* Check correct result size fetched.
*
@@ -82,6 +138,53 @@ public class LimitExecutionTest extends
AbstractExecutionTest {
}
}
+ /**
+ * Check the passed value to belong to array type.
+ *
+ * @param val Value to check.
+ * @return {@code True} if not null and array.
+ */
+ public static boolean isArray(Object val) {
+ return val != null && val.getClass().isArray();
+ }
+
+ /**
+ * Compare arrays.
+ *
+ * @param a1 Value 1.
+ * @param a2 Value 2.
+ * @return a negative integer, zero, or a positive integer as the first
argument is less than, equal to, or greater than the second.
+ */
+ public static int compareArrays(Object[] a1, Object[] a2) {
+ if (a1 == a2) {
+ return 0;
+ }
+
+ int l = Math.min(a1.length, a2.length);
+
+ for (int i = 0; i < l; i++) {
+ if (a1[i] == null || a2[i] == null) {
+ if (a1[i] != null || a2[i] != null) {
+ return a1[i] != null ? 1 : -1;
+ }
+
+ continue;
+ }
+
+ if (isArray(a1[i]) && isArray(a2[i])) {
+ int res = compareArrays((Object[]) a1[i], (Object[]) a2[i]);
+
+ if (res != 0) {
+ return res;
+ }
+ }
+
+ return ((Comparable) a1[i]).compareTo(a2[i]);
+ }
+
+ return Integer.compare(a1.length, a2.length);
+ }
+
private static class SourceNode extends AbstractNode<Object[]> {
AtomicInteger requested = new AtomicInteger();
@@ -90,7 +193,8 @@ public class LimitExecutionTest extends
AbstractExecutionTest {
}
/** {@inheritDoc} */
- @Override protected void rewindInternal() {
+ @Override
+ protected void rewindInternal() {
// No-op.
}
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
index 103821bc48..6490f9574e 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
@@ -106,6 +106,7 @@ import
org.apache.ignite.internal.sql.engine.prepare.MappingQueryContext;
import org.apache.ignite.internal.sql.engine.prepare.PlannerHelper;
import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
import org.apache.ignite.internal.sql.engine.prepare.Splitter;
+import org.apache.ignite.internal.sql.engine.prepare.bounds.ExactBounds;
import org.apache.ignite.internal.sql.engine.prepare.bounds.RangeBounds;
import org.apache.ignite.internal.sql.engine.prepare.bounds.SearchBounds;
import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
@@ -1324,6 +1325,10 @@ public abstract class AbstractPlannerTest extends
IgniteAbstractTest {
}
}
+ Predicate<SearchBounds> exact(Object val) {
+ return b -> b instanceof ExactBounds && matchValue(val, ((ExactBounds)
b).bound());
+ }
+
void assertBounds(String sql, List<Object> params, IgniteSchema schema,
Predicate<SearchBounds>... predicates) throws Exception {
assertPlan(sql, schema,
nodeOrAnyChild(isInstanceOf(IgniteIndexScan.class)
.and(scan -> matchBounds(scan.searchBounds(), predicates))),
params);
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
index e5f219cb68..ee052b2fed 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.sql.engine.planner;
+import static
org.apache.ignite.internal.sql.engine.util.RexUtils.doubleFromRex;
+
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.util.ImmutableIntList;
@@ -24,6 +26,7 @@ import
org.apache.ignite.internal.sql.engine.rel.IgniteExchange;
import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
import org.apache.ignite.internal.sql.engine.rel.IgniteLimit;
import org.apache.ignite.internal.sql.engine.rel.IgniteSort;
+import org.apache.ignite.internal.sql.engine.rel.IgniteTableScan;
import org.apache.ignite.internal.sql.engine.rel.IgniteUnionAll;
import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
@@ -83,44 +86,67 @@ public class LimitOffsetPlannerTest extends
AbstractPlannerTest {
IgniteSchema publicSchema =
createSchemaWithTable(IgniteDistributions.random());
// Simple case, Limit can't be pushed down under Exchange or Sort.
Sort before Exchange is more preferable.
- assertPlan("SELECT * FROM TEST ORDER BY ID LIMIT 10 OFFSET 10",
publicSchema,
+ assertPlan("SELECT * FROM TEST ORDER BY ID LIMIT 5 OFFSET 10",
publicSchema,
isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteExchange.class)
- .and(input(isInstanceOf(IgniteSort.class))))));
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.fetch, -1)
== 5.0)
+ .and(s -> doubleFromRex(s.offset, -1)
== 10.0))))));
- // Simple case without ordering.
- assertPlan("SELECT * FROM TEST OFFSET 10 ROWS FETCH FIRST 10 ROWS
ONLY", publicSchema,
+ // Same simple case but witout offset.
+ assertPlan("SELECT * FROM TEST ORDER BY ID LIMIT 5", publicSchema,
isInstanceOf(IgniteLimit.class)
- .and(input(isInstanceOf(IgniteExchange.class)))
-
.and(hasChildThat(isInstanceOf(IgniteSort.class)).negate()));
+ .and(input(isInstanceOf(IgniteExchange.class)
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.fetch, -1)
== 5.0)
+ .and(s -> s.offset == null))))));
+
+ // No special liited sort required if LIMIT is not set.
+ assertPlan("SELECT * FROM TEST ORDER BY ID OFFSET 10", publicSchema,
+ isInstanceOf(IgniteLimit.class)
+ .and(input(isInstanceOf(IgniteExchange.class)
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> s.fetch == null)
+ .and(s -> s.offset == null))))));
+ // Simple case without ordering.
+ assertPlan("SELECT * FROM TEST OFFSET 10 ROWS FETCH FIRST 5 ROWS
ONLY", publicSchema,
+ isInstanceOf(IgniteLimit.class)
+ .and(s -> doubleFromRex(s.fetch(), -1) == 5)
+ .and(s -> doubleFromRex(s.offset(), -1) == 10)
+ .and(input(isInstanceOf(IgniteExchange.class)))
+
.and(hasChildThat(isInstanceOf(IgniteSort.class)).negate()));
// Check that Sort node is not eliminated by aggregation and Exchange
node is not eliminated by distribution
// required by parent nodes.
assertPlan("SELECT * FROM TEST UNION (SELECT * FROM TEST ORDER BY ID
LIMIT 10)", publicSchema,
nodeOrAnyChild(isInstanceOf(IgniteUnionAll.class)
- .and(hasChildThat(isInstanceOf(IgniteLimit.class)
- .and(input(isInstanceOf(IgniteExchange.class)
- .and(input(isInstanceOf(IgniteSort.class)))))))));
+ .and(hasChildThat(isInstanceOf(IgniteLimit.class)
+ .and(input(isInstanceOf(IgniteExchange.class)
+
.and(input(isInstanceOf(IgniteSort.class)
+ .and(s ->
doubleFromRex(s.fetch, -1) == 10.0)))))))));
// Check that internal Sort node is not eliminated by external Sort
node with different collation.
assertPlan("SELECT * FROM (SELECT * FROM TEST ORDER BY ID LIMIT 10)
ORDER BY VAL", publicSchema,
nodeOrAnyChild(isInstanceOf(IgniteSort.class)
.and(hasChildThat(isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteExchange.class)
- .and(input(isInstanceOf(IgniteSort.class)))))))));
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.fetch, -1)
== 10.0)))))))));
// Check that extended collation is passed through the Limit node if
it satisfies the Limit collation.
assertPlan("SELECT * FROM (SELECT * FROM TEST ORDER BY ID LIMIT 10)
ORDER BY ID, VAL", publicSchema,
isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteExchange.class)
.and(input(isInstanceOf(IgniteSort.class)
- .and(s ->
s.collation().getKeys().equals(ImmutableIntList.of(0, 1))))))));
+
.and(input(isInstanceOf(IgniteTableScan.class)))
+ .and(s ->
s.collation().getKeys().equals(ImmutableIntList.of(0, 1))))))));
// Check that external Sort node is not required if external collation
is subset of internal collation.
assertPlan("SELECT * FROM (SELECT * FROM TEST ORDER BY ID, VAL LIMIT
10) ORDER BY ID", publicSchema,
isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteExchange.class)
- .and(input(isInstanceOf(IgniteSort.class))))));
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.fetch, -1) ==
10.0))))));
// Check double limit when external collation is a subset of internal
collation.
assertPlan("SELECT * FROM (SELECT * FROM TEST ORDER BY ID, VAL LIMIT
10) ORDER BY ID LIMIT 5 OFFSET 3",
@@ -128,15 +154,19 @@ public class LimitOffsetPlannerTest extends
AbstractPlannerTest {
isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteLimit.class)
.and(input(isInstanceOf(IgniteExchange.class)
- .and(input(isInstanceOf(IgniteSort.class))))))));
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.fetch, -1)
== 10.0)
+ .and(s -> s.offset == null))))))));
// Check limit/exchange/sort rel order in subquery.
assertPlan("SELECT NULLIF((SELECT id FROM test ORDER BY id LIMIT 1
OFFSET 1), id) FROM test",
publicSchema,
hasChildThat(isInstanceOf(IgniteLimit.class)
- .and(input(isInstanceOf(IgniteExchange.class)
- .and(e -> e.distribution() ==
IgniteDistributions.single())
- .and(input(isInstanceOf(IgniteSort.class)))))));
+ .and(input(isInstanceOf(IgniteExchange.class)
+ .and(e -> e.distribution() ==
IgniteDistributions.single())
+ .and(input(isInstanceOf(IgniteSort.class)
+ .and(s -> doubleFromRex(s.offset, -1)
== 1)
+ .and(s -> doubleFromRex(s.fetch, -1)
== 1)))))));
publicSchema = createSchemaWithTable(IgniteDistributions.random(), 0);
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PlannerTest.java
index 6aa5cb3f4c..2a6ab9f3d1 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PlannerTest.java
@@ -493,6 +493,59 @@ public class PlannerTest extends AbstractPlannerTest {
);
}
+ /** Tests complex bounds expressions. */
+ @Test
+ public void testBoundsComplex() throws Exception {
+ IgniteTypeFactory typeFactory = new
IgniteTypeFactory(IgniteTypeSystem.INSTANCE);
+
+ TestTable tbl = new TestTable(
+ new RelDataTypeFactory.Builder(typeFactory)
+ .add("C1", typeFactory.createJavaType(Integer.class))
+ .add("C2", typeFactory.createJavaType(Integer.class))
+ .add("C3", typeFactory.createJavaType(Integer.class))
+ .add("C4", typeFactory.createJavaType(Integer.class))
+ .build(), "TEST") {
+ @Override
+ public ColocationGroup colocationGroup(MappingQueryContext ctx) {
+ return ColocationGroup.forNodes(select(NODES, 0));
+ }
+
+ @Override
+ public IgniteDistribution distribution() {
+ return IgniteDistributions.hash(List.of(0));
+ }
+ };
+
+ tbl.addIndex("C1C2C3", 0, 1, 2);
+
+ IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
+
+ publicSchema.addTable(tbl);
+
+ assertBounds("SELECT * FROM TEST WHERE C1 = ? + 10", List.of(10),
publicSchema,
+ exact("+(?0, 10)")
+ );
+
+ assertBounds("SELECT * FROM TEST WHERE C1 = 1 AND C2 >
SUBSTRING(?::VARCHAR, 1, 2) || '3'", List.of("10"), publicSchema,
+ exact(1),
+ range("||(SUBSTRING(?0, 1, 2), _UTF-8'3')", "$NULL_BOUND()",
false, false)
+ );
+
+ assertBounds("SELECT * FROM TEST WHERE C1 = 1 AND C2 >
SUBSTRING(C3::VARCHAR, 1, 2) || '3'", List.of(), publicSchema,
+ exact(1),
+ empty()
+ );
+
+ assertBounds("SELECT (SELECT C1 FROM TEST t2 WHERE t2.C1 = t1.C1 +
t1.C3 * ?) FROM TEST t1", List.of(10), publicSchema,
+ exact("+($cor0.C1, *($cor0.C3, ?0))")
+ );
+
+ assertPlan("SELECT * FROM TEST WHERE C1 = ? + C3", publicSchema,
nodeOrAnyChild(isTableScan("TEST")), List.of(10));
+
+ assertPlan("SELECT (SELECT C1 FROM TEST t2 WHERE t2.C1 < t1.C1 +
t2.C1) FROM TEST t1", publicSchema,
+ nodeOrAnyChild(isIndexScan("TEST", "C1C2C3")).negate());
+ }
+
/** String representation of LEAST or GREATEST operator converted to CASE.
*/
private String leastOrGreatest(boolean least, String val0, String val1,
String type) {
return "CASE(OR(IS NULL(" + val0 + "), IS NULL(" + val1 + ")), null:"
+ type + ", " + (least ? '<' : '>')
@@ -961,6 +1014,15 @@ public class PlannerTest extends AbstractPlannerTest {
assertPlan(sql, Collections.singleton(publicSchema), hintCheck,
hintStrategies, List.of());
}
+ @Test
+ public void testMinusDateSerialization() throws Exception {
+ IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
+
+ IgniteRel phys = physicalPlan("SELECT (DATE '2021-03-01' - DATE
'2021-01-01') MONTHS", publicSchema);
+
+ checkSplitAndSerialization(phys, publicSchema);
+ }
+
/**
* IntermediateMapping.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859