You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ko...@apache.org on 2022/06/21 07:51:27 UTC
[ignite-3] branch main updated: IGNITE-17094 ColumnMetadata.nullable() returns value for non-nullable columns (#887)
This is an automated email from the ASF dual-hosted git repository.
korlov 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 ae6a93c51 IGNITE-17094 ColumnMetadata.nullable() returns value for non-nullable columns (#887)
ae6a93c51 is described below
commit ae6a93c51ee42caf9df77b24226fedc1ae36cac1
Author: korlov42 <ko...@gridgain.com>
AuthorDate: Tue Jun 21 10:51:22 2022 +0300
IGNITE-17094 ColumnMetadata.nullable() returns value for non-nullable columns (#887)
---
.../internal/sql/api/ItSqlAsynchronousApiTest.java | 7 +-
.../internal/sql/engine/ItDataTypesTest.java | 141 ++++++-------
.../internal/sql/engine/ItFunctionsTest.java | 8 +-
.../ignite/internal/sql/engine/ItMetadataTest.java | 6 +-
.../internal/sql/engine/util/QueryChecker.java | 4 +-
.../ignite/internal/schema/NativeTypeSpec.java | 48 +++++
.../sql/engine/exec/ExecutionServiceImpl.java | 2 +-
.../sql/engine/exec/exp/IgniteSqlFunctions.java | 7 +
.../internal/sql/engine/exec/exp/RexImpTable.java | 2 +-
.../sql/engine/prepare/PrepareServiceImpl.java | 10 +-
.../sql/engine/schema/ColumnDescriptor.java | 22 +-
.../sql/engine/schema/ColumnDescriptorImpl.java | 61 ++++--
...mnDescriptor.java => DefaultValueStrategy.java} | 36 ++--
.../sql/engine/schema/IgniteTableImpl.java | 12 +-
.../sql/engine/schema/SqlSchemaManagerImpl.java | 2 +
.../sql/engine/schema/TableDescriptorImpl.java | 36 ++--
.../sql/engine/type/IgniteTypeFactory.java | 7 +-
.../internal/sql/engine/util/IgniteMethod.java | 6 +-
.../ignite/internal/sql/engine/util/TypeUtils.java | 222 +++++++++++----------
.../sql/engine/planner/AbstractPlannerTest.java | 17 +-
20 files changed, 373 insertions(+), 283 deletions(-)
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
index c64f0335e..2f45a0a77 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
@@ -216,7 +216,7 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
AsyncResultSet rs = ses.executeAsync(null, "SELECT COL1, COL0 FROM TEST").get();
- // Validata columns metadata.
+ // Validate columns metadata.
ResultSetMetadata meta = rs.metadata();
assertNotNull(meta);
@@ -224,10 +224,9 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
assertEquals(0, meta.indexOf("COL1"));
assertEquals(1, meta.indexOf("COL0"));
- //TODO: IGNITE-17094: ColumnMetadata.nullable() must return false for non-null column.
- checkMetadata(new ColumnMetadataImpl("COL1", SqlColumnType.STRING, 0, 0, true, new ColumnOriginImpl("PUBLIC", "TEST", "COL1")),
+ checkMetadata(new ColumnMetadataImpl("COL1", SqlColumnType.STRING, 0, 0, false, new ColumnOriginImpl("PUBLIC", "TEST", "COL1")),
meta.columns().get(0));
- checkMetadata(new ColumnMetadataImpl("COL0", SqlColumnType.INT64, 0, 0, true, new ColumnOriginImpl("PUBLIC", "TEST", "COL0")),
+ checkMetadata(new ColumnMetadataImpl("COL0", SqlColumnType.INT64, 0, 0, false, new ColumnOriginImpl("PUBLIC", "TEST", "COL0")),
meta.columns().get(1));
// Validate result columns types.
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 359998734..f9f129333 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
@@ -23,59 +23,74 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
-import org.apache.ignite.lang.IgniteException;
-import org.junit.jupiter.api.Disabled;
+import org.apache.calcite.runtime.CalciteContextException;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
/**
* Test SQL data types.
*/
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-16679")
public class ItDataTypesTest extends AbstractBasicIntegrationTest {
+ /**
+ * Drops all created tables.
+ */
+ @AfterEach
+ public void dropTables() {
+ var igniteTables = CLUSTER_NODES.get(0).tables();
+
+ var tables = igniteTables.tables();
+
+ var futs = new CompletableFuture<?>[tables.size()];
+
+ int idx = 0;
+ for (var table : tables) {
+ futs[idx++] = igniteTables.dropTableAsync(table.name());
+ }
+
+ CompletableFuture.allOf(futs).join();
+ }
+
/** Tests correctness with unicode. */
@Test
public void testUnicodeStrings() {
- try {
- sql("CREATE TABLE string_table(key int primary key, val varchar)");
+ sql("CREATE TABLE string_table(key int primary key, val varchar)");
- String[] values = new String[]{"Кирилл", "Müller", "我是谁", "ASCII"};
+ String[] values = new String[]{"Кирилл", "Müller", "我是谁", "ASCII"};
- int key = 0;
+ int key = 0;
- // Insert as inlined values.
- for (String val : values) {
- sql("INSERT INTO string_table (key, val) VALUES (?, ?)", key++, val);
- }
+ // Insert as inlined values.
+ for (String val : values) {
+ sql("INSERT INTO string_table (key, val) VALUES (?, ?)", key++, val);
+ }
- var rows = sql("SELECT val FROM string_table");
+ var rows = sql("SELECT val FROM string_table");
- assertEquals(Set.of(values), rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
+ assertEquals(Set.of(values), rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
- sql("DELETE FROM string_table");
+ sql("DELETE FROM string_table");
- // Insert as parameters.
- for (String val : values) {
- sql("INSERT INTO string_table (key, val) VALUES (?, ?)", key++, val);
- }
+ // Insert as parameters.
+ for (String val : values) {
+ sql("INSERT INTO string_table (key, val) VALUES (?, ?)", key++, val);
+ }
- rows = sql("SELECT val FROM string_table");
+ rows = sql("SELECT val FROM string_table");
- assertEquals(Set.of(values), rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
+ assertEquals(Set.of(values), rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
- rows = sql("SELECT substring(val, 1, 2) FROM string_table");
+ rows = sql("SELECT substring(val, 1, 2) FROM string_table");
- assertEquals(Set.of("Ки", "Mü", "我是", "AS"),
- rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
+ assertEquals(Set.of("Ки", "Mü", "我是", "AS"),
+ rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
- for (String val : values) {
- rows = sql("SELECT char_length(val) FROM string_table WHERE val = ?", val);
+ for (String val : values) {
+ rows = sql("SELECT char_length(val) FROM string_table WHERE val = ?", val);
- assertEquals(1, rows.size());
- assertEquals(val.length(), rows.get(0).get(0));
- }
- } finally {
- sql("DROP TABLE IF EXISTS string_table");
+ assertEquals(1, rows.size());
+ assertEquals(val.length(), rows.get(0).get(0));
}
}
@@ -97,7 +112,7 @@ public class ItDataTypesTest extends AbstractBasicIntegrationTest {
assertEquals(Set.of(101), rows.stream().map(r -> r.get(0)).collect(Collectors.toSet()));
//todo: correct exception https://issues.apache.org/jira/browse/IGNITE-16095
- assertThrows(IgniteException.class, () -> sql("INSERT INTO tbl(c1, c2) VALUES (2, NULL)"));
+ assertThrows(CalciteContextException.class, () -> sql("INSERT INTO tbl(c1, c2) VALUES (2, NULL)"));
}
/**
@@ -105,29 +120,25 @@ public class ItDataTypesTest extends AbstractBasicIntegrationTest {
*/
@Test
public void testNumericRanges() {
- try {
- sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");
+ sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");
- sql("INSERT INTO tbl VALUES (1, " + Byte.MAX_VALUE + ", " + Short.MAX_VALUE + ", "
- + Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ')');
+ sql("INSERT INTO tbl VALUES (1, " + Byte.MAX_VALUE + ", " + Short.MAX_VALUE + ", "
+ + Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ')');
- assertQuery("SELECT tiny FROM tbl").returns(Byte.MAX_VALUE).check();
- assertQuery("SELECT small FROM tbl").returns(Short.MAX_VALUE).check();
- assertQuery("SELECT i FROM tbl").returns(Integer.MAX_VALUE).check();
- assertQuery("SELECT big FROM tbl").returns(Long.MAX_VALUE).check();
+ assertQuery("SELECT tiny FROM tbl").returns(Byte.MAX_VALUE).check();
+ assertQuery("SELECT small FROM tbl").returns(Short.MAX_VALUE).check();
+ assertQuery("SELECT i FROM tbl").returns(Integer.MAX_VALUE).check();
+ assertQuery("SELECT big FROM tbl").returns(Long.MAX_VALUE).check();
- sql("DELETE from tbl");
+ sql("DELETE from tbl");
- sql("INSERT INTO tbl VALUES (1, " + Byte.MIN_VALUE + ", " + Short.MIN_VALUE + ", "
- + Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ')');
+ sql("INSERT INTO tbl VALUES (1, " + Byte.MIN_VALUE + ", " + Short.MIN_VALUE + ", "
+ + Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ')');
- assertQuery("SELECT tiny FROM tbl").returns(Byte.MIN_VALUE).check();
- assertQuery("SELECT small FROM tbl").returns(Short.MIN_VALUE).check();
- assertQuery("SELECT i FROM tbl").returns(Integer.MIN_VALUE).check();
- assertQuery("SELECT big FROM tbl").returns(Long.MIN_VALUE).check();
- } finally {
- sql("DROP TABLE IF EXISTS tbl");
- }
+ assertQuery("SELECT tiny FROM tbl").returns(Byte.MIN_VALUE).check();
+ assertQuery("SELECT small FROM tbl").returns(Short.MIN_VALUE).check();
+ assertQuery("SELECT i FROM tbl").returns(Integer.MIN_VALUE).check();
+ assertQuery("SELECT big FROM tbl").returns(Long.MIN_VALUE).check();
}
/**
@@ -135,31 +146,27 @@ public class ItDataTypesTest extends AbstractBasicIntegrationTest {
*/
@Test
public void testNumericConvertingOnEquals() {
- try {
- sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");
+ sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");
- sql("INSERT INTO tbl VALUES (-1, 1, 2, 3, 4), (0, 5, 5, 5, 5)");
+ sql("INSERT INTO tbl VALUES (-1, 1, 2, 3, 4), (0, 5, 5, 5, 5)");
- assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.small)").returns((byte) 5).check();
- assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.tiny)").returns((short) 5).check();
+ assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.small)").returns((byte) 5).check();
+ assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.tiny)").returns((short) 5).check();
- assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.i)").returns((byte) 5).check();
- assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.tiny)").returns(5).check();
+ assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.i)").returns((byte) 5).check();
+ assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.tiny)").returns(5).check();
- assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.big)").returns((byte) 5).check();
- assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.tiny)").returns(5L).check();
+ assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.big)").returns((byte) 5).check();
+ assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.tiny)").returns(5L).check();
- assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.i)").returns((short) 5).check();
- assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.small)").returns(5).check();
+ assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.i)").returns((short) 5).check();
+ assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.small)").returns(5).check();
- assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.big)").returns((short) 5).check();
- assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.small)").returns(5L).check();
+ assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.big)").returns((short) 5).check();
+ assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.small)").returns(5L).check();
- assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.big)").returns(5).check();
- assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.i)").returns(5L).check();
- } finally {
- sql("DROP TABLE if exists tbl");
- }
+ assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.big)").returns(5).check();
+ assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.i)").returns(5L).check();
}
/**
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
index 7ed8c9e49..95efbd8c5 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
@@ -310,13 +310,7 @@ public class ItFunctionsTest extends AbstractBasicIntegrationTest {
/**
* A clock reporting a local time.
*/
- Clock<LocalTime> TIME_CLOCK = () -> {
- var tmp = LocalTime.now();
-
- // need to erase millis because that is what we do when converting from internal types
- // see: org.apache.ignite.internal.sql.engine.util.TypeUtils.fromInternal
- return LocalTime.of(tmp.getHour(), tmp.getMinute(), tmp.getSecond());
- };
+ Clock<LocalTime> TIME_CLOCK = LocalTime::now;
/**
* A clock reporting a local date.
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
index 9f3dba325..769c9853e 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.sql.engine;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.generate;
+import java.time.Duration;
+import java.time.Period;
import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
import org.apache.ignite.schema.SchemaBuilders;
import org.apache.ignite.schema.definition.ColumnType;
@@ -81,9 +83,7 @@ public class ItMetadataTest extends AbstractBasicIntegrationTest {
assertQuery("select id, id::tinyint as tid, id::smallint as sid, id::varchar as vid, id::interval hour, "
+ "id::interval year from person")
.columnNames("ID", "TID", "SID", "VID", "ID :: INTERVAL INTERVAL_HOUR", "ID :: INTERVAL INTERVAL_YEAR")
- // TODO: IGNITE-16635 replace byte arrays for correct types.
- //.columnTypes(Integer.class, Byte.class, Short.class, String.class, Duration.class, Period.class)
- .columnTypes(Integer.class, Byte.class, Short.class, String.class, byte[].class, byte[].class)
+ .columnTypes(Integer.class, Byte.class, Short.class, String.class, Duration.class, Period.class)
.check();
}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
index 5027cf735..43d99888a 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
@@ -368,12 +368,12 @@ public abstract class QueryChecker {
}
if (expectedColumnTypes != null) {
- List<Type> colNames = cur.metadata().columns().stream()
+ List<Type> colTypes = cur.metadata().columns().stream()
.map(ColumnMetadata::type)
.map(Commons::columnTypeToClass)
.collect(Collectors.toList());
- assertThat("Column types don't match", colNames, equalTo(expectedColumnTypes));
+ assertThat("Column types don't match", colTypes, equalTo(expectedColumnTypes));
}
var res = CursorUtils.getAllFromCursor(cur);
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
index 66180043b..d65eea7cc 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
@@ -315,6 +315,54 @@ public enum NativeTypeSpec {
return null;
}
+ /**
+ * Maps native type spec to a particular java class.
+ *
+ * @param spec Type spec to map.
+ * @param nullable Whether class should accept null values.
+ * @return Java class.
+ */
+ public static Class<?> toClass(NativeTypeSpec spec, boolean nullable) {
+ assert spec != null;
+
+ switch (spec) {
+ case INT8:
+ return nullable ? Byte.class : byte.class;
+ case INT16:
+ return nullable ? Short.class : short.class;
+ case INT32:
+ return nullable ? Integer.class : int.class;
+ case INT64:
+ return nullable ? Long.class : long.class;
+ case FLOAT:
+ return nullable ? Float.class : float.class;
+ case DOUBLE:
+ return nullable ? Double.class : double.class;
+ case BITMASK:
+ return BitSet.class;
+ case BYTES:
+ return byte[].class;
+ case STRING:
+ return String.class;
+ case DATE:
+ return LocalDate.class;
+ case TIME:
+ return LocalTime.class;
+ case TIMESTAMP:
+ return Instant.class;
+ case DATETIME:
+ return LocalDateTime.class;
+ case UUID:
+ return java.util.UUID.class;
+ case NUMBER:
+ return BigInteger.class;
+ case DECIMAL:
+ return BigDecimal.class;
+ default:
+ throw new IllegalStateException("Unknown typeSpec " + spec);
+ }
+ }
+
/**
* Maps object to native type.
*
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
index 150028a0d..dc2fec326 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
@@ -578,7 +578,7 @@ public class ExecutionServiceImpl<RowT> implements ExecutionService, TopologyEve
start
.thenCompose(none -> {
- if (!root.completeExceptionally(new ExecutionCancelledException())) {
+ if (!root.completeExceptionally(new ExecutionCancelledException()) && !root.isCompletedExceptionally()) {
if (cancel) {
return root.thenAccept(root -> root.onError(new ExecutionCancelledException()));
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
index 2d75feadb..b86f95707 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.sql.engine.exec.exp;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
+import java.time.LocalTime;
import org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.config.CalciteConnectionConfig;
@@ -35,8 +36,10 @@ import org.apache.calcite.schema.Statistic;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@@ -158,6 +161,10 @@ public class IgniteSqlFunctions {
return s == null ? null : new ByteString(s.getBytes(Commons.typeFactory().getDefaultCharset()));
}
+ public static int currentTime(DataContext ctx) {
+ return (int) TypeUtils.toInternal((ExecutionContext<?>) ctx, LocalTime.now(), LocalTime.class);
+ }
+
/**
* Dummy table to implement the SYSTEM_RANGE function.
*/
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
index 3c5ee0702..1623ead2a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
@@ -1712,7 +1712,7 @@ public class RexImpTable {
} else if (op == CURRENT_TIMESTAMP) {
return Expressions.call(BuiltInMethod.CURRENT_TIMESTAMP.method, root);
} else if (op == CURRENT_TIME) {
- return Expressions.call(BuiltInMethod.CURRENT_TIME.method, root);
+ return Expressions.call(IgniteMethod.CURRENT_TIME.method(), root);
} else if (op == CURRENT_DATE) {
return Expressions.call(BuiltInMethod.CURRENT_DATE.method, root);
} else if (op == LOCALTIMESTAMP) {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
index f55f0fbc8..20d4942f3 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
@@ -226,7 +226,7 @@ public class PrepareServiceImpl implements PrepareService, SchemaUpdateListener
QueryTemplate template = new QueryTemplate(fragments);
- return new MultiStepQueryPlan(template, resultSetMetadata(ctx, validated.dataType(), validated.origins()));
+ return new MultiStepQueryPlan(template, resultSetMetadata(validated.dataType(), validated.origins()));
}, planningPool));
return planFut.thenApply(QueryPlan::copy);
@@ -255,12 +255,12 @@ public class PrepareServiceImpl implements PrepareService, SchemaUpdateListener
return planFut.thenApply(QueryPlan::copy);
}
- private ResultSetMetadata resultSetMetadata(PlanningContext ctx, RelDataType sqlType,
- @Nullable List<List<String>> origins) {
+ private ResultSetMetadata resultSetMetadata(
+ RelDataType rowType,
+ @Nullable List<List<String>> origins
+ ) {
return new LazyResultSetMetadata(
() -> {
- RelDataType rowType = TypeUtils.getResultType(ctx.typeFactory(), ctx.catalogReader(), sqlType, origins);
-
List<ColumnMetadata> fieldsMeta = new ArrayList<>(rowType.getFieldCount());
for (int i = 0; i < rowType.getFieldCount(); ++i) {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java
index 57293c8d5..e2236a9f2 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java
@@ -17,28 +17,34 @@
package org.apache.ignite.internal.sql.engine.schema;
-import org.apache.calcite.rel.type.RelDataType;
import org.apache.ignite.internal.schema.NativeType;
-import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.jetbrains.annotations.Nullable;
/**
- * ColumnDescriptor interface.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+ * An object describing a particular column in a table.
*/
public interface ColumnDescriptor {
+ /** Returns {@code true} if this column accepts a null value. */
+ boolean nullable();
+
+ /** Returns {@code true} if this column is part of the primary key. */
boolean key();
- boolean hasDefaultValue();
+ /** Returns the strategy to follow when generating value for column not specified in the INSERT statement. */
+ DefaultValueStrategy defaultStrategy();
+ /** Returns the name of the column. */
String name();
+ /** Returns 0-based index of the column according to a schema defined by a user. */
int logicalIndex();
+ /** Returns 0-based index of the column according to an actual row layout defined by a storage. */
int physicalIndex();
- RelDataType logicalType(IgniteTypeFactory f);
-
+ /** Returns the type of this column in a storage. */
NativeType physicalType();
- Object defaultValue();
+ /** Returns the value to use for column not specified in the INSERT statement. */
+ @Nullable Object defaultValue();
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptorImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptorImpl.java
index baabb4ae3..346d3d4cc 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptorImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptorImpl.java
@@ -17,23 +17,25 @@
package org.apache.ignite.internal.sql.engine.schema;
+import java.util.Objects;
import java.util.function.Supplier;
-import org.apache.calcite.rel.type.RelDataType;
import org.apache.ignite.internal.schema.NativeType;
-import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
-import org.apache.ignite.internal.sql.engine.util.Commons;
-import org.jetbrains.annotations.Nullable;
/**
- * ColumnDescriptorImpl.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+ * Simple implementation of {@link ColumnDescriptor}.
*/
public class ColumnDescriptorImpl implements ColumnDescriptor {
+ private static final Supplier<Object> NULL_SUPPLIER = () -> null;
+
+ private final boolean nullable;
+
private final boolean key;
private final String name;
- private final @Nullable Supplier<Object> dfltVal;
+ private final Supplier<Object> dfltVal;
+
+ private final DefaultValueStrategy defaultStrategy;
private final int logicalIndex;
@@ -43,22 +45,45 @@ public class ColumnDescriptorImpl implements ColumnDescriptor {
/**
* Constructor.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+ *
+ * @param name The name of the column.
+ * @param key If {@code true}, this column will be considered as a part of PK.
+ * @param nullable If {@code true}, this column will be considered as a nullable.
+ * @param logicalIndex A 0-based index in a schema defined by a user.
+ * @param physicalIndex A 0-based index in a schema defined by a storage.
+ * @param type Type of the value in the underlying storage.
+ * @param defaultStrategy A strategy to follow when generating value for column not specified in the INSERT statement.
+ * @param dfltVal A value generator to use when generating value for column not specified in the INSERT statement.
+ * If {@link #defaultStrategy} is {@link DefaultValueStrategy#DEFAULT_NULL DEFAULT_NULL} then the passed supplier will
+ * be ignored, thus may be {@code null}. In other cases value supplier MUST be specified.
*/
public ColumnDescriptorImpl(
String name,
boolean key,
+ boolean nullable,
int logicalIndex,
int physicalIndex,
- NativeType storageType,
- @Nullable Supplier<Object> dfltVal
+ NativeType type,
+ DefaultValueStrategy defaultStrategy,
+ Supplier<Object> dfltVal
) {
this.key = key;
+ this.nullable = nullable;
this.name = name;
- this.dfltVal = dfltVal;
+ this.defaultStrategy = defaultStrategy;
this.logicalIndex = logicalIndex;
this.physicalIndex = physicalIndex;
- this.storageType = storageType;
+ this.storageType = type;
+
+ this.dfltVal = defaultStrategy != DefaultValueStrategy.DEFAULT_NULL
+ ? Objects.requireNonNull(dfltVal, "dfltVal")
+ : NULL_SUPPLIER;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean nullable() {
+ return nullable;
}
/** {@inheritDoc} */
@@ -69,14 +94,14 @@ public class ColumnDescriptorImpl implements ColumnDescriptor {
/** {@inheritDoc} */
@Override
- public boolean hasDefaultValue() {
- return dfltVal != null;
+ public DefaultValueStrategy defaultStrategy() {
+ return defaultStrategy;
}
/** {@inheritDoc} */
@Override
public Object defaultValue() {
- return dfltVal != null ? dfltVal.get() : null;
+ return dfltVal.get();
}
/** {@inheritDoc} */
@@ -97,12 +122,6 @@ public class ColumnDescriptorImpl implements ColumnDescriptor {
return physicalIndex;
}
- /** {@inheritDoc} */
- @Override
- public RelDataType logicalType(IgniteTypeFactory f) {
- return f.createJavaType(Commons.nativeTypeToClass(storageType));
- }
-
/** {@inheritDoc} */
@Override
public NativeType physicalType() {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/DefaultValueStrategy.java
similarity index 61%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java
copy to modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/DefaultValueStrategy.java
index 57293c8d5..bbd8c054c 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/ColumnDescriptor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/DefaultValueStrategy.java
@@ -17,28 +17,20 @@
package org.apache.ignite.internal.sql.engine.schema;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.ignite.internal.schema.NativeType;
-import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
-
/**
- * ColumnDescriptor interface.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+ * The strategy of default value computation for a particular column.
*/
-public interface ColumnDescriptor {
- boolean key();
-
- boolean hasDefaultValue();
-
- String name();
-
- int logicalIndex();
-
- int physicalIndex();
-
- RelDataType logicalType(IgniteTypeFactory f);
-
- NativeType physicalType();
-
- Object defaultValue();
+public enum DefaultValueStrategy {
+ /** Default value is not specified, thus {@code null} will be used instead. */
+ DEFAULT_NULL,
+
+ /**
+ * Default value is specified as a constant, thus may be inlined into the final plan.
+ *
+ * <p>Note: the value may still be {@code null} if someone specify this explicitly.
+ */
+ DEFAULT_CONSTANT,
+
+ /** Default value is specified as an expression and will be evaluated at a runtime. */
+ DEFAULT_COMPUTED
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteTableImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteTableImpl.java
index 1cbe84ec1..37d09d5c2 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteTableImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteTableImpl.java
@@ -43,6 +43,7 @@ import org.apache.calcite.schema.Statistic;
import org.apache.calcite.schema.impl.AbstractTable;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.schema.BinaryRow;
+import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
import org.apache.ignite.internal.schema.row.Row;
@@ -58,6 +59,7 @@ import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
import org.apache.ignite.internal.sql.engine.trait.RewindabilityTrait;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.apache.ignite.internal.storage.PartitionStorage;
import org.apache.ignite.internal.table.InternalTable;
import org.jetbrains.annotations.Nullable;
@@ -318,6 +320,8 @@ public class IgniteTableImpl extends AbstractTable implements InternalIgniteTabl
val = hnd.get(colDesc.logicalIndex(), row);
}
+ val = TypeUtils.fromInternal(ectx, val, NativeTypeSpec.toClass(colDesc.physicalType().spec(), colDesc.nullable()));
+
RowAssembler.writeValue(rowAssembler, colDesc.physicalType(), val);
}
@@ -379,7 +383,8 @@ public class IgniteTableImpl extends AbstractTable implements InternalIgniteTabl
for (ColumnDescriptor colDesc : columnsOrderedByPhysSchema) {
int colIdx = columnToIndex.getOrDefault(colDesc.name(), colDesc.logicalIndex() + offset);
- Object val = hnd.get(colIdx, row);
+ Object val = TypeUtils.fromInternal(ectx, hnd.get(colIdx, row),
+ NativeTypeSpec.toClass(colDesc.physicalType().spec(), colDesc.nullable()));
RowAssembler.writeValue(rowAssembler, colDesc.physicalType(), val);
}
@@ -434,7 +439,10 @@ public class IgniteTableImpl extends AbstractTable implements InternalIgniteTabl
break;
}
- RowAssembler.writeValue(rowAssembler, colDesc.physicalType(), hnd.get(colDesc.logicalIndex(), row));
+ Object val = TypeUtils.fromInternal(ectx, hnd.get(colDesc.logicalIndex(), row),
+ NativeTypeSpec.toClass(colDesc.physicalType().spec(), colDesc.nullable()));
+
+ RowAssembler.writeValue(rowAssembler, colDesc.physicalType(), val);
}
return new ModifyRow(new Row(schemaDescriptor, rowAssembler.build()), Operation.DELETE_ROW);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
index be2ed7b89..b61aac62b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
@@ -317,9 +317,11 @@ public class SqlSchemaManagerImpl implements SqlSchemaManager {
.map(col -> new ColumnDescriptorImpl(
col.name(),
descriptor.isKeyColumn(col.schemaIndex()),
+ col.nullable(),
col.columnOrder(),
col.schemaIndex(),
col.type(),
+ DefaultValueStrategy.DEFAULT_CONSTANT,
col::defaultValue
))
.collect(Collectors.toList());
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
index ada088a9a..d0d0befb4 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.sql.engine.schema;
+import static org.apache.ignite.internal.sql.engine.util.TypeUtils.native2relationalType;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -35,7 +37,6 @@ import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
-import org.apache.ignite.internal.sql.engine.util.TypeUtils;
/**
* TableDescriptorImpl.
@@ -93,27 +94,20 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory implem
keyFields = keyFieldsBuilder.build();
}
+ @SuppressWarnings("AssertWithSideEffects")
private ColumnDescriptor injectDefault(ColumnDescriptor desc) {
assert Commons.implicitPkEnabled() && Commons.IMPLICIT_PK_COL_NAME.equals(desc.name()) : desc;
return new ColumnDescriptorImpl(
desc.name(),
desc.key(),
+ desc.nullable(),
desc.logicalIndex(),
desc.physicalIndex(),
desc.physicalType(),
- null
- ) {
- @Override
- public boolean hasDefaultValue() {
- return false;
- }
-
- @Override
- public Object defaultValue() {
- return UUID.randomUUID().toString();
- }
- };
+ DefaultValueStrategy.DEFAULT_COMPUTED,
+ () -> UUID.randomUUID().toString()
+ );
}
/** {@inheritDoc} */
@@ -143,7 +137,7 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory implem
/** {@inheritDoc} */
@Override
public ColumnStrategy generationStrategy(RelOptTable tbl, int colIdx) {
- if (descriptors[colIdx].hasDefaultValue()) {
+ if (descriptors[colIdx].defaultStrategy() != DefaultValueStrategy.DEFAULT_NULL) {
return ColumnStrategy.DEFAULT;
}
@@ -155,14 +149,14 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory implem
public RexNode newColumnDefaultValue(RelOptTable tbl, int colIdx, InitializerContext ctx) {
final ColumnDescriptor desc = descriptors[colIdx];
- if (!desc.hasDefaultValue()) {
+ if (desc.defaultStrategy() != DefaultValueStrategy.DEFAULT_CONSTANT) {
return super.newColumnDefaultValue(tbl, colIdx, ctx);
}
final RexBuilder rexBuilder = ctx.getRexBuilder();
final IgniteTypeFactory typeFactory = (IgniteTypeFactory) rexBuilder.getTypeFactory();
- return rexBuilder.makeLiteral(desc.defaultValue(), desc.logicalType(typeFactory), false);
+ return rexBuilder.makeLiteral(desc.defaultValue(), deriveLogicalType(typeFactory, desc), false);
}
/** {@inheritDoc} */
@@ -172,15 +166,15 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory implem
if (usedColumns == null) {
for (int i = 0; i < descriptors.length; i++) {
- b.add(descriptors[i].name(), descriptors[i].logicalType(factory));
+ b.add(descriptors[i].name(), deriveLogicalType(factory, descriptors[i]));
}
} else {
for (int i = usedColumns.nextSetBit(0); i != -1; i = usedColumns.nextSetBit(i + 1)) {
- b.add(descriptors[i].name(), descriptors[i].logicalType(factory));
+ b.add(descriptors[i].name(), deriveLogicalType(factory, descriptors[i]));
}
}
- return TypeUtils.sqlType(factory, b.build());
+ return b.build();
}
/** {@inheritDoc} */
@@ -200,4 +194,8 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory implem
public int columnsCount() {
return descriptors.length;
}
+
+ private RelDataType deriveLogicalType(RelDataTypeFactory factory, ColumnDescriptor desc) {
+ return native2relationalType(factory, desc.physicalType(), desc.nullable());
+ }
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
index d9c0da349..893515d53 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
@@ -26,6 +26,7 @@ import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
+import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -206,12 +207,12 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl {
case DATE:
return LocalDate.class;
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
return LocalTime.class;
case TIMESTAMP:
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return LocalDateTime.class;
- case TIME_WITH_LOCAL_TIME_ZONE:
- return LocalTime.class;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return Instant.class;
case INTEGER:
return type.isNullable() ? Integer.class : int.class;
case INTERVAL_YEAR:
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 74a518255..a343ae705 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 org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.sql.SqlIntervalQualifier;
@@ -73,7 +74,10 @@ public enum IgniteMethod {
BYTESTRING_TO_STRING(IgniteSqlFunctions.class, "toString", ByteString.class),
/** See {@link IgniteSqlFunctions#toByteString(String)}. */
- STRING_TO_BYTESTRING(IgniteSqlFunctions.class, "toByteString", String.class);
+ STRING_TO_BYTESTRING(IgniteSqlFunctions.class, "toByteString", String.class),
+
+ /** See {@link IgniteSqlFunctions#currentTime(DataContext)}. */
+ CURRENT_TIME(IgniteSqlFunctions.class, "currentTime", DataContext.class);
private final Method method;
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 510c7847e..16b1804b4 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
@@ -17,12 +17,9 @@
package org.apache.ignite.internal.sql.engine.util;
-import static org.apache.calcite.util.Util.last;
import static org.apache.ignite.internal.sql.engine.util.Commons.transform;
-import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
import java.lang.reflect.Type;
-import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -30,6 +27,7 @@ import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneOffset;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -37,41 +35,48 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
-import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.plan.RelOptSchema;
-import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
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.calcite.util.Pair;
+import org.apache.ignite.internal.schema.DecimalNativeType;
+import org.apache.ignite.internal.schema.NativeType;
+import org.apache.ignite.internal.schema.NumberNativeType;
+import org.apache.ignite.internal.schema.VarlenNativeType;
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.schema.ColumnDescriptor;
-import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.sql.SqlColumnType;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* TypeUtils.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
*/
public class TypeUtils {
- private static final Set<Type> CONVERTABLE_TYPES = Set.of(
- LocalDate.class,
- LocalDateTime.class,
- LocalTime.class,
- Timestamp.class,
- Duration.class,
- Period.class
+ private static final Set<SqlTypeName> CONVERTABLE_TYPES = EnumSet.of(
+ SqlTypeName.DATE,
+ SqlTypeName.TIME,
+ SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
+ SqlTypeName.TIMESTAMP,
+ SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+ SqlTypeName.INTERVAL_SECOND,
+ SqlTypeName.INTERVAL_MINUTE,
+ SqlTypeName.INTERVAL_MINUTE_SECOND,
+ SqlTypeName.INTERVAL_HOUR,
+ SqlTypeName.INTERVAL_HOUR_MINUTE,
+ SqlTypeName.INTERVAL_HOUR_SECOND,
+ SqlTypeName.INTERVAL_DAY,
+ SqlTypeName.INTERVAL_DAY_HOUR,
+ SqlTypeName.INTERVAL_DAY_MINUTE,
+ SqlTypeName.INTERVAL_DAY_SECOND,
+ SqlTypeName.INTERVAL_MONTH,
+ SqlTypeName.INTERVAL_YEAR,
+ SqlTypeName.INTERVAL_YEAR_MONTH
);
/**
@@ -174,71 +179,6 @@ public class TypeUtils {
return typeFactory.createStructType(fields, names);
}
- /**
- * SqlType.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- */
- public static RelDataType sqlType(IgniteTypeFactory typeFactory, RelDataType rowType) {
- if (!rowType.isStruct()) {
- return typeFactory.toSql(rowType);
- }
-
- return typeFactory.createStructType(
- transform(rowType.getFieldList(),
- f -> Pair.of(f.getName(), sqlType(typeFactory, f.getType()))));
- }
-
- /**
- * GetResultType.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- *
- * @param schema Schema.
- * @param sqlType Logical row type.
- * @param origins Columns origins.
- * @return Result type.
- */
- public static RelDataType getResultType(IgniteTypeFactory typeFactory, RelOptSchema schema, RelDataType sqlType,
- @Nullable List<List<String>> origins) {
- assert origins == null || origins.size() == sqlType.getFieldCount();
-
- RelDataTypeFactory.Builder b = new RelDataTypeFactory.Builder(typeFactory);
- List<RelDataTypeField> fields = sqlType.getFieldList();
-
- for (int i = 0; i < sqlType.getFieldCount(); i++) {
- List<String> origin = origins == null ? null : origins.get(i);
- b.add(fields.get(i).getName(), typeFactory.createType(
- getResultClass(typeFactory, schema, fields.get(i).getType(), origin)));
- }
-
- return b.build();
- }
-
- /**
- * GetResultClass.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- *
- * @param schema Schema.
- * @param type Logical column type.
- * @param origin Column origin.
- * @return Result type.
- */
- private static Type getResultClass(IgniteTypeFactory typeFactory, RelOptSchema schema, RelDataType type,
- @Nullable List<String> origin) {
- if (nullOrEmpty(origin)) {
- return typeFactory.getResultClass(type);
- }
-
- RelOptTable table = schema.getTableForMember(origin.subList(0, origin.size() - 1));
-
- assert table != null;
-
- ColumnDescriptor fldDesc = table.unwrap(TableDescriptor.class).columnDescriptor(last(origin));
-
- assert fldDesc != null;
-
- return Commons.nativeTypeToClass(fldDesc.physicalType());
- }
-
/**
* Function.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
@@ -246,8 +186,6 @@ public class TypeUtils {
public static <RowT> Function<RowT, RowT> resultTypeConverter(ExecutionContext<RowT> ectx, RelDataType resultType) {
assert resultType.isStruct();
- resultType = TypeUtils.getResultType(Commons.typeFactory(), ectx.unwrap(BaseQueryContext.class).catalogReader(), resultType, null);
-
if (hasConvertableFields(resultType)) {
RowHandler<RowT> handler = ectx.rowHandler();
List<RelDataType> types = RelOptUtil.getFieldTypeList(resultType);
@@ -268,30 +206,21 @@ public class TypeUtils {
}
private static Function<Object, Object> fieldConverter(ExecutionContext<?> ectx, RelDataType fieldType) {
- Type storageType = ectx.getTypeFactory().getJavaClass(fieldType);
+ Type storageType = ectx.getTypeFactory().getResultClass(fieldType);
- if (isConvertableType(storageType)) {
+ if (isConvertableType(fieldType)) {
return v -> fromInternal(ectx, v, storageType);
}
return Function.identity();
}
- /**
- * IsConvertableType.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- */
- public static boolean isConvertableType(Type type) {
- return CONVERTABLE_TYPES.contains(type);
- }
-
/**
* IsConvertableType.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
*/
public static boolean isConvertableType(RelDataType type) {
- return type instanceof RelDataTypeFactoryImpl.JavaType
- && isConvertableType(((RelDataTypeFactoryImpl.JavaType) type).getJavaClass());
+ return CONVERTABLE_TYPES.contains(type.getSqlTypeName());
}
private static boolean hasConvertableFields(RelDataType resultType) {
@@ -314,21 +243,19 @@ public class TypeUtils {
public static Object toInternal(ExecutionContext<?> ectx, Object val, Type storageType) {
if (val == null) {
return null;
- } else if (storageType == java.sql.Date.class) {
- return (int) (SqlFunctions.toLong((java.util.Date) val, DataContext.Variable.TIME_ZONE.get(ectx))
- / DateTimeUtils.MILLIS_PER_DAY);
- } else if (storageType == java.sql.Time.class) {
- return (int) (SqlFunctions.toLong((java.util.Date) val, DataContext.Variable.TIME_ZONE.get(ectx))
- % DateTimeUtils.MILLIS_PER_DAY);
- } else if (storageType == Timestamp.class) {
- return SqlFunctions.toLong((java.util.Date) val, DataContext.Variable.TIME_ZONE.get(ectx));
- } else if (storageType == java.util.Date.class) {
- return SqlFunctions.toLong((java.util.Date) val, DataContext.Variable.TIME_ZONE.get(ectx));
+ } else if (storageType == LocalDate.class) {
+ return (int) ((LocalDate) val).toEpochDay();
+ } else if (storageType == LocalTime.class) {
+ return (int) (((LocalTime) val).toNanoOfDay() / 1000 / 1000 /* convert to millis */);
+ } else if (storageType == LocalDateTime.class) {
+ return ((LocalDateTime) val).toEpochSecond(ZoneOffset.UTC);
} else if (storageType == Duration.class) {
return TimeUnit.SECONDS.toMillis(((Duration) val).getSeconds())
+ TimeUnit.NANOSECONDS.toMillis(((Duration) val).getNano());
} else if (storageType == Period.class) {
return (int) ((Period) val).toTotalMonths();
+ } else if (storageType == byte[].class) {
+ return new ByteString((byte[]) val);
} else {
return val;
}
@@ -344,7 +271,7 @@ public class TypeUtils {
} else if (storageType == LocalDate.class && val instanceof Integer) {
return LocalDate.ofEpochDay((Integer) val);
} else if (storageType == LocalTime.class && val instanceof Integer) {
- return LocalTime.ofSecondOfDay((Integer) val / 1000);
+ return LocalTime.ofNanoOfDay(Long.valueOf((Integer) val) * 1000 * 1000 /* convert from millis */);
} else if (storageType == LocalDateTime.class && (val instanceof Long)) {
return LocalDateTime.ofEpochSecond((Long) val / 1000, (int) ((Long) val % 1000) * 1000 * 1000, ZoneOffset.UTC);
} else if (storageType == Duration.class && val instanceof Long) {
@@ -374,6 +301,7 @@ public class TypeUtils {
case INTEGER:
return SqlColumnType.INT32;
case TIMESTAMP:
+ return SqlColumnType.DATETIME;
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return SqlColumnType.TIMESTAMP;
case BIGINT:
@@ -416,4 +344,80 @@ public class TypeUtils {
return null;
}
}
+
+ /**
+ * Converts a {@link NativeType native type} to {@link RelDataType relational type} with respect to the nullability flag.
+ *
+ * @param factory Type factory.
+ * @param nativeType A native type to convert.
+ * @param nullable A flag that specify whether the resulting type should be nullable or not.
+ * @return Relational type.
+ */
+ public static RelDataType native2relationalType(RelDataTypeFactory factory, NativeType nativeType, boolean nullable) {
+ return factory.createTypeWithNullability(native2relationalType(factory, nativeType), nullable);
+ }
+
+ /**
+ * Converts a {@link NativeType native type} to {@link RelDataType relational type}.
+ *
+ * @param factory Type factory.
+ * @param nativeType A native type to convert.
+ * @return Relational type.
+ */
+ public static RelDataType native2relationalType(RelDataTypeFactory factory, NativeType nativeType) {
+ switch (nativeType.spec()) {
+ case INT8:
+ return factory.createSqlType(SqlTypeName.TINYINT);
+ case INT16:
+ return factory.createSqlType(SqlTypeName.SMALLINT);
+ case INT32:
+ return factory.createSqlType(SqlTypeName.INTEGER);
+ case INT64:
+ return factory.createSqlType(SqlTypeName.BIGINT);
+ case FLOAT:
+ return factory.createSqlType(SqlTypeName.REAL);
+ case DOUBLE:
+ return factory.createSqlType(SqlTypeName.DOUBLE);
+ case DECIMAL:
+ assert nativeType instanceof DecimalNativeType;
+
+ var decimal = (DecimalNativeType) nativeType;
+
+ return factory.createSqlType(SqlTypeName.DECIMAL, decimal.precision(), decimal.scale());
+ case UUID:
+ throw new AssertionError("UUID is not supported yet");
+ case STRING: {
+ assert nativeType instanceof VarlenNativeType;
+
+ var varlen = (VarlenNativeType) nativeType;
+
+ return factory.createSqlType(SqlTypeName.VARCHAR, varlen.length());
+ }
+ case BYTES: {
+ assert nativeType instanceof VarlenNativeType;
+
+ var varlen = (VarlenNativeType) nativeType;
+
+ return factory.createSqlType(SqlTypeName.BINARY, varlen.length());
+ }
+ case BITMASK:
+ throw new AssertionError("BITMASK is not supported yet");
+ case NUMBER:
+ assert nativeType instanceof NumberNativeType;
+
+ var number = (NumberNativeType) nativeType;
+
+ return factory.createSqlType(SqlTypeName.DECIMAL, number.precision(), 0);
+ case DATE:
+ return factory.createSqlType(SqlTypeName.DATE);
+ case TIME:
+ return factory.createSqlType(SqlTypeName.TIME);
+ case TIMESTAMP:
+ return factory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ case DATETIME:
+ return factory.createSqlType(SqlTypeName.TIMESTAMP);
+ default:
+ throw new IllegalStateException("Unexpected native type " + nativeType);
+ }
+ }
}
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 245406ae6..4397fe3f3 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
@@ -92,6 +92,7 @@ import org.apache.ignite.internal.sql.engine.rel.IgniteTableScan;
import org.apache.ignite.internal.sql.engine.rel.logical.IgniteLogicalIndexScan;
import org.apache.ignite.internal.sql.engine.rel.logical.IgniteLogicalTableScan;
import org.apache.ignite.internal.sql.engine.schema.ColumnDescriptor;
+import org.apache.ignite.internal.sql.engine.schema.DefaultValueStrategy;
import org.apache.ignite.internal.sql.engine.schema.IgniteIndex;
import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
@@ -1024,6 +1025,12 @@ public abstract class AbstractPlannerTest extends IgniteAbstractTest {
this.name = name;
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean nullable() {
+ return true;
+ }
+
/** {@inheritDoc} */
@Override
public boolean key() {
@@ -1032,8 +1039,8 @@ public abstract class AbstractPlannerTest extends IgniteAbstractTest {
/** {@inheritDoc} */
@Override
- public boolean hasDefaultValue() {
- return false;
+ public DefaultValueStrategy defaultStrategy() {
+ return DefaultValueStrategy.DEFAULT_NULL;
}
/** {@inheritDoc} */
@@ -1054,12 +1061,6 @@ public abstract class AbstractPlannerTest extends IgniteAbstractTest {
return idx;
}
- /** {@inheritDoc} */
- @Override
- public RelDataType logicalType(IgniteTypeFactory f) {
- throw new AssertionError();
- }
-
/** {@inheritDoc} */
@Override
public NativeType physicalType() {