You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by iv...@apache.org on 2022/10/17 07:11:20 UTC
[ignite] branch master updated: IGNITE-17598 SQL Calcite: Implement query metadata. (#10291)
This is an automated email from the ASF dual-hosted git repository.
ivandasch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 9e64220c422 IGNITE-17598 SQL Calcite: Implement query metadata. (#10291)
9e64220c422 is described below
commit 9e64220c4229afa9b30d707ba24a9803c79a3b9e
Author: Ivan Daschinskiy <iv...@apache.org>
AuthorDate: Mon Oct 17 10:11:09 2022 +0300
IGNITE-17598 SQL Calcite: Implement query metadata. (#10291)
---
modules/calcite/pom.xml | 7 +
.../query/calcite/CalciteQueryProcessor.java | 159 +++++++---
.../query/calcite/exec/ExecutionServiceImpl.java | 12 -
.../calcite/prepare/AbstractMultiStepPlan.java | 16 +-
.../calcite/prepare/DynamicParamTypeExtractor.java | 91 ++++++
.../query/calcite/prepare/FieldsMetadata.java | 38 +--
.../query/calcite/prepare/FieldsMetadataImpl.java | 45 ++-
.../calcite/prepare/IgniteRelRexNodeShuttle.java | 142 +++++++++
.../query/calcite/prepare/MultiStepDmlPlan.java | 12 +-
.../query/calcite/prepare/MultiStepPlan.java | 5 +
.../query/calcite/prepare/MultiStepQueryPlan.java | 12 +-
.../query/calcite/prepare/PrepareServiceImpl.java | 13 +-
.../calcite/schema/CacheTableDescriptorImpl.java | 32 +-
.../integration/QueryMetadataIntegrationTest.java | 345 +++++++++++++++++++++
.../query/calcite/jdbc/JdbcQueryTest.java | 49 +++
.../query/calcite/planner/PlannerTest.java | 17 +-
.../ignite/testsuites/IntegrationTestSuite.java | 2 +
.../internal/jdbc2/JdbcPreparedStatement.java | 4 +-
.../processors/odbc/jdbc/JdbcParameterMeta.java | 14 +
.../processors/odbc/jdbc/JdbcRequestHandler.java | 3 +-
.../odbc/jdbc/JdbcRequestHandlerWorker.java | 2 +-
.../processors/odbc/odbc/OdbcRequestHandler.java | 4 +-
.../odbc/odbc/OdbcRequestHandlerWorker.java | 2 +-
.../processors/query/GridQueryProcessor.java | 87 +++++-
.../internal/processors/query/NoOpQueryEngine.java | 20 +-
.../internal/processors/query/QueryEngine.java | 30 +-
26 files changed, 1032 insertions(+), 131 deletions(-)
diff --git a/modules/calcite/pom.xml b/modules/calcite/pom.xml
index 6721982ed6f..5bfc03b2b77 100644
--- a/modules/calcite/pom.xml
+++ b/modules/calcite/pom.xml
@@ -198,6 +198,13 @@
<artifactId>ignite-clients</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index 6ff731ae2d6..4d25fcef58d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
+import java.util.function.BiFunction;
import java.util.function.Function;
import org.apache.calcite.DataContexts;
import org.apache.calcite.config.Lex;
@@ -48,6 +49,7 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.failure.FailureProcessor;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryContext;
import org.apache.ignite.internal.processors.query.QueryEngine;
@@ -70,8 +72,11 @@ import org.apache.ignite.internal.processors.query.calcite.metadata.MappingServi
import org.apache.ignite.internal.processors.query.calcite.metadata.MappingServiceImpl;
import org.apache.ignite.internal.processors.query.calcite.metadata.cost.IgniteCostFactory;
import org.apache.ignite.internal.processors.query.calcite.prepare.CacheKey;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ExplainPlan;
+import org.apache.ignite.internal.processors.query.calcite.prepare.FieldsMetadata;
import org.apache.ignite.internal.processors.query.calcite.prepare.IgniteConvertletTable;
import org.apache.ignite.internal.processors.query.calcite.prepare.IgniteTypeCoercion;
+import org.apache.ignite.internal.processors.query.calcite.prepare.MultiStepPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.PrepareServiceImpl;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCache;
@@ -85,6 +90,7 @@ import org.apache.ignite.internal.processors.query.calcite.sql.generated.IgniteS
import org.apache.ignite.internal.processors.query.calcite.trait.CorrelationTraitDef;
import org.apache.ignite.internal.processors.query.calcite.trait.DistributionTraitDef;
import org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTraitDef;
+import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeSystem;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.calcite.util.LifecycleAware;
@@ -330,39 +336,39 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
String sql,
Object... params
) throws IgniteSQLException {
- SchemaPlus schema = schemaHolder.schema(schemaName);
-
- assert schema != null : "Schema not found: " + schemaName;
-
- QueryPlan plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql));
-
- if (plan != null) {
- return Collections.singletonList(
- executeQuery(qryCtx, qry -> plan, schemaName, sql, null, params)
- );
- }
-
- SqlNodeList qryList = Commons.parse(sql, FRAMEWORK_CONFIG.getParserConfig());
-
- List<FieldsQueryCursor<List<?>>> cursors = new ArrayList<>(qryList.size());
- List<RootQuery<Object[]>> qrys = new ArrayList<>(qryList.size());
-
- for (final SqlNode sqlNode: qryList) {
- FieldsQueryCursor<List<?>> cursor = executeQuery(qryCtx, qry -> {
- if (qryList.size() == 1) {
- return queryPlanCache().queryPlan(
- new CacheKey(schemaName, sql), // Use source SQL to avoid redundant parsing next time.
- () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext())
- );
- }
- else
- return prepareSvc.prepareSingle(sqlNode, qry.planningContext());
- }, schemaName, sqlNode.toString(), qrys, params);
+ return parseAndProcessQuery(qryCtx, executionSvc::executePlan, schemaName, sql, params);
+ }
- cursors.add(cursor);
- }
+ /** {@inheritDoc} */
+ @Override public List<List<GridQueryFieldMetadata>> parameterMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String sql
+ ) throws IgniteSQLException {
+ return parseAndProcessQuery(ctx, (qry, plan) -> {
+ try {
+ return fieldsMeta(plan, true);
+ }
+ finally {
+ qryReg.unregister(qry.id());
+ }
+ }, schemaName, sql);
+ }
- return cursors;
+ /** {@inheritDoc} */
+ @Override public List<List<GridQueryFieldMetadata>> resultSetMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String sql
+ ) throws IgniteSQLException {
+ return parseAndProcessQuery(ctx, (qry, plan) -> {
+ try {
+ return fieldsMeta(plan, false);
+ }
+ finally {
+ qryReg.unregister(qry.id());
+ }
+ }, schemaName, sql);
}
/** {@inheritDoc} */
@@ -372,6 +378,10 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
String sql,
List<Object[]> batchedParams
) throws IgniteSQLException {
+ SchemaPlus schema = schemaHolder.schema(schemaName);
+
+ assert schema != null : "Schema not found: " + schemaName;
+
SqlNodeList qryNodeList = Commons.parse(sql, FRAMEWORK_CONFIG.getParserConfig());
if (qryNodeList.size() != 1) {
@@ -396,7 +406,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
@Override public QueryPlan apply(RootQuery<Object[]> qry) {
if (plan == null) {
- plan = queryPlanCache().queryPlan(new CacheKey(schemaName, sql), () ->
+ plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql), () ->
prepareSvc.prepareSingle(qryNode, qry.planningContext())
);
}
@@ -405,16 +415,66 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
}
};
- for (final Object[] batch: batchedParams)
- cursors.add(executeQuery(qryCtx, planSupplier, schemaName, sql, qrys, batch));
+ for (final Object[] batch: batchedParams) {
+ FieldsQueryCursor<List<?>> cur = processQuery(qryCtx, qry ->
+ executionSvc.executePlan(qry, planSupplier.apply(qry)), schema.getName(), sql, qrys, batch);
+
+ cursors.add(cur);
+ }
return cursors;
}
/** */
- private FieldsQueryCursor<List<?>> executeQuery(
+ private <T> List<T> parseAndProcessQuery(
@Nullable QueryContext qryCtx,
- Function<RootQuery<Object[]>, QueryPlan> plan,
+ BiFunction<RootQuery<Object[]>, QueryPlan, T> action,
+ @Nullable String schemaName,
+ String sql,
+ Object... params
+ ) throws IgniteSQLException {
+ SchemaPlus schema = schemaHolder.schema(schemaName);
+
+ assert schema != null : "Schema not found: " + schemaName;
+
+ QueryPlan plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql));
+
+ if (plan != null) {
+ return Collections.singletonList(
+ processQuery(qryCtx, qry -> action.apply(qry, plan), schema.getName(), sql, null, params)
+ );
+ }
+
+ SqlNodeList qryList = Commons.parse(sql, FRAMEWORK_CONFIG.getParserConfig());
+
+ List<T> res = new ArrayList<>(qryList.size());
+ List<RootQuery<Object[]>> qrys = new ArrayList<>(qryList.size());
+
+ for (final SqlNode sqlNode: qryList) {
+ T singleRes = processQuery(qryCtx, qry -> {
+ QueryPlan plan0;
+ if (qryList.size() == 1) {
+ plan0 = queryPlanCache().queryPlan(
+ new CacheKey(schema.getName(), sql), // Use source SQL to avoid redundant parsing next time.
+ () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext())
+ );
+ }
+ else
+ plan0 = prepareSvc.prepareSingle(sqlNode, qry.planningContext());
+
+ return action.apply(qry, plan0);
+ }, schema.getName(), sqlNode.toString(), qrys, params);
+
+ res.add(singleRes);
+ }
+
+ return res;
+ }
+
+ /** */
+ private <T> T processQuery(
+ @Nullable QueryContext qryCtx,
+ Function<RootQuery<Object[]>, T> action,
String schema,
String sql,
@Nullable List<RootQuery<Object[]>> qrys,
@@ -437,7 +497,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
qryReg.register(qry);
try {
- return executionSvc.executePlan(qry, plan.apply(qry));
+ return action.apply(qry);
}
catch (Throwable e) {
boolean isCanceled = qry.isCancelled();
@@ -454,6 +514,31 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
}
}
+ /**
+ * @param plan Query plan.
+ * @param isParamsMeta If {@code true}, return parameter metadata, otherwise result set metadata.
+ * @return Return query fields metadata.
+ */
+ private List<GridQueryFieldMetadata> fieldsMeta(QueryPlan plan, boolean isParamsMeta) {
+ IgniteTypeFactory typeFactory = Commons.typeFactory();
+
+ switch (plan.type()) {
+ case QUERY:
+ case DML:
+ MultiStepPlan msPlan = (MultiStepPlan)plan;
+
+ FieldsMetadata meta = isParamsMeta ? msPlan.paramsMetadata() : msPlan.fieldsMetadata();
+
+ return meta.queryFieldsMetadata(typeFactory);
+ case EXPLAIN:
+ ExplainPlan exPlan = (ExplainPlan)plan;
+
+ return isParamsMeta ? Collections.emptyList() : exPlan.fieldsMeta().queryFieldsMetadata(typeFactory);
+ default:
+ return Collections.emptyList();
+ }
+ }
+
/** */
private void onStart(GridKernalContext ctx, Service... services) {
for (Service service : services) {
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
index 5aa192c73d8..6263a235d7d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
@@ -28,7 +28,6 @@ import java.util.stream.Collectors;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlInsert;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.tools.Frameworks;
@@ -73,13 +72,11 @@ import org.apache.ignite.internal.processors.query.calcite.prepare.BaseQueryCont
import org.apache.ignite.internal.processors.query.calcite.prepare.CacheKey;
import org.apache.ignite.internal.processors.query.calcite.prepare.DdlPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.ExplainPlan;
-import org.apache.ignite.internal.processors.query.calcite.prepare.FieldsMetadata;
import org.apache.ignite.internal.processors.query.calcite.prepare.FieldsMetadataImpl;
import org.apache.ignite.internal.processors.query.calcite.prepare.Fragment;
import org.apache.ignite.internal.processors.query.calcite.prepare.FragmentPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.MappingQueryContext;
import org.apache.ignite.internal.processors.query.calcite.prepare.MultiStepPlan;
-import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
import org.apache.ignite.internal.processors.query.calcite.prepare.PrepareServiceImpl;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCache;
@@ -90,7 +87,6 @@ import org.apache.ignite.internal.processors.query.calcite.util.AbstractService;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.calcite.util.ConvertingClosableIterator;
import org.apache.ignite.internal.processors.query.calcite.util.ListFieldsQueryCursor;
-import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -658,14 +654,6 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
}
}
- /** */
- private FieldsMetadata queryFieldsMetadata(PlanningContext ctx, RelDataType sqlType,
- @Nullable List<List<String>> origins) {
- RelDataType resultType = TypeUtils.getResultType(
- ctx.typeFactory(), ctx.catalogReader(), sqlType, origins);
- return new FieldsMetadataImpl(resultType, origins);
- }
-
/** */
private void onMessage(UUID nodeId, final QueryStartRequest msg) {
assert nodeId != null && msg != null;
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractMultiStepPlan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractMultiStepPlan.java
index b86f226377d..fed7a996658 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractMultiStepPlan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractMultiStepPlan.java
@@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.query.calcite.rel.IgniteReceiver;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSender;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
/**
*
@@ -37,6 +38,9 @@ public abstract class AbstractMultiStepPlan implements MultiStepPlan {
/** */
protected final FieldsMetadata fieldsMetadata;
+ /** */
+ protected final FieldsMetadata paramsMetadata;
+
/** */
protected final QueryTemplate queryTemplate;
@@ -44,9 +48,14 @@ public abstract class AbstractMultiStepPlan implements MultiStepPlan {
protected ExecutionPlan executionPlan;
/** */
- protected AbstractMultiStepPlan(QueryTemplate queryTemplate, FieldsMetadata fieldsMetadata) {
+ protected AbstractMultiStepPlan(
+ QueryTemplate queryTemplate,
+ FieldsMetadata fieldsMetadata,
+ @Nullable FieldsMetadata paramsMetadata
+ ) {
this.queryTemplate = queryTemplate;
this.fieldsMetadata = fieldsMetadata;
+ this.paramsMetadata = paramsMetadata;
}
/** {@inheritDoc} */
@@ -59,6 +68,11 @@ public abstract class AbstractMultiStepPlan implements MultiStepPlan {
return fieldsMetadata;
}
+ /** {@inheritDoc} */
+ @Override public FieldsMetadata paramsMetadata() {
+ return paramsMetadata;
+ }
+
/** {@inheritDoc} */
@Override public FragmentMapping mapping(Fragment fragment) {
return fragment.mapping();
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DynamicParamTypeExtractor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DynamicParamTypeExtractor.java
new file mode 100644
index 00000000000..a6ae77d1dff
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DynamicParamTypeExtractor.java
@@ -0,0 +1,91 @@
+/*
+ * 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.lang.reflect.Type;
+import java.util.Collection;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexDynamicParam;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
+import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+
+/** */
+public class DynamicParamTypeExtractor {
+ /** */
+ public static ParamsMetadata go(IgniteRel root) {
+ DynamicParamsShuttle paramsShuttle = new DynamicParamsShuttle();
+
+ new IgniteRelRexNodeShuttle(paramsShuttle).visit(root);
+
+ return new ParamsMetadata(paramsShuttle.acc.values());
+ }
+
+ /** */
+ private static final class DynamicParamsShuttle extends RexShuttle {
+ /** */
+ private final SortedMap<Integer, RexDynamicParam> acc = new TreeMap<>();
+
+ /** {@inheritDoc} */
+ @Override public RexNode visitDynamicParam(RexDynamicParam param) {
+ acc.put(param.getIndex(), param);
+
+ return super.visitDynamicParam(param);
+ }
+ }
+
+ /** */
+ private static final class ParamsMetadata implements FieldsMetadata {
+ /** */
+ private final Collection<RexDynamicParam> params;
+
+ /** */
+ ParamsMetadata(Collection<RexDynamicParam> params) {
+ this.params = params;
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelDataType rowType() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<GridQueryFieldMetadata> queryFieldsMetadata(IgniteTypeFactory typeFactory) {
+ return params.stream().map(param -> {
+ RelDataType paramType = param.getType();
+ Type fieldCls = typeFactory.getResultClass(paramType);
+
+ return new CalciteQueryFieldMetadata(
+ null,
+ null,
+ param.getName(),
+ fieldCls == null ? Void.class.getName() : fieldCls.getTypeName(),
+ paramType.getPrecision(),
+ paramType.getScale(),
+ paramType.isNullable()
+ );
+ }).collect(Collectors.toList());
+ }
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadata.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadata.java
index 492926cd989..5164878d4d5 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadata.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadata.java
@@ -17,14 +17,10 @@
package org.apache.ignite.internal.processors.query.calcite.prepare;
-import java.lang.reflect.Type;
import java.util.List;
-import com.google.common.collect.ImmutableList;
import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
-import org.apache.ignite.internal.util.typedef.F;
/**
*
@@ -35,41 +31,9 @@ public interface FieldsMetadata {
*/
RelDataType rowType();
- /**
- * @return Result row origins (or where a field value comes from).
- */
- List<List<String>> origins();
-
/**
* @param typeFactory Type factory.
* @return Query field descriptors collection&
*/
- default List<GridQueryFieldMetadata> queryFieldsMetadata(IgniteTypeFactory typeFactory) {
- RelDataType rowType = rowType();
- List<List<String>> origins = origins();
- List<RelDataTypeField> fields = rowType.getFieldList();
-
- assert origins == null || fields.size() == origins.size();
-
- ImmutableList.Builder<GridQueryFieldMetadata> b = ImmutableList.builder();
-
- for (int i = 0; i < fields.size(); i++) {
- List<String> origin = origins != null ? origins.get(i) : null;
- RelDataTypeField field = fields.get(i);
- RelDataType fieldType = field.getType();
- Type fieldCls = typeFactory.getResultClass(fieldType);
-
- b.add(new CalciteQueryFieldMetadata(
- F.isEmpty(origin) ? null : origin.get(0),
- F.isEmpty(origin) ? null : origin.get(1),
- F.isEmpty(origin) ? field.getName() : origin.get(2),
- fieldCls == null ? Void.class.getName() : fieldCls.getTypeName(),
- fieldType.getPrecision(),
- fieldType.getScale(),
- fieldType.isNullable()
- ));
- }
-
- return b.build();
- }
+ List<GridQueryFieldMetadata> queryFieldsMetadata(IgniteTypeFactory typeFactory);
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadataImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadataImpl.java
index 42a7c14b565..06dea24164d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadataImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/FieldsMetadataImpl.java
@@ -17,30 +17,71 @@
package org.apache.ignite.internal.processors.query.calcite.prepare;
+import java.lang.reflect.Type;
import java.util.List;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
+import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+import org.apache.ignite.internal.util.typedef.F;
/** */
public class FieldsMetadataImpl implements FieldsMetadata {
/** */
private final RelDataType rowType;
+ /** */
+ private final RelDataType sqlRowType;
+
/** */
private final List<List<String>> origins;
/** */
public FieldsMetadataImpl(RelDataType rowType, List<List<String>> origins) {
this.rowType = rowType;
+ sqlRowType = rowType;
+ this.origins = origins;
+ }
+
+ /** */
+ public FieldsMetadataImpl(RelDataType sqlRowType, RelDataType rowType, List<List<String>> origins) {
+ this.rowType = rowType;
+ this.sqlRowType = sqlRowType;
this.origins = origins;
}
+
/** {@inheritDoc} */
@Override public RelDataType rowType() {
return rowType;
}
/** {@inheritDoc} */
- @Override public List<List<String>> origins() {
- return origins;
+ @Override public List<GridQueryFieldMetadata> queryFieldsMetadata(IgniteTypeFactory typeFactory) {
+ List<RelDataTypeField> fields = sqlRowType.getFieldList();
+
+ assert origins == null || fields.size() == origins.size();
+
+ ImmutableList.Builder<GridQueryFieldMetadata> b = ImmutableList.builder();
+
+ for (int i = 0; i < fields.size(); i++) {
+ List<String> origin = origins != null ? origins.get(i) : null;
+ RelDataTypeField field = fields.get(i);
+ RelDataType fieldType = field.getType();
+ Type fieldCls = typeFactory.getResultClass(fieldType);
+
+ b.add(new CalciteQueryFieldMetadata(
+ F.isEmpty(origin) ? null : origin.get(0),
+ F.isEmpty(origin) ? null : origin.get(1),
+ F.isEmpty(origin) ? field.getName() : origin.get(2),
+ fieldCls == null ? Void.class.getName() : fieldCls.getTypeName(),
+ fieldType.getPrecision(),
+ fieldType.getScale(),
+ fieldType.isNullable()
+ ));
+ }
+
+ return b.build();
}
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteRelRexNodeShuttle.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteRelRexNodeShuttle.java
new file mode 100644
index 00000000000..bb3741132bd
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteRelRexNodeShuttle.java
@@ -0,0 +1,142 @@
+/*
+ * 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 org.apache.calcite.rex.RexShuttle;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteFilter;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteHashIndexSpool;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteLimit;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteMergeJoin;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteNestedLoopJoin;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteProject;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSort;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSortedIndexSpool;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableFunctionScan;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableModify;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
+
+/** */
+public class IgniteRelRexNodeShuttle extends IgniteRelShuttle {
+ /** */
+ private final RexShuttle rexShuttle;
+
+ /** */
+ public IgniteRelRexNodeShuttle(RexShuttle rexShuttle) {
+ this.rexShuttle = rexShuttle;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteFilter rel) {
+ rexShuttle.apply(rel.getCondition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteProject rel) {
+ rexShuttle.apply(rel.getProjects());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteNestedLoopJoin rel) {
+ rexShuttle.apply(rel.getCondition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteCorrelatedNestedLoopJoin rel) {
+ rexShuttle.apply(rel.getCondition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteMergeJoin rel) {
+ rexShuttle.apply(rel.getCondition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteIndexScan rel) {
+ rexShuttle.apply(rel.projects());
+ rexShuttle.apply(rel.condition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteTableScan rel) {
+ rexShuttle.apply(rel.projects());
+ rexShuttle.apply(rel.condition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteSortedIndexSpool rel) {
+ rexShuttle.apply(rel.condition());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteHashIndexSpool rel) {
+ rexShuttle.apply(rel.condition());
+ rexShuttle.apply(rel.searchRow());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteTableModify rel) {
+ rexShuttle.apply(rel.getSourceExpressionList());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteLimit rel) {
+ rexShuttle.apply(rel.fetch());
+ rexShuttle.apply(rel.offset());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteSort rel) {
+ rexShuttle.apply(rel.offset);
+ rexShuttle.apply(rel.fetch);
+ rexShuttle.apply(rel.getSortExps());
+
+ return super.visit(rel);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteRel visit(IgniteTableFunctionScan rel) {
+ rexShuttle.apply(rel.getCall());
+
+ return super.visit(rel);
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepDmlPlan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepDmlPlan.java
index de2aae72483..6eaae464090 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepDmlPlan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepDmlPlan.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.query.calcite.prepare;
+import org.jetbrains.annotations.Nullable;
+
/**
* Distributed dml plan.
*/
@@ -24,8 +26,12 @@ public class MultiStepDmlPlan extends AbstractMultiStepPlan {
/**
* @param fieldsMeta Fields metadata.
*/
- public MultiStepDmlPlan(QueryTemplate queryTemplate, FieldsMetadata fieldsMeta) {
- super(queryTemplate, fieldsMeta);
+ public MultiStepDmlPlan(
+ QueryTemplate queryTemplate,
+ FieldsMetadata fieldsMeta,
+ @Nullable FieldsMetadata paramsMetadata
+ ) {
+ super(queryTemplate, fieldsMeta, paramsMetadata);
}
/** {@inheritDoc} */
@@ -35,6 +41,6 @@ public class MultiStepDmlPlan extends AbstractMultiStepPlan {
/** {@inheritDoc} */
@Override public QueryPlan copy() {
- return new MultiStepDmlPlan(queryTemplate, fieldsMetadata);
+ return new MultiStepDmlPlan(queryTemplate, fieldsMetadata, paramsMetadata);
}
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepPlan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepPlan.java
index fc27362da2a..1636bcc1124 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepPlan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepPlan.java
@@ -38,6 +38,11 @@ public interface MultiStepPlan extends QueryPlan {
*/
FieldsMetadata fieldsMetadata();
+ /**
+ * @return Parameters metadata;
+ */
+ FieldsMetadata paramsMetadata();
+
/**
* @param fragment Fragment.
* @return Mapping for a given fragment.
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepQueryPlan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepQueryPlan.java
index 866514fa936..1a18f81eb0b 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepQueryPlan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/MultiStepQueryPlan.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.query.calcite.prepare;
+import org.jetbrains.annotations.Nullable;
+
/**
* Distributed query plan.
*/
@@ -24,8 +26,12 @@ public class MultiStepQueryPlan extends AbstractMultiStepPlan {
/**
* @param fieldsMeta Fields metadata.
*/
- public MultiStepQueryPlan(QueryTemplate queryTemplate, FieldsMetadata fieldsMeta) {
- super(queryTemplate, fieldsMeta);
+ public MultiStepQueryPlan(
+ QueryTemplate queryTemplate,
+ FieldsMetadata fieldsMeta,
+ @Nullable FieldsMetadata paramsMetadata
+ ) {
+ super(queryTemplate, fieldsMeta, paramsMetadata);
}
/** {@inheritDoc} */
@@ -35,6 +41,6 @@ public class MultiStepQueryPlan extends AbstractMultiStepPlan {
/** {@inheritDoc} */
@Override public QueryPlan copy() {
- return new MultiStepQueryPlan(queryTemplate, fieldsMetadata);
+ return new MultiStepQueryPlan(queryTemplate, fieldsMetadata, paramsMetadata);
}
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
index 5494caf621c..922eb5953d2 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
@@ -151,12 +151,16 @@ public class PrepareServiceImpl extends AbstractService implements PrepareServic
IgniteRel igniteRel = optimize(sqlNode, planner, log);
+ // Extract parameters meta.
+ FieldsMetadata params = DynamicParamTypeExtractor.go(igniteRel);
+
// Split query plan to query fragments.
List<Fragment> fragments = new Splitter().go(igniteRel);
QueryTemplate template = new QueryTemplate(fragments);
- return new MultiStepQueryPlan(template, queryFieldsMetadata(ctx, validated.dataType(), validated.origins()));
+ return new MultiStepQueryPlan(template, queryFieldsMetadata(ctx, validated.dataType(), validated.origins()),
+ params);
}
/** */
@@ -169,12 +173,15 @@ public class PrepareServiceImpl extends AbstractService implements PrepareServic
// Convert to Relational operators graph
IgniteRel igniteRel = optimize(sqlNode, planner, log);
+ // Extract parameters meta.
+ FieldsMetadata params = DynamicParamTypeExtractor.go(igniteRel);
+
// Split query plan to query fragments.
List<Fragment> fragments = new Splitter().go(igniteRel);
QueryTemplate template = new QueryTemplate(fragments);
- return new MultiStepDmlPlan(template, queryFieldsMetadata(ctx, igniteRel.getRowType(), null));
+ return new MultiStepDmlPlan(template, queryFieldsMetadata(ctx, igniteRel.getRowType(), null), params);
}
/** */
@@ -183,7 +190,7 @@ public class PrepareServiceImpl extends AbstractService implements PrepareServic
RelDataType resultType = TypeUtils.getResultType(
ctx.typeFactory(), ctx.catalogReader(), sqlType, origins);
- return new FieldsMetadataImpl(resultType, origins);
+ return new FieldsMetadataImpl(sqlType, resultType, origins);
}
/** */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheTableDescriptorImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheTableDescriptorImpl.java
index 6d23cde0a3e..2a64f93962e 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheTableDescriptorImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheTableDescriptorImpl.java
@@ -156,23 +156,23 @@ public class CacheTableDescriptorImpl extends NullInitializerExpressionFactory
int valField = QueryUtils.VAL_COL;
for (String field : fields) {
+ GridQueryProperty prop = typeDesc.property(field);
+
if (Objects.equals(field, typeDesc.keyFieldAlias())) {
keyField = descriptors.size();
virtualFields.set(0);
- descriptors.add(new KeyValDescriptor(typeDesc.keyFieldAlias(), typeDesc.keyClass(), true, fldIdx++));
+ descriptors.add(new KeyValDescriptor(typeDesc.keyFieldAlias(), prop, true, fldIdx++));
}
else if (Objects.equals(field, typeDesc.valueFieldAlias())) {
valField = descriptors.size();
virtualFields.set(1);
- descriptors.add(new KeyValDescriptor(typeDesc.valueFieldAlias(), typeDesc.valueClass(), false, fldIdx++));
+ descriptors.add(new KeyValDescriptor(typeDesc.valueFieldAlias(), prop, false, fldIdx++));
}
else {
- GridQueryProperty prop = typeDesc.property(field);
-
virtualFields.set(prop.key() ? 0 : 1);
descriptors.add(new FieldDescriptor(prop, fldIdx++));
@@ -638,6 +638,9 @@ public class CacheTableDescriptorImpl extends NullInitializerExpressionFactory
/** */
private static class KeyValDescriptor implements CacheColumnDescriptor {
+ /** */
+ private final GridQueryProperty desc;
+
/** */
private final String name;
@@ -658,10 +661,21 @@ public class CacheTableDescriptorImpl extends NullInitializerExpressionFactory
this.name = name;
this.isKey = isKey;
this.fieldIdx = fieldIdx;
+ desc = null;
storageType = type;
}
+ /** */
+ private KeyValDescriptor(String name, GridQueryProperty desc, boolean isKey, int fieldIdx) {
+ this.name = name;
+ this.isKey = isKey;
+ this.fieldIdx = fieldIdx;
+ this.desc = desc;
+
+ storageType = desc.type();
+ }
+
/** {@inheritDoc} */
@Override public boolean field() {
return false;
@@ -694,8 +708,14 @@ public class CacheTableDescriptorImpl extends NullInitializerExpressionFactory
/** {@inheritDoc} */
@Override public RelDataType logicalType(IgniteTypeFactory f) {
- if (logicalType == null)
- logicalType = TypeUtils.sqlType(f, storageType, PRECISION_NOT_SPECIFIED, SCALE_NOT_SPECIFIED);
+ if (logicalType == null) {
+ logicalType = TypeUtils.sqlType(
+ f,
+ storageType,
+ desc != null && desc.precision() != -1 ? desc.precision() : PRECISION_NOT_SPECIFIED,
+ desc != null && desc.scale() != -1 ? desc.scale() : SCALE_NOT_SPECIFIED
+ );
+ }
return logicalType;
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryMetadataIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryMetadataIntegrationTest.java
new file mode 100644
index 00000000000..168d91009fe
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryMetadataIntegrationTest.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.integration;
+
+import java.math.BigDecimal;
+import java.sql.ResultSetMetaData;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
+import org.apache.ignite.internal.processors.query.QueryEngine;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.apache.calcite.rel.type.RelDataType.PRECISION_NOT_SPECIFIED;
+import static org.apache.calcite.rel.type.RelDataType.SCALE_NOT_SPECIFIED;
+
+/**
+ * Test query metadata.
+ */
+public class QueryMetadataIntegrationTest extends AbstractBasicIntegrationTest {
+ /** */
+ @Test
+ public void testJoin() throws Exception {
+ executeSql("CREATE TABLE tbl1 (id DECIMAL(10, 2), val VARCHAR, val2 BIGINT, ts TIMESTAMP(14), PRIMARY KEY(id, val))");
+ executeSql("CREATE TABLE tbl2 (id DECIMAL(10, 2), val VARCHAR, val2 BIGINT, ts TIMESTAMP(14), PRIMARY KEY(id, val))");
+
+ checker("select * from tbl1 inner join tbl2 on (tbl1.id > tbl2.id and tbl1.id <> ?) " +
+ "where tbl1.id > ? and tbl2.val like ? or tbl1.ts = ?")
+ .addMeta(
+ builder -> builder
+ .add("PUBLIC", "TBL1", BigDecimal.class, "ID", 10, 2, false)
+ .add("PUBLIC", "TBL1", String.class, "VAL", true)
+ .add("PUBLIC", "TBL1", Long.class, "VAL2", 19, 0, true)
+ .add("PUBLIC", "TBL1", java.sql.Timestamp.class, "TS", 0, SCALE_NOT_SPECIFIED, true)
+ .add("PUBLIC", "TBL2", BigDecimal.class, "ID", 10, 2, false)
+ .add("PUBLIC", "TBL2", String.class, "VAL", true)
+ .add("PUBLIC", "TBL2", Long.class, "VAL2", 19, 0, true)
+ .add("PUBLIC", "TBL2", java.sql.Timestamp.class, "TS", 0, SCALE_NOT_SPECIFIED, true),
+ builder -> builder
+ .add(BigDecimal.class, 10, 2)
+ .add(BigDecimal.class, 10, 2)
+ .add(String.class)
+ .add(java.sql.Timestamp.class, 0, SCALE_NOT_SPECIFIED)
+ ).check();
+ }
+
+ /** */
+ @Test
+ public void testMultipleConditions() throws Exception {
+ executeSql("CREATE TABLE tbl (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+ executeSql("CREATE INDEX tbl_val_idx ON tbl(val)");
+
+ checker("select * from tbl where id in (?, ?) or (id > ? and id <= ?) or (val <> ?)")
+ .addMeta(
+ builder -> builder
+ .add("PUBLIC", "TBL", Long.class, "ID", 19, 0, true)
+ .add("PUBLIC", "TBL", String.class, "VAL", true),
+ builder -> builder
+ .add(Long.class, 19, 0)
+ .add(Long.class, 19, 0)
+ .add(Long.class, 19, 0)
+ .add(Long.class, 19, 0)
+ .add(String.class)
+ ).check();
+ }
+
+ /** */
+ @Test
+ public void testMultipleQueries() throws Exception {
+ executeSql("CREATE TABLE tbl (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+ executeSql("CREATE INDEX tbl_val_idx ON tbl(val)");
+
+ checker("insert into tbl(id, val) values (?, ?); select * from tbl where id > ?")
+ .addMeta(
+ builder -> builder
+ .add(null, null, long.class, "ROWCOUNT", 19, 0, false),
+ builder -> builder
+ .add(Long.class, 19, 0)
+ .add(String.class)
+ )
+ .addMeta(
+ builder -> builder
+ .add("PUBLIC", "TBL", Long.class, "ID", 19, 0, true)
+ .add("PUBLIC", "TBL", String.class, "VAL", true),
+ builder -> builder
+ .add(Long.class, 19, 0)
+ )
+ .check();
+ }
+
+ /** */
+ @Test
+ public void testDml() throws Exception {
+ executeSql("CREATE TABLE tbl1 (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+ executeSql("CREATE TABLE tbl2 (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+
+ checker("insert into tbl1(id, val) values (?, ?)")
+ .addMeta(
+ builder -> builder
+ .add(null, null, long.class, "ROWCOUNT", 19, 0, false),
+ builder -> builder
+ .add(Long.class, 19, 0)
+ .add(String.class)
+ )
+ .check();
+
+ checker("insert into tbl2(id) select id from tbl1 where id > ? and val in (?, ?)")
+ .addMeta(
+ builder -> builder
+ .add(null, null, long.class, "ROWCOUNT", 19, 0, false),
+ builder -> builder
+ .add(Long.class, 19, 0)
+ .add(String.class)
+ .add(String.class)
+ )
+ .check();
+
+ checker("update tbl2 set val = ? where id in (?, ?)")
+ .addMeta(
+ builder -> builder
+ .add(null, null, long.class, "ROWCOUNT", 19, 0, false),
+ builder -> builder
+ .add(String.class)
+ .add(Long.class, 19, 0)
+ .add(Long.class, 19, 0)
+ )
+ .check();
+ }
+
+ /** */
+ @Test
+ public void testDdl() throws Exception {
+ executeSql("CREATE TABLE tbl1 (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+
+ checker("CREATE TABLE tbl2 (id BIGINT, val VARCHAR, PRIMARY KEY(id))")
+ .addMeta(builder -> {}, builder -> {})
+ .check();
+
+ // Metadata of create as select is always empty, because it is impossible to validate insert
+ // query without creating table.
+ checker("CREATE TABLE tbl2 (id, val) AS SELECT id, val FROM tbl1 WHERE id > ?")
+ .addMeta(builder -> {}, builder -> {})
+ .check();
+ }
+
+ /** */
+ @Test
+ public void testExplain() throws Exception {
+ executeSql("CREATE TABLE tbl (id BIGINT, val VARCHAR, PRIMARY KEY(id))");
+
+ checker("explain plan for select * from tbl where id in (?, ?) or (id > ? and id <= ?) or (val <> ?)")
+ .addMeta(
+ builder -> builder
+ .add(null, null, String.class, "PLAN", false),
+ builder -> {}
+ ).check();
+ }
+
+ /** */
+ private MetadataChecker checker(String sql) {
+ return new MetadataChecker(queryEngine(client), sql);
+ }
+
+ /** */
+ private static class MetadataChecker {
+ /** */
+ private final QueryEngine qryEngine;
+
+ /** */
+ private final String sql;
+
+ /** */
+ private final String schema;
+
+ /** */
+ private final List<T2<List<GridQueryFieldMetadata>, List<GridQueryFieldMetadata>>> expected = new ArrayList<>();
+
+ /** */
+ private int paramOffset;
+
+ /** */
+ public MetadataChecker(QueryEngine qryEngine, String sql) {
+ this(qryEngine, sql, "PUBLIC");
+ }
+
+ /** */
+ public MetadataChecker(QueryEngine qryEngine, String sql, String schema) {
+ this.qryEngine = qryEngine;
+ this.sql = sql;
+ this.schema = schema;
+ }
+
+ /** */
+ public MetadataChecker addMeta(Consumer<MetadataBuilder> resultMeta, Consumer<MetadataBuilder> paramMeta) {
+ MetadataBuilder rmBuilder = new MetadataBuilder();
+ resultMeta.accept(rmBuilder);
+
+ MetadataBuilder prmBuilder = new MetadataBuilder(paramOffset);
+ paramMeta.accept(prmBuilder);
+ List<GridQueryFieldMetadata> paramMetaRes = prmBuilder.build();
+ paramOffset += paramMetaRes.size();
+
+ expected.add(new T2<>(rmBuilder.build(), paramMetaRes));
+
+ return this;
+ }
+
+ /** */
+ public void check() throws Exception {
+ List<List<GridQueryFieldMetadata>> actualRsMeta = qryEngine.resultSetMetaData(null, schema, sql);
+ List<List<GridQueryFieldMetadata>> actualParamMeta = qryEngine.parameterMetaData(null, schema, sql);
+
+ assertEquals(expected.size(), actualRsMeta.size());
+ assertEquals(expected.size(), actualParamMeta.size());
+
+ for (int i = 0; i < expected.size(); ++i) {
+ checkMeta(expected.get(i).getKey(), actualRsMeta.get(i));
+ checkMeta(expected.get(i).getValue(), actualParamMeta.get(i));
+ }
+ }
+
+ /** */
+ private void checkMeta(List<GridQueryFieldMetadata> exp, List<GridQueryFieldMetadata> actual) {
+ assertEquals(exp.size(), actual.size());
+
+ for (int i = 0; i < actual.size(); ++i) {
+ GridQueryFieldMetadata actualMeta = actual.get(i);
+ GridQueryFieldMetadata expMeta = exp.get(i);
+
+ assertEquals(expMeta.schemaName(), actualMeta.schemaName());
+ assertEquals(expMeta.typeName(), actualMeta.typeName());
+ assertEquals(expMeta.fieldName(), actualMeta.fieldName());
+ assertEquals(expMeta.fieldTypeName(), actualMeta.fieldTypeName());
+ assertEquals(expMeta.nullability(), actualMeta.nullability());
+ assertEquals(expMeta.scale(), actualMeta.scale());
+ assertEquals(expMeta.precision(), actualMeta.precision());
+ }
+ }
+ }
+
+ /** */
+ private static class MetadataBuilder {
+ /** */
+ private final List<GridQueryFieldMetadata> fields = new ArrayList<>();
+
+ /** */
+ private final int fieldOffset;
+
+ /** */
+ MetadataBuilder() {
+ this(0);
+ }
+
+ /** */
+ MetadataBuilder(int fieldOffset) {
+ this.fieldOffset = fieldOffset;
+ }
+
+ /** */
+ MetadataBuilder add(Class<?> fieldClass) {
+ add(fieldClass, PRECISION_NOT_SPECIFIED, SCALE_NOT_SPECIFIED);
+ return this;
+ }
+
+ /** */
+ MetadataBuilder add(Class<?> fieldClass, int precision, int scale) {
+ add(null, null, fieldClass, "?" + (fields.size() + fieldOffset), precision, scale, true);
+ return this;
+ }
+
+ /** */
+ MetadataBuilder add(String schemaName, String typeName, Class<?> fieldClass, String fieldName) {
+ add(schemaName, typeName, fieldClass, fieldName, true);
+ return this;
+ }
+
+ /** */
+ MetadataBuilder add(
+ String schemaName,
+ String typeName,
+ Class<?> fieldClass,
+ String fieldName,
+ boolean isNullable
+ ) {
+ add(schemaName, typeName, fieldClass, fieldName, PRECISION_NOT_SPECIFIED, SCALE_NOT_SPECIFIED, isNullable);
+ return this;
+ }
+
+ /** */
+ MetadataBuilder add(
+ String schemaName,
+ String typeName,
+ Class<?> fieldClass,
+ String fieldName,
+ int precision,
+ int scale,
+ boolean isNullable
+ ) {
+ GridQueryFieldMetadata mocked = Mockito.mock(GridQueryFieldMetadata.class);
+
+ Mockito.when(mocked.schemaName()).thenReturn(schemaName);
+ Mockito.when(mocked.typeName()).thenReturn(typeName);
+ Mockito.when(mocked.fieldName()).thenReturn(fieldName);
+ Mockito.when(mocked.fieldTypeName()).thenReturn(fieldClass.getName());
+ Mockito.when(mocked.precision()).thenReturn(precision);
+ Mockito.when(mocked.scale()).thenReturn(scale);
+ Mockito.when(mocked.nullability())
+ .thenReturn(isNullable ? ResultSetMetaData.columnNullable : ResultSetMetaData.columnNoNulls);
+
+ fields.add(mocked);
+
+ return this;
+ }
+
+ /** */
+ List<GridQueryFieldMetadata> build() {
+ return Collections.unmodifiableList(fields);
+ }
+ }
+
+ /** */
+ private QueryEngine queryEngine(IgniteEx ign) {
+ return Commons.lookupComponent(ign.context(), QueryEngine.class);
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java
index c5e4bc017fe..97504e94ca1 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java
@@ -18,9 +18,12 @@
package org.apache.ignite.internal.processors.query.calcite.jdbc;
import java.io.Serializable;
+import java.math.BigDecimal;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.DriverManager;
+import java.sql.JDBCType;
+import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -346,6 +349,52 @@ public class JdbcQueryTest extends GridCommonAbstractTest {
stmt.close();
}
+ /**
+ * @throws SQLException If failed.
+ */
+ @Test
+ public void testParametersMetadata() throws Exception {
+ stmt.execute("CREATE TABLE Person(id BIGINT, PRIMARY KEY(id), name VARCHAR, amount DECIMAL(10,2))");
+
+ try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO Person VALUES (?, ?, ?)")) {
+ ParameterMetaData meta = stmt.getParameterMetaData();
+
+ assertEquals(3, meta.getParameterCount(), 3);
+
+ assertEquals(Long.class.getName(), meta.getParameterClassName(1));
+ assertEquals(String.class.getName(), meta.getParameterClassName(2));
+ assertEquals(BigDecimal.class.getName(), meta.getParameterClassName(3));
+
+ assertEquals(JDBCType.valueOf(Types.BIGINT).getName(), meta.getParameterTypeName(1));
+ assertEquals(JDBCType.valueOf(Types.VARCHAR).getName(), meta.getParameterTypeName(2));
+ assertEquals(JDBCType.valueOf(Types.DECIMAL).getName(), meta.getParameterTypeName(3));
+
+ assertEquals(Types.BIGINT, meta.getParameterType(1));
+ assertEquals(Types.VARCHAR, meta.getParameterType(2));
+ assertEquals(Types.DECIMAL, meta.getParameterType(3));
+
+ assertEquals(19, meta.getPrecision(1));
+ assertEquals(-1, meta.getPrecision(2));
+ assertEquals(10, meta.getPrecision(3));
+
+ assertEquals(0, meta.getScale(1));
+ assertEquals(Integer.MIN_VALUE, meta.getScale(2));
+ assertEquals(2, meta.getScale(3));
+
+ assertEquals(ParameterMetaData.parameterNullable, meta.isNullable(1));
+ assertEquals(ParameterMetaData.parameterNullable, meta.isNullable(2));
+ assertEquals(ParameterMetaData.parameterNullable, meta.isNullable(3));
+
+ assertEquals(ParameterMetaData.parameterModeIn, meta.getParameterMode(1));
+ assertEquals(ParameterMetaData.parameterModeIn, meta.getParameterMode(2));
+ assertEquals(ParameterMetaData.parameterModeIn, meta.getParameterMode(3));
+
+ assertTrue(meta.isSigned(1));
+ assertTrue(meta.isSigned(2));
+ assertTrue(meta.isSigned(3));
+ }
+ }
+
/** Some object to store. */
private static class ObjectToStore implements Serializable {
/** */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlannerTest.java
index 19a2d8595ef..26bb1b06133 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlannerTest.java
@@ -166,7 +166,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -274,8 +274,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
-
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
plan.init(this::intermediateMapping, Commons.mapContext(F.first(nodes), AffinityTopologyVersion.NONE));
@@ -501,7 +500,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -720,7 +719,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -803,7 +802,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -883,7 +882,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -965,7 +964,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
@@ -1041,7 +1040,7 @@ public class PlannerTest extends AbstractPlannerTest {
assertNotNull(phys);
- MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null);
+ MultiStepPlan plan = new MultiStepQueryPlan(new QueryTemplate(new Splitter().go(phys)), null, null);
assertNotNull(plan);
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 ded1524ad90..c6f061890c9 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
@@ -41,6 +41,7 @@ import org.apache.ignite.internal.processors.query.calcite.integration.KillComma
import org.apache.ignite.internal.processors.query.calcite.integration.KillQueryCommandDdlIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.MetadataIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.QueryEngineConfigurationIntegrationTest;
+import org.apache.ignite.internal.processors.query.calcite.integration.QueryMetadataIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.RunningQueriesIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.SearchSargOnIndexIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.ServerStatisticsIntegrationTest;
@@ -107,6 +108,7 @@ import org.junit.runners.Suite;
QueryEngineConfigurationIntegrationTest.class,
SearchSargOnIndexIntegrationTest.class,
KeepBinaryIntegrationTest.class,
+ QueryMetadataIntegrationTest.class
})
public class IntegrationTestSuite {
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
index c632bccf78b..858a1b63f8b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
@@ -305,7 +305,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
setupQuery(qry);
try {
- List<GridQueryFieldMetadata> meta = conn.ignite().context().query().getIndexing().resultMetaData(conn.schemaName(), qry);
+ List<GridQueryFieldMetadata> meta = conn.ignite().context().query().resultSetMetaData(qry, null);
if (meta == null)
return null;
@@ -366,7 +366,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
setupQuery(qry);
try {
- List<JdbcParameterMeta> params = conn.ignite().context().query().getIndexing().parameterMetaData(conn.schemaName(), qry);
+ List<JdbcParameterMeta> params = conn.ignite().context().query().parameterMetaData(qry, null);
return new JdbcThinParameterMetadata(params);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java
index f1e1f915255..5f953499560 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java
@@ -22,6 +22,8 @@ import java.sql.SQLException;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.internal.binary.BinaryReaderExImpl;
import org.apache.ignite.internal.binary.BinaryWriterExImpl;
+import org.apache.ignite.internal.jdbc.thin.JdbcThinUtils;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
import org.apache.ignite.internal.util.typedef.internal.S;
/**
@@ -75,6 +77,18 @@ public class JdbcParameterMeta implements JdbcRawBinarylizable {
mode = meta.getParameterMode(order);
}
+ /** */
+ public JdbcParameterMeta(GridQueryFieldMetadata meta) {
+ isNullable = meta.nullability();
+ signed = true;
+ precision = meta.precision();
+ scale = meta.scale();
+ type = JdbcThinUtils.type(meta.fieldTypeName());
+ typeName = JdbcThinUtils.typeName(meta.fieldTypeName());
+ typeClass = meta.fieldTypeName();
+ mode = ParameterMetaData.parameterModeIn;
+ }
+
/**
* @return Nullable mode.
*/
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index 10937a9c393..f0be798624c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -1290,8 +1290,7 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
setupQuery(qry, schemaName);
try {
- List<JdbcParameterMeta> meta = connCtx.kernalContext().query().getIndexing().
- parameterMetaData(schemaName, qry);
+ List<JdbcParameterMeta> meta = connCtx.kernalContext().query().parameterMetaData(qry, cliCtx);
JdbcMetaParamsResult res = new JdbcMetaParamsResult(meta);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
index 3833992adf9..06fda201a2d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
@@ -98,7 +98,7 @@ class JdbcRequestHandlerWorker extends GridWorker {
finally {
// Notify indexing that this worker is being stopped.
try {
- ctx.query().getIndexing().onClientDisconnect();
+ ctx.query().onClientDisconnect();
}
catch (Exception ignored) {
// No-op.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
index 58bea940164..5415395ff7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
@@ -766,7 +766,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
SqlFieldsQueryEx qry = makeQuery(schema, sql);
- List<JdbcParameterMeta> params = ctx.query().getIndexing().parameterMetaData(schema, qry);
+ List<JdbcParameterMeta> params = ctx.query().parameterMetaData(qry, cliCtx);
byte[] typeIds = new byte[params.size()];
@@ -801,7 +801,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
SqlFieldsQueryEx qry = makeQuery(schema, sql);
- List<GridQueryFieldMetadata> columns = ctx.query().getIndexing().resultMetaData(schema, qry);
+ List<GridQueryFieldMetadata> columns = ctx.query().resultSetMetaData(qry, cliCtx);
Collection<OdbcColumnMeta> meta = OdbcUtils.convertMetadata(columns);
OdbcQueryGetResultsetMetaResult res = new OdbcQueryGetResultsetMetaResult(meta);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
index a9b18682ff3..4a9b5080ea3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
@@ -98,7 +98,7 @@ class OdbcRequestHandlerWorker extends GridWorker {
finally {
// Notify indexing that this worker is being stopped.
try {
- ctx.query().getIndexing().onClientDisconnect();
+ ctx.query().onClientDisconnect();
}
catch (Exception e) {
// No-op.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index fd01dc62aeb..e8f5d4d3711 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import javax.cache.Cache;
import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
@@ -94,6 +95,7 @@ import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.cache.query.IndexQueryDesc;
import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcParameterMeta;
import org.apache.ignite.internal.processors.platform.PlatformContext;
import org.apache.ignite.internal.processors.platform.PlatformProcessor;
import org.apache.ignite.internal.processors.query.aware.IndexBuildStatusStorage;
@@ -2887,6 +2889,18 @@ public class GridQueryProcessor extends GridProcessorAdapter {
INDEXING.module() + " to classpath or moving it from 'optional' to 'libs' folder).");
}
+ /**
+ * @throws IgniteException If indexing is disabled.
+ */
+ private void checkxModuleEnabled() throws IgniteException {
+ if (!moduleEnabled()) {
+ throw new IgniteException("Failed to execute query because indexing is disabled and no query engine is " +
+ "configured (consider adding module " + INDEXING.module() + " to classpath or moving it " +
+ "from 'optional' to 'libs' folder or configuring any query engine with " +
+ "IgniteConfiguration.SqlConfiguration.QueryEnginesConfiguration property).");
+ }
+ }
+
/**
* Execute update on DHT node (i.e. when it is possible to execute and update on all nodes independently).
*
@@ -3054,12 +3068,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
GridCacheQueryType qryType,
@Nullable final GridQueryCancel cancel
) {
- if (!moduleEnabled()) {
- throw new IgniteException("Failed to execute query because indexing is disabled and no query engine is " +
- "configured (consider adding module " + INDEXING.module() + " to classpath or moving it " +
- "from 'optional' to 'libs' folder or configuring any query engine with " +
- "IgniteConfiguration.SqlConfiguration.QueryEnginesConfiguration property).");
- }
+ checkxModuleEnabled();
if (qry.isDistributedJoins() && qry.getPartitions() != null)
throw new CacheException("Using both partitions and distributed JOINs is not supported for the same query");
@@ -3121,6 +3130,62 @@ public class GridQueryProcessor extends GridProcessorAdapter {
});
}
+ /** */
+ public List<JdbcParameterMeta> parameterMetaData(
+ final SqlFieldsQuery qry,
+ @Nullable final SqlClientContext cliCtx
+ ) {
+ checkxModuleEnabled();
+
+ return executeQuerySafe(null, () -> {
+ final String schemaName = qry.getSchema() == null ? QueryUtils.DFLT_SCHEMA : qry.getSchema();
+
+ QueryEngine qryEngine = engineForQuery(cliCtx, qry);
+
+ if (qryEngine != null) {
+ List<List<GridQueryFieldMetadata>> meta = qryEngine.parameterMetaData(
+ QueryContext.of(qry, cliCtx),
+ schemaName,
+ qry.getSql());
+
+ return meta.stream()
+ .flatMap(m -> m.stream().map(JdbcParameterMeta::new))
+ .collect(Collectors.toList());
+ }
+ else
+ return idx.parameterMetaData(schemaName, qry);
+
+ });
+ }
+
+ /** */
+ public List<GridQueryFieldMetadata> resultSetMetaData(
+ final SqlFieldsQuery qry,
+ @Nullable final SqlClientContext cliCtx
+ ) {
+ checkxModuleEnabled();
+
+ return executeQuerySafe(null, () -> {
+ final String schemaName = qry.getSchema() == null ? QueryUtils.DFLT_SCHEMA : qry.getSchema();
+
+ QueryEngine qryEngine = engineForQuery(cliCtx, qry);
+
+ if (qryEngine != null) {
+ List<List<GridQueryFieldMetadata>> meta = qryEngine.resultSetMetaData(
+ QueryContext.of(qry, cliCtx),
+ schemaName,
+ qry.getSql());
+
+ if (meta.size() == 1)
+ return meta.get(0);
+
+ return null;
+ }
+ else
+ return idx.resultMetaData(schemaName, qry);
+ });
+ }
+
/**
* @param cctx Cache context.
* @return Schema name.
@@ -3957,6 +4022,16 @@ public class GridQueryProcessor extends GridProcessorAdapter {
desc.validateKeyAndValue(key, val);
}
+ /**
+ * Performs necessary actions on disconnect of a stateful client (say, one associated with a transaction).
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ public void onClientDisconnect() throws IgniteCheckedException {
+ if (idx != null)
+ idx.onClientDisconnect();
+ }
+
/**
* @param ver Version.
*/
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NoOpQueryEngine.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NoOpQueryEngine.java
index 4730f65bca0..51ea2abb275 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NoOpQueryEngine.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NoOpQueryEngine.java
@@ -45,11 +45,29 @@ public class NoOpQueryEngine extends GridProcessorAdapter implements QueryEngine
return Collections.emptyList();
}
+ /** {@inheritDoc} */
+ @Override public List<List<GridQueryFieldMetadata>> parameterMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String qry
+ ) throws IgniteSQLException {
+ return Collections.emptyList();
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<List<GridQueryFieldMetadata>> resultSetMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String qry
+ ) throws IgniteSQLException {
+ return Collections.emptyList();
+ }
+
/** {@inheritDoc} */
@Override public List<FieldsQueryCursor<List<?>>> queryBatched(
@Nullable QueryContext ctx,
String schemaName,
- String query,
+ String qry,
List<Object[]> batchedParams
) throws IgniteSQLException {
return Collections.emptyList();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEngine.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEngine.java
index 003f37339a1..d6ffeef4507 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEngine.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEngine.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.query;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
-
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.internal.processors.GridProcessor;
import org.jetbrains.annotations.Nullable;
@@ -42,8 +41,33 @@ public interface QueryEngine extends GridProcessor {
String schemaName,
String qry,
Object... params
- )
- throws IgniteSQLException;
+ ) throws IgniteSQLException;
+
+ /**
+ * @param ctx Query context, may be null.
+ * @param schemaName Schema name.
+ * @param qry Query.
+ * @return List of queries' parameters metadata. Size of list depends on number of distinct queries in {@code qry}.
+ * @throws IgniteSQLException If failed.
+ */
+ List<List<GridQueryFieldMetadata>> parameterMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String qry
+ ) throws IgniteSQLException;
+
+ /**
+ * @param ctx Query context, may be null.
+ * @param schemaName Schema name.
+ * @param qry Query.
+ * @return List of queries' result sets metadata. Size of list depends on number of distinct queries in {@code qry}.
+ * @throws IgniteSQLException If failed.
+ */
+ List<List<GridQueryFieldMetadata>> resultSetMetaData(
+ @Nullable QueryContext ctx,
+ String schemaName,
+ String qry
+ ) throws IgniteSQLException;
/**
* @param ctx Query context, may be null.