This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch sql-calcite
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/sql-calcite by this push:
new e8a25be IGNITE-16018 DEFAULT operator support - Fixes #9633.
e8a25be is described below
commit e8a25bec813bd36426eb3d3fe3d040a76dc13f67
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Mon Dec 20 14:41:34 2021 +0300
IGNITE-16018 DEFAULT operator support - Fixes #9633.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
.../query/calcite/exec/ExecutionContext.java | 44 ++----
.../query/calcite/exec/exp/RexImpTable.java | 5 +-
.../query/calcite/prepare/BaseDataContext.java | 90 +++++++++++
.../prepare/ddl/DdlSqlToCommandConverter.java | 15 +-
.../query/calcite/schema/TableDescriptorImpl.java | 33 +++-
.../calcite/sql/fun/IgniteStdSqlOperatorTable.java | 1 +
.../processors/query/calcite/util/TypeUtils.java | 82 ++++++++--
.../integration/TableDmlIntegrationTest.java | 88 +++++++++++
.../query/calcite/jdbc/JdbcCrossEngineTest.java | 169 +++++++++++++++++++++
.../ignite/testsuites/IntegrationTestSuite.java | 2 +
10 files changed, 470 insertions(+), 59 deletions(-)
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
index 2a94b21..73a0472 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
@@ -18,19 +18,15 @@
package org.apache.ignite.internal.processors.query.calcite.exec;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
-
import org.apache.calcite.DataContext;
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.schema.SchemaPlus;
-import org.apache.calcite.tools.Frameworks;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
@@ -40,11 +36,11 @@ import
org.apache.ignite.internal.processors.query.calcite.exec.exp.ExpressionFa
import
org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
import
org.apache.ignite.internal.processors.query.calcite.metadata.FragmentDescription;
import
org.apache.ignite.internal.processors.query.calcite.prepare.AbstractQueryContext;
+import
org.apache.ignite.internal.processors.query.calcite.prepare.BaseDataContext;
import
org.apache.ignite.internal.processors.query.calcite.prepare.BaseQueryContext;
import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
-import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.NotNull;
import static
org.apache.ignite.internal.processors.query.calcite.util.Commons.checkRange;
@@ -54,16 +50,6 @@ import static
org.apache.ignite.internal.processors.query.calcite.util.Commons.c
*/
public class ExecutionContext<Row> extends AbstractQueryContext implements
DataContext {
/** */
- private final TimeZone timeZone = TimeZone.getDefault(); // TODO
DistributedSqlConfiguration#timeZone
-
- /** */
- private static final SchemaPlus DFLT_SCHEMA =
Frameworks.createRootSchema(false);
-
- /** */
- // TODO https://issues.apache.org/jira/browse/IGNITE-15276 Support other
locales.
- private static final Locale LOCALE = Locale.ENGLISH;
-
- /** */
private final UUID qryId;
/** */
@@ -93,11 +79,8 @@ public class ExecutionContext<Row> extends
AbstractQueryContext implements DataC
/** */
private final AtomicBoolean cancelFlag = new AtomicBoolean();
- /**
- * Need to store timestamp, since SQL standard says that functions such as
CURRENT_TIMESTAMP return the same value
- * throughout the query.
- */
- private final long startTs;
+ /** */
+ private final BaseDataContext baseDataContext;
/** */
private Object[] correlations = new Object[16];
@@ -131,14 +114,13 @@ public class ExecutionContext<Row> extends
AbstractQueryContext implements DataC
this.handler = handler;
this.params = params;
+ baseDataContext = new BaseDataContext(qctx.typeFactory());
+
expressionFactory = new ExpressionFactoryImpl<>(
this,
qctx.typeFactory(),
qctx.config().getParserConfig().conformance()
);
-
- long ts = U.currentTimeMillis();
- startTs = ts + timeZone.getOffset(ts);
}
/**
@@ -228,35 +210,27 @@ public class ExecutionContext<Row> extends
AbstractQueryContext implements DataC
/** {@inheritDoc} */
@Override public SchemaPlus getRootSchema() {
- return DFLT_SCHEMA;
+ return baseDataContext.getRootSchema();
}
/** {@inheritDoc} */
@Override public IgniteTypeFactory getTypeFactory() {
- return unwrap(BaseQueryContext.class).typeFactory();
+ return baseDataContext.getTypeFactory();
}
/** {@inheritDoc} */
@Override public QueryProvider getQueryProvider() {
- return null; // TODO
+ return baseDataContext.getQueryProvider();
}
/** {@inheritDoc} */
@Override public Object get(String name) {
if (Variable.CANCEL_FLAG.camelName.equals(name))
return cancelFlag;
- if (Variable.TIME_ZONE.camelName.equals(name))
- return timeZone; // TODO DistributedSqlConfiguration#timeZone
- if (Variable.CURRENT_TIMESTAMP.camelName.equals(name))
- return startTs;
- if (Variable.LOCAL_TIMESTAMP.camelName.equals(name))
- return startTs;
- if (Variable.LOCALE.camelName.equals(name))
- return LOCALE;
if (name.startsWith("?"))
return TypeUtils.toInternal(this, params.get(name));
- return params.get(name);
+ return baseDataContext.get(name);
}
/**
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
index 9b8a77d..6c8ae50 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
@@ -253,6 +253,9 @@ public class RexImpTable {
/** */
private final Map<SqlOperator, RexCallImplementor> map = new HashMap<>();
+ /** Placeholder for DEFAULT operator value. */
+ public static final Object DEFAULT_VALUE_PLACEHOLDER = new Object();
+
/** */
RexImpTable() {
defineMethod(ROW, BuiltInMethod.ARRAY.method, NullPolicy.NONE);
@@ -2500,7 +2503,7 @@ public class RexImpTable {
/** {@inheritDoc} */
@Override Expression implementSafe(final RexToLixTranslator translator,
final RexCall call, final List<Expression> argValueList) {
- return Expressions.constant(null);
+ return Expressions.field(null, RexImpTable.class,
"DEFAULT_VALUE_PLACEHOLDER");
}
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/BaseDataContext.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/BaseDataContext.java
new file mode 100644
index 0000000..720372d
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/BaseDataContext.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.prepare;
+
+import java.util.Locale;
+import java.util.TimeZone;
+import org.apache.calcite.DataContext;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.tools.Frameworks;
+import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Base data context.
+ */
+public final class BaseDataContext implements DataContext {
+ /** */
+ // TODO https://issues.apache.org/jira/browse/IGNITE-15276 Should be
configurable.
+ private final TimeZone timeZone = TimeZone.getDefault();
+
+ /** */
+ private static final SchemaPlus DFLT_SCHEMA =
Frameworks.createRootSchema(false);
+
+ /** */
+ // TODO https://issues.apache.org/jira/browse/IGNITE-15276 Support other
locales.
+ private static final Locale LOCALE = Locale.ENGLISH;
+
+ /**
+ * Need to store timestamp, since SQL standard says that functions such as
CURRENT_TIMESTAMP return the same value
+ * throughout the query.
+ */
+ // TODO https://issues.apache.org/jira/browse/IGNITE-15276 Should be
propagated from the initiator node.
+ private final long startTs;
+
+ /** */
+ private final IgniteTypeFactory typeFactory;
+
+ /** */
+ public BaseDataContext(IgniteTypeFactory typeFactory) {
+ this.typeFactory = typeFactory;
+
+ long ts = U.currentTimeMillis();
+ startTs = ts + timeZone.getOffset(ts);
+ }
+
+ /** {@inheritDoc} */
+ @Override public SchemaPlus getRootSchema() {
+ return DFLT_SCHEMA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteTypeFactory getTypeFactory() {
+ return typeFactory;
+ }
+
+ /** {@inheritDoc} */
+ @Override public QueryProvider getQueryProvider() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object get(String name) {
+ if (Variable.TIME_ZONE.camelName.equals(name))
+ return timeZone;
+ if (Variable.CURRENT_TIMESTAMP.camelName.equals(name))
+ return startTs;
+ if (Variable.LOCAL_TIMESTAMP.camelName.equals(name))
+ return startTs;
+ if (Variable.LOCALE.camelName.equals(name))
+ return LOCALE;
+
+ return null;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
index f728750..f0b029f 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.query.calcite.prepare.ddl;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -28,6 +29,7 @@ import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.calcite.DataContext;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.sql.SqlDdl;
@@ -45,6 +47,7 @@ import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryUtils;
+import
org.apache.ignite.internal.processors.query.calcite.prepare.BaseDataContext;
import
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePlanner;
import
org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
import
org.apache.ignite.internal.processors.query.calcite.prepare.ValidationResult;
@@ -53,6 +56,7 @@ import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlAlterTab
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTable;
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOption;
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum;
+import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.typedef.F;
import static org.apache.calcite.sql.type.SqlTypeName.BOOLEAN;
@@ -189,8 +193,15 @@ public class DdlSqlToCommandConverter {
RelDataType type = planner.convert(col.dataType);
Object dflt = null;
- if (col.expression != null)
- dflt = ((SqlLiteral)col.expression).getValue();
+ if (col.expression != null) {
+ assert col.expression instanceof SqlLiteral;
+
+ Type storageType = ctx.typeFactory().getResultClass(type);
+
+ DataContext dataCtx = new
BaseDataContext(ctx.typeFactory());
+
+ dflt = TypeUtils.fromLiteral(dataCtx, storageType,
(SqlLiteral)col.expression);
+ }
cols.add(new ColumnDefinition(name, type, dflt));
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
index 633cf8d..d0d5129 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
@@ -27,6 +27,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
+
+import org.apache.calcite.DataContext;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.type.RelDataType;
@@ -55,7 +57,9 @@ import
org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.QueryUtils;
import
org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.RowHandler;
+import
org.apache.ignite.internal.processors.query.calcite.exec.exp.RexImpTable;
import
org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
+import
org.apache.ignite.internal.processors.query.calcite.prepare.BaseDataContext;
import
org.apache.ignite.internal.processors.query.calcite.prepare.MappingQueryContext;
import
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
import
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
@@ -301,7 +305,11 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
final RexBuilder rexBuilder = ctx.getRexBuilder();
final IgniteTypeFactory typeFactory =
(IgniteTypeFactory)rexBuilder.getTypeFactory();
- return rexBuilder.makeLiteral(desc.defaultValue(),
desc.logicalType(typeFactory), false);
+ DataContext dataCtx = new BaseDataContext(typeFactory);
+
+ Object dfltVal = TypeUtils.toInternal(dataCtx, desc.defaultValue());
+
+ return rexBuilder.makeLiteral(dfltVal, desc.logicalType(typeFactory),
false);
}
/** {@inheritDoc} */
@@ -345,8 +353,11 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
Object key = handler.get(keyField, row);
- if (key != null)
+ if (key != null) {
+ key = replaceDefault(key, descriptors[QueryUtils.KEY_COL]);
+
return TypeUtils.fromInternal(ectx, key,
descriptors[QueryUtils.KEY_COL].storageType());
+ }
// skip _key and _val
for (int i = 2; i < descriptors.length; i++) {
@@ -355,7 +366,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
if (!desc.field() || !desc.key())
continue;
- Object fieldVal = handler.get(i, row);
+ Object fieldVal = replaceDefault(handler.get(i, row), desc);
if (fieldVal != null) {
if (key == null)
@@ -384,19 +395,27 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
for (int i = 2; i < descriptors.length; i++) {
final ColumnDescriptor desc = descriptors[i];
- Object fieldVal = handler.get(i, row);
+ Object fieldVal = replaceDefault(handler.get(i, row), desc);
if (desc.field() && !desc.key() && fieldVal != null)
desc.set(val, TypeUtils.fromInternal(ectx, fieldVal,
desc.storageType()));
}
}
- else
+ else {
+ val = replaceDefault(val, descriptors[QueryUtils.VAL_COL]);
+
val = TypeUtils.fromInternal(ectx, val,
descriptors[QueryUtils.VAL_COL].storageType());
+ }
return val;
}
/** */
+ private Object replaceDefault(Object val, ColumnDescriptor desc) {
+ return val == RexImpTable.DEFAULT_VALUE_PLACEHOLDER ?
desc.defaultValue() : val;
+ }
+
+ /** */
private Object newVal(String typeName, Class<?> typeCls) throws
IgniteCheckedException {
GridCacheContext<?, ?> cctx = cacheContext();
@@ -665,7 +684,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
/** {@inheritDoc} */
@Override public RelDataType logicalType(IgniteTypeFactory f) {
- return f.createJavaType(storageType);
+ return f.toSql(f.createJavaType(storageType));
}
/** {@inheritDoc} */
@@ -739,7 +758,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
/** {@inheritDoc} */
@Override public RelDataType logicalType(IgniteTypeFactory f) {
- return f.createJavaType(storageType);
+ return f.toSql(f.createJavaType(storageType));
}
/** {@inheritDoc} */
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/fun/IgniteStdSqlOperatorTable.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/fun/IgniteStdSqlOperatorTable.java
index 0f4e7dc..b947462 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/fun/IgniteStdSqlOperatorTable.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/fun/IgniteStdSqlOperatorTable.java
@@ -250,6 +250,7 @@ public class IgniteStdSqlOperatorTable extends
ReflectiveSqlOperatorTable {
register(SqlLibraryOperators.GREATEST);
register(SqlLibraryOperators.COMPRESS);
register(SqlStdOperatorTable.OCTET_LENGTH);
+ register(SqlStdOperatorTable.DEFAULT);
// XML Operators.
register(SqlLibraryOperators.EXTRACT_VALUE);
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/TypeUtils.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/TypeUtils.java
index a6e0376..bcba8bb 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/TypeUtils.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/TypeUtils.java
@@ -18,6 +18,8 @@
package org.apache.ignite.internal.processors.query.calcite.util;
import java.lang.reflect.Type;
+import java.sql.Date;
+import java.sql.Time;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.Period;
@@ -34,6 +36,7 @@ import com.google.common.collect.ImmutableSet;
import org.apache.calcite.DataContext;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
@@ -42,9 +45,16 @@ 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.SqlIntervalLiteral;
+import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.util.DateString;
import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimestampString;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import
org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.RowHandler;
import
org.apache.ignite.internal.processors.query.calcite.schema.ColumnDescriptor;
@@ -54,6 +64,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import static
org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.UNEXPECTED_ELEMENT_TYPE;
import static
org.apache.ignite.internal.processors.query.calcite.util.Commons.transform;
/** */
@@ -260,22 +271,22 @@ public class TypeUtils {
}
/** */
- public static Object toInternal(ExecutionContext<?> ectx, Object val) {
- return val == null ? null : toInternal(ectx, val, val.getClass());
+ public static Object toInternal(DataContext ctx, Object val) {
+ return val == null ? null : toInternal(ctx, val, val.getClass());
}
/** */
- public static Object toInternal(ExecutionContext<?> ectx, Object val, Type
storageType) {
+ public static Object toInternal(DataContext ctx, 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);
+ return (int)(SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ctx)) / 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);
+ return (int)(SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ctx)) % DateTimeUtils.MILLIS_PER_DAY);
else if (storageType == Timestamp.class)
- return SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ectx));
+ return SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ctx));
else if (storageType == java.util.Date.class)
- return SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ectx));
+ return SqlFunctions.toLong((java.util.Date)val,
DataContext.Variable.TIME_ZONE.get(ctx));
else if (storageType == Duration.class) {
return TimeUnit.SECONDS.toMillis(((Duration)val).getSeconds())
+ TimeUnit.NANOSECONDS.toMillis(((Duration)val).getNano());
@@ -289,17 +300,17 @@ public class TypeUtils {
}
/** */
- public static Object fromInternal(ExecutionContext<?> ectx, Object val,
Type storageType) {
+ public static Object fromInternal(DataContext ctx, Object val, Type
storageType) {
if (val == null)
return null;
else if (storageType == java.sql.Date.class && val instanceof Integer)
- return new java.sql.Date(fromLocalTs(ectx, (Integer)val *
DateTimeUtils.MILLIS_PER_DAY));
+ return new java.sql.Date(fromLocalTs(ctx, (Integer)val *
DateTimeUtils.MILLIS_PER_DAY));
else if (storageType == java.sql.Time.class && val instanceof Integer)
- return new java.sql.Time(fromLocalTs(ectx, (Integer)val));
+ return new java.sql.Time(fromLocalTs(ctx, (Integer)val));
else if (storageType == Timestamp.class && val instanceof Long)
- return new Timestamp(fromLocalTs(ectx, (Long)val));
+ return new Timestamp(fromLocalTs(ctx, (Long)val));
else if (storageType == java.util.Date.class && val instanceof Long)
- return new java.util.Date(fromLocalTs(ectx, (Long)val));
+ return new java.util.Date(fromLocalTs(ctx, (Long)val));
else if (storageType == Duration.class && val instanceof Long)
return Duration.ofMillis((Long)val);
else if (storageType == Period.class && val instanceof Integer)
@@ -310,9 +321,52 @@ public class TypeUtils {
return val;
}
+ /**
+ * Creates a value of required type from the literal.
+ */
+ public static Object fromLiteral(DataContext ctx, Type storageType,
SqlLiteral literal) {
+ Object internalVal;
+
+ try {
+ storageType = Primitive.box(storageType); // getValueAs()
implemented only for boxed classes.
+
+ if (Date.class.equals(storageType))
+ internalVal =
literal.getValueAs(DateString.class).getDaysSinceEpoch();
+ else if (Time.class.equals(storageType))
+ internalVal =
literal.getValueAs(TimeString.class).getMillisOfDay();
+ else if (Timestamp.class.equals(storageType))
+ internalVal =
literal.getValueAs(TimestampString.class).getMillisSinceEpoch();
+ else if (Duration.class.equals(storageType)) {
+ if (literal instanceof SqlIntervalLiteral &&
+
!literal.getValueAs(SqlIntervalLiteral.IntervalValue.class).getIntervalQualifier().isYearMonth())
+ internalVal = literal.getValueAs(Long.class);
+ else
+ throw new IgniteException("Expected DAY-TIME interval
literal");
+ }
+ else if (Period.class.equals(storageType))
+ if (literal instanceof SqlIntervalLiteral &&
+
literal.getValueAs(SqlIntervalLiteral.IntervalValue.class).getIntervalQualifier().isYearMonth())
+ internalVal = literal.getValueAs(Long.class).intValue();
+ else
+ throw new IgniteException("Expected YEAR-MONTH interval
literal");
+ else {
+ if (storageType instanceof Class)
+ internalVal = literal.getValueAs((Class<?>)storageType);
+ else
+ throw new IgniteException("Unexpected storage type: " +
storageType);
+ }
+ }
+ catch (Throwable t) { // Throwable is requred here, since Calcite
throws Assertion error in case of type mismatch.
+ throw new IgniteSQLException("Cannot convert literal " + literal +
" to type " + storageType,
+ UNEXPECTED_ELEMENT_TYPE, t);
+ }
+
+ return fromInternal(ctx, internalVal, storageType);
+ }
+
/** */
- private static long fromLocalTs(ExecutionContext<?> ectx, long ts) {
- TimeZone tz = DataContext.Variable.TIME_ZONE.get(ectx);
+ private static long fromLocalTs(DataContext ctx, long ts) {
+ TimeZone tz = DataContext.Variable.TIME_ZONE.get(ctx);
// Taking into account DST, offset can be changed after converting
from UTC to time-zone.
return ts - tz.getOffset(ts - tz.getOffset(ts));
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
index eca6b63..be3ee77 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
@@ -16,8 +16,15 @@
*/
package org.apache.ignite.internal.processors.query.calcite.integration;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Period;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@@ -427,4 +434,85 @@ public class TableDmlIntegrationTest extends
AbstractBasicIntegrationTest {
"WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, b)",
IgniteSQLException.class,
"Failed to MERGE some keys due to keys conflict");
}
+
+ /** */
+ @Test
+ public void testInsertDefaultValue() {
+ checkDefaultValue("BOOLEAN", "TRUE", Boolean.TRUE);
+ checkDefaultValue("BOOLEAN NOT NULL", "TRUE", Boolean.TRUE);
+ checkDefaultValue("BIGINT", "10", 10L);
+ checkDefaultValue("INTEGER", "10", 10);
+ checkDefaultValue("SMALLINT", "10", (short)10);
+ checkDefaultValue("TINYINT", "10", (byte)10);
+ checkDefaultValue("DOUBLE", "10.01", 10.01d);
+ checkDefaultValue("FLOAT", "10.01", 10.01f);
+ checkDefaultValue("DECIMAL(4, 2)", "10.01", new BigDecimal("10.01"));
+ checkDefaultValue("CHAR(2)", "'10'", "10");
+ checkDefaultValue("VARCHAR", "'10'", "10");
+ checkDefaultValue("VARCHAR NOT NULL", "'10'", "10");
+ checkDefaultValue("VARCHAR(2)", "'10'", "10");
+ checkDefaultValue("INTERVAL DAYS TO SECONDS", "INTERVAL '10' DAYS",
Duration.ofDays(10));
+ checkDefaultValue("INTERVAL YEARS TO MONTHS", "INTERVAL '10' MONTHS",
Period.ofMonths(10));
+ checkDefaultValue("INTERVAL MONTHS", "INTERVAL '10' YEARS",
Period.ofYears(10));
+ checkDefaultValue("DATE", "DATE '2021-01-01'",
Date.valueOf("2021-01-01"));
+ checkDefaultValue("TIME", "TIME '01:01:01'", Time.valueOf("01:01:01"));
+ checkDefaultValue("TIMESTAMP", "TIMESTAMP '2021-01-01 01:01:01'",
Timestamp.valueOf("2021-01-01 01:01:01"));
+ checkDefaultValue("BINARY(3)", "x'010203'", new byte[] {1, 2, 3});
+ checkDefaultValue("VARBINARY", "x'010203'", new byte[] {1, 2, 3});
+
+ checkWrongDefault("VARCHAR", "10");
+ checkWrongDefault("INT", "'10'");
+ checkWrongDefault("INT", "TRUE");
+ checkWrongDefault("DATE", "10");
+ checkWrongDefault("DATE", "TIME '01:01:01'");
+ checkWrongDefault("TIME", "TIMESTAMP '2021-01-01 01:01:01'");
+ checkWrongDefault("BOOLEAN", "1");
+ checkWrongDefault("INTERVAL DAYS", "INTERVAL '10' MONTHS");
+ checkWrongDefault("INTERVAL MONTHS", "INTERVAL '10' DAYS");
+ checkWrongDefault("VARBINARY", "'10'");
+ checkWrongDefault("VARBINARY", "10");
+ }
+
+ /** */
+ private void checkDefaultValue(String sqlType, String sqlVal, Object
expectedVal) {
+ try {
+ executeSql("CREATE TABLE test (dummy INT, val " + sqlType + "
DEFAULT " + sqlVal + ")");
+ executeSql("INSERT INTO test (dummy) VALUES (0)");
+
+ checkQueryResult("SELECT val FROM test", expectedVal);
+
+ executeSql("DELETE FROM test");
+ executeSql("INSERT INTO test (dummy, val) VALUES (0, DEFAULT)");
+
+ checkQueryResult("SELECT val FROM test", expectedVal);
+ }
+ finally {
+ executeSql("DROP TABLE IF EXISTS test");
+ }
+ }
+
+ /** */
+ private void checkQueryResult(String sql, Object expectedVal) {
+ if (expectedVal.getClass().isArray()) {
+ List<List<?>> res = executeSql(sql);
+
+ assertEquals(1, res.size());
+ assertEquals(1, res.get(0).size());
+ assertTrue("Expected: " + Arrays.deepToString(new Object[]
{expectedVal}) + ", actual: " +
+ Arrays.deepToString(new Object[] {res.get(0).get(0)}),
Objects.deepEquals(expectedVal, res.get(0).get(0)));
+ }
+ else
+ assertQuery(sql).returns(expectedVal).check();
+ }
+
+ /** */
+ private void checkWrongDefault(String sqlType, String sqlVal) {
+ try {
+ assertThrows("CREATE TABLE test (val " + sqlType + " DEFAULT " +
sqlVal + ")",
+ IgniteSQLException.class, "Cannot convert literal");
+ }
+ finally {
+ executeSql("DROP TABLE IF EXISTS test");
+ }
+ }
}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcCrossEngineTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcCrossEngineTest.java
new file mode 100644
index 0000000..c5430fc
--- /dev/null
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcCrossEngineTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.jdbc;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Cross check queries on experimental and non-experimental SQL engines.
+ */
+public class JdbcCrossEngineTest extends GridCommonAbstractTest {
+ /** URL. */
+ private static final String url =
"jdbc:ignite:thin://127.0.0.1?useExperimentalQueryEngine=";
+
+ /** Nodes count. */
+ private static final int nodesCnt = 3;
+
+ /** Connections. */
+ private final Connection[] conns = new Connection[2];
+
+ /** Statements. */
+ private final Statement[] stmts = new Statement[2];
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ startGrids(nodesCnt);
+
+ for (int i = 0; i < conns.length; i++) {
+ conns[i] = DriverManager.getConnection(url + (i == 0 ? "false" :
"true"));
+ conns[i].setSchema("PUBLIC");
+ stmts[i] = conns[i].createStatement();
+
+ assert stmts[i] != null;
+ assert !stmts[i].isClosed();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ for (int i = 0; i < conns.length; i++) {
+ if (stmts[i] != null && !stmts[i].isClosed()) {
+ stmts[i].close();
+
+ assert stmts[i].isClosed();
+ }
+
+ conns[i].close();
+
+ assert stmts[i].isClosed();
+ assert conns[i].isClosed();
+ }
+
+ stopAllGrids();
+ }
+
+ /** */
+ @Test
+ public void testInsertDefaultValue() {
+ // Test only types supported by both SQL engines.
+ checkInsertDefaultValue("BOOLEAN", "TRUE", Boolean.TRUE);
+ checkInsertDefaultValue("BOOLEAN NOT NULL", "TRUE", Boolean.TRUE);
+ checkInsertDefaultValue("BIGINT", "10", 10L);
+ checkInsertDefaultValue("INTEGER", "10", 10);
+ checkInsertDefaultValue("SMALLINT", "10", (short)10);
+ checkInsertDefaultValue("TINYINT", "10", (byte)10);
+ checkInsertDefaultValue("DOUBLE", "10.01", 10.01d);
+ checkInsertDefaultValue("REAL", "10.01", 10.01f);
+ checkInsertDefaultValue("DECIMAL(4, 2)", "10.01", new
BigDecimal("10.01"));
+ checkInsertDefaultValue("CHAR(2)", "'10'", "10");
+ checkInsertDefaultValue("VARCHAR", "'10'", "10");
+ checkInsertDefaultValue("VARCHAR NOT NULL", "'10'", "10");
+ checkInsertDefaultValue("VARCHAR(2)", "'10'", "10");
+ checkInsertDefaultValue("DATE", "DATE '2021-01-01'",
Date.valueOf("2021-01-01"));
+ checkInsertDefaultValue("TIME", "TIME '01:01:01'",
Time.valueOf("01:01:01"));
+ checkInsertDefaultValue("TIMESTAMP", "TIMESTAMP '2021-01-01
01:01:01'", Timestamp.valueOf("2021-01-01 01:01:01"));
+ checkInsertDefaultValue("BINARY(3)", "x'010203'", new byte[] {1, 2,
3});
+ }
+
+ /** */
+ private void checkInsertDefaultValue(String sqlType, String sqlVal, Object
expectedVal) {
+ crossCheck(
+ stmt -> execute(stmt, "CREATE TABLE test (id INT PRIMARY KEY, val
" + sqlType + " DEFAULT " + sqlVal + ")"),
+ stmt -> {
+ try {
+ execute(stmt, "INSERT INTO test (id) VALUES (0)");
+
+ List<List<Object>> res = executeQuery(stmt, "SELECT val
FROM test");
+
+ if (expectedVal.getClass().isArray())
+ assertTrue(Objects.deepEquals(expectedVal,
res.get(0).get(0)));
+ else
+ assertEquals(expectedVal, res.get(0).get(0));
+ }
+ finally {
+ execute(stmt, "DROP TABLE IF EXISTS test");
+ }
+ }
+ );
+ }
+
+ /** */
+ private void execute(Statement stmt, String sql) {
+ try {
+ stmt.execute(sql);
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e.getMessage(), e);
+ }
+ }
+
+ /** */
+ private List<List<Object>> executeQuery(Statement stmt, String sql) {
+ try (ResultSet rs = stmt.executeQuery(sql)) {
+ List<List<Object>> res = new ArrayList<>();
+ while (rs.next()) {
+ List<Object> row = new ArrayList<>();
+
+ for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++)
+ row.add(rs.getObject(i));
+
+ res.add(row);
+ }
+
+ return res;
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e.getMessage(), e);
+ }
+ }
+
+ /** */
+ private void crossCheck(Consumer<Statement> consumer1, Consumer<Statement>
consumer2) {
+ // Execute consumer1 on non-experimental engine, consumer2 in
experimental engine.
+ consumer1.accept(stmts[0]);
+ consumer2.accept(stmts[1]);
+ // Execute consumer1 on non-experimental engine, consumer2 in
experimental engine.
+ consumer1.accept(stmts[1]);
+ consumer2.accept(stmts[0]);
+ }
+}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index a859aaa..580a2e9 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -44,6 +44,7 @@ import
org.apache.ignite.internal.processors.query.calcite.integration.TableDdlI
import
org.apache.ignite.internal.processors.query.calcite.integration.TableDmlIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.UserDdlIntegrationTest;
import
org.apache.ignite.internal.processors.query.calcite.integration.UserDefinedFunctionsIntegrationTest;
+import
org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcCrossEngineTest;
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcQueryTest;
import
org.apache.ignite.internal.processors.query.calcite.rules.JoinCommuteRulesTest;
import
org.apache.ignite.internal.processors.query.calcite.rules.OrToUnionRuleTest;
@@ -61,6 +62,7 @@ import org.junit.runners.Suite;
CalciteQueryProcessorTest.class,
CalciteErrorHandlilngIntegrationTest.class,
JdbcQueryTest.class,
+ JdbcCrossEngineTest.class,
CalciteBasicSecondaryIndexIntegrationTest.class,
CancelTest.class,
DateTimeTest.class,