From 57b9c0277fce0f2555b856a749acc3dc56b7cff3 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Thu, 17 Aug 2023 18:18:32 +0300 Subject: [PATCH 01/22] Adjust joins cost estimation Signed-off-by: Sasha Syrotenko --- .../jet/sql/impl/CalciteSqlOptimizer.java | 1 + .../jet/sql/impl/OptimizerContext.java | 4 ++- .../hazelcast/jet/sql/impl/opt/cost/Cost.java | 9 +++++ .../opt/physical/JoinHashPhysicalRel.java | 10 ++++-- .../physical/JoinNestedLoopPhysicalRel.java | 33 +++++++++++++++++++ .../sql/impl/schema/HazelcastSchemaUtils.java | 12 +++---- .../jet/sql/impl/schema/HazelcastTable.java | 33 +++++++++++++++---- .../sql/impl/opt/OptimizerTestSupport.java | 7 +--- .../impl/parse/ParserNameResolutionTest.java | 1 + .../sql/impl/parse/ParserOperationsTest.java | 1 + .../jet/sql/impl/parse/UnparseTest.java | 1 + 11 files changed, 90 insertions(+), 22 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java index d87d1e7f4e40..36326eeb3ba2 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java @@ -310,6 +310,7 @@ public SqlPlan prepare(OptimizationTask task) { int memberCount = nodeEngine.getClusterService().getSize(MemberSelectors.DATA_MEMBER_SELECTOR); OptimizerContext context = OptimizerContext.create( + nodeEngine.getHazelcastInstance(), task.getSchema(), task.getSearchPaths(), task.getArguments(), diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/OptimizerContext.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/OptimizerContext.java index 60da01a81e0f..56918ea6116b 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/OptimizerContext.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/OptimizerContext.java @@ -17,6 +17,7 @@ package com.hazelcast.jet.sql.impl; import com.google.common.collect.ImmutableList; +import com.hazelcast.core.HazelcastInstance; import com.hazelcast.jet.sql.impl.opt.cost.CostFactory; import com.hazelcast.jet.sql.impl.opt.metadata.HazelcastRelMdBoundedness; import com.hazelcast.jet.sql.impl.opt.metadata.HazelcastRelMdPrunability; @@ -107,6 +108,7 @@ private OptimizerContext( * @return Context. */ public static OptimizerContext create( + HazelcastInstance hz, SqlCatalog schema, List> searchPaths, List arguments, @@ -114,7 +116,7 @@ public static OptimizerContext create( IMapResolver iMapResolver ) { // Resolve tables. - HazelcastSchema rootSchema = HazelcastSchemaUtils.createRootSchema(schema); + HazelcastSchema rootSchema = HazelcastSchemaUtils.createRootSchema(hz, schema); return create(rootSchema, searchPaths, arguments, memberCount, iMapResolver); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java index 0580f74d9cd3..4b1caa22c1f3 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java @@ -40,6 +40,15 @@ public class Cost implements RelOptCost { public static final Cost HUGE = new Cost(Double.MAX_VALUE / 100, Double.MAX_VALUE / 100, Double.MAX_VALUE / 100); public static final Cost INFINITY = new Cost(Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + /** + * Multiplier to display hash table building actions: + * - row hash computation; + * - probe hash table; + * - worst case scenario : walk through hash chain and compare with each element (assessed as 3 ops in average); + * - add the hash to the bucket. + */ + public static final double HASH_JOIN_MULTIPLIER = 6; + private final double rows; private final double cpu; private final double network; diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 26177efb7279..47c3346e37ba 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -16,6 +16,7 @@ package com.hazelcast.jet.sql.impl.opt.physical; +import com.hazelcast.jet.sql.impl.opt.cost.Cost; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptPlanner; @@ -28,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class JoinHashPhysicalRel extends JoinPhysicalRel { - private static final double COST_FACTOR = 1.1; JoinHashPhysicalRel( RelOptCluster cluster, @@ -61,6 +61,12 @@ public Join copy( @Override @Nullable public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { - return super.computeSelfCost(planner, mq).multiplyBy(COST_FACTOR); + double leftRowCount = mq.getRowCount(getLeft()); + double rightRowCount = mq.getRowCount(getRight()); + double rowCount = leftRowCount + rightRowCount; + + double cpu = Cost.HASH_JOIN_MULTIPLIER * leftRowCount + /* TODO: selectivity */ rightRowCount; + + return planner.getCostFactory().makeCost(rowCount, cpu, 0.); } } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 8ac5d73653e3..bdef47fe9efc 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -22,16 +22,20 @@ import com.hazelcast.jet.sql.impl.schema.HazelcastTable; import com.hazelcast.jet.sql.impl.validate.HazelcastSqlOperatorTable; import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptCost; +import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Join; import org.apache.calcite.rel.core.JoinInfo; import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.util.ImmutableIntList; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; @@ -109,6 +113,35 @@ public V accept(CreateDagVisitor visitor) { return visitor.onNestedLoopJoin(this); } + /** + * Copied from {@link org.apache.calcite.rel.core.Correlate#computeSelfCost(RelOptPlanner, RelMetadataQuery)} + */ + @Override + @Nullable + public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { + double rowCount = mq.getRowCount(this); + + final double rightRowCount = right.estimateRowCount(mq); + final double leftRowCount = left.estimateRowCount(mq); + if (Double.isInfinite(leftRowCount) || Double.isInfinite(rightRowCount)) { + return planner.getCostFactory().makeInfiniteCost(); + } + + Double restartCount = mq.getRowCount(getLeft()); + if (restartCount == null) { + return planner.getCostFactory().makeInfiniteCost(); + } + // RelMetadataQuery.getCumulativeCost(getRight()); does not work for + // RelSubset, so we ask planner to cost-estimate right relation + RelOptCost rightCost = planner.getCost(getRight(), mq); + if (rightCost == null) { + return planner.getCostFactory().makeInfiniteCost(); + } + RelOptCost rescanCost = rightCost.multiplyBy(Math.max(1.0, restartCount - 1)); + + return planner.getCostFactory().makeCost(rowCount + leftRowCount, 0, 0).plus(rescanCost); + } + @Override public Join copy( RelTraitSet traitSet, diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java index 72434452c6db..3d3d80167789 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java @@ -16,9 +16,11 @@ package com.hazelcast.jet.sql.impl.schema; +import com.hazelcast.core.HazelcastInstance; import com.hazelcast.sql.impl.QueryUtils; import com.hazelcast.sql.impl.schema.SqlCatalog; import com.hazelcast.sql.impl.schema.Table; +import com.hazelcast.sql.impl.schema.map.PartitionedMapTable; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.Statistic; @@ -56,7 +58,7 @@ public static HazelcastSchema createCatalog(Schema schema) { * * @return Top-level schema. */ - public static HazelcastSchema createRootSchema(SqlCatalog catalog) { + public static HazelcastSchema createRootSchema(HazelcastInstance hz, SqlCatalog catalog) { // Create schemas. Map schemaMap = new HashMap<>(); @@ -68,11 +70,9 @@ public static HazelcastSchema createRootSchema(SqlCatalog catalog) { for (Map.Entry tableEntry : currentSchemaEntry.getValue().entrySet()) { String tableName = tableEntry.getKey(); Table table = tableEntry.getValue(); - - HazelcastTable convertedTable = new HazelcastTable( - table, - createTableStatistic(table) - ); + HazelcastTable convertedTable = table instanceof PartitionedMapTable + ? new HazelcastTable(table, hz) + : new HazelcastTable(table, createTableStatistic(table)); schemaTables.put(tableName, convertedTable); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java index 02b96ea0258f..5ff5b1295fc0 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java @@ -16,6 +16,7 @@ package com.hazelcast.jet.sql.impl.schema; +import com.hazelcast.core.HazelcastInstance; import com.hazelcast.jet.sql.impl.opt.OptUtils; import com.hazelcast.jet.sql.impl.opt.common.CalcIntoScanRule; import com.hazelcast.jet.sql.impl.opt.cost.CostUtils; @@ -46,6 +47,7 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; +import java.util.function.Supplier; import static java.util.stream.Collectors.joining; @@ -84,29 +86,39 @@ */ public class HazelcastTable extends AbstractTable { + private final HazelcastInstance instance; private final Table target; - private final Statistic statistic; + private final Supplier statisticSupplier; private final RexNode filter; private List projects; private RelDataType rowType; private final Set hiddenFieldNames = new HashSet<>(); + public HazelcastTable(Table target, HazelcastInstance instance) { + this.instance = instance; + this.target = target; + this.statisticSupplier = () -> createTableStatistic(target, instance); + this.filter = null; + } + public HazelcastTable(Table target, Statistic statistic) { + this.instance = null; this.target = target; - this.statistic = statistic; + this.statisticSupplier = () -> statistic; this.filter = null; } private HazelcastTable( Table target, - Statistic statistic, + Supplier statisticSupplier, @Nonnull List projects, @Nullable RelDataType rowType, @Nullable RexNode filter ) { + this.instance = null; this.target = target; - this.statistic = statistic; + this.statisticSupplier = statisticSupplier; this.projects = projects; this.rowType = rowType == null ? computeRowType(projects) : rowType; this.filter = filter; @@ -127,11 +139,11 @@ private void initRowType() { } public HazelcastTable withProject(List projects, @Nullable RelDataType rowType) { - return new HazelcastTable(target, statistic, projects, rowType, filter); + return new HazelcastTable(target, statisticSupplier, projects, rowType, filter); } public HazelcastTable withFilter(RexNode filter) { - return new HazelcastTable(target, statistic, projects, rowType, filter); + return new HazelcastTable(target, statisticSupplier, projects, rowType, filter); } @Nonnull @@ -158,6 +170,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { @Override public Statistic getStatistic() { + Statistic statistic = statisticSupplier.get(); if (filter == null) { return statistic; } else { @@ -167,8 +180,9 @@ public Statistic getStatistic() { } } + @SuppressWarnings("DataFlowIssue") public double getTotalRowCount() { - return statistic.getRowCount(); + return statisticSupplier.get().getRowCount(); } public boolean isHidden(String fieldName) { @@ -212,12 +226,17 @@ private RelDataType computeRowType(List projects) { return new RelRecordType(StructKind.PEEK_FIELDS, typeFields, false); } + private static Statistic createTableStatistic(Table table, HazelcastInstance instance) { + return new HazelcastTableStatistic(instance.getMap(table.getSqlName()).size()); + } + /** * Statistics that takes into account the row count after the filter is applied. */ private final class AdjustedStatistic implements Statistic { private final Double rowCount; + private final Statistic statistic = statisticSupplier.get(); private AdjustedStatistic(Double rowCount) { this.rowCount = rowCount; diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/OptimizerTestSupport.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/OptimizerTestSupport.java index 71f6064e49d0..0e0a7b571c65 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/OptimizerTestSupport.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/OptimizerTestSupport.java @@ -314,12 +314,7 @@ protected static PlanRow parse(String input) { @Override public String toString() { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < level; i++) { - builder.append(" "); - } - builder.append(node); - return builder.toString(); + return " ".repeat(Math.max(0, level)) + node; } @Override diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserNameResolutionTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserNameResolutionTest.java index 9f491c4fe4e8..6ee228f24477 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserNameResolutionTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserNameResolutionTest.java @@ -224,6 +224,7 @@ private static OptimizerContext createContext() { List> searchPaths = QueryUtils.prepareSearchPaths(emptyList(), tableResolvers); return OptimizerContext.create( + instance(), new SqlCatalog(tableResolvers), searchPaths, emptyList(), diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserOperationsTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserOperationsTest.java index ed899484a8f4..0d41d0030814 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserOperationsTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/ParserOperationsTest.java @@ -206,6 +206,7 @@ private static OptimizerContext createContext() { List> searchPaths = QueryUtils.prepareSearchPaths(emptyList(), tableResolvers); return OptimizerContext.create( + instance(), new SqlCatalog(tableResolvers), searchPaths, emptyList(), diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/UnparseTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/UnparseTest.java index f5919c554c40..57f9ab4f4adb 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/UnparseTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/parse/UnparseTest.java @@ -91,6 +91,7 @@ private void checkQuery(String query) { private static OptimizerContext createContext() { return OptimizerContext.create( + instance(), new SqlCatalog(emptyList()), emptyList(), emptyList(), From 7d4410f2922e424acfc4eec10b2787b1ad9e439f Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 18 Aug 2023 08:05:35 +0300 Subject: [PATCH 02/22] Fix test Signed-off-by: Sasha Syrotenko --- .../jet/sql/impl/opt/prunability/RelPrunabilityTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java index a80debf8f320..fa857b153157 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java @@ -25,7 +25,7 @@ import com.hazelcast.jet.sql.impl.opt.physical.CalcPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.FullScanPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.IndexScanMapPhysicalRel; -import com.hazelcast.jet.sql.impl.opt.physical.JoinNestedLoopPhysicalRel; +import com.hazelcast.jet.sql.impl.opt.physical.JoinHashPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.PhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.SortPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.UnionPhysicalRel; @@ -221,7 +221,7 @@ public void test_joinAndCalc_areNotSupported() { ).getPhysical(); assertPlan(root, plan( - planRow(0, JoinNestedLoopPhysicalRel.class), + planRow(0, JoinHashPhysicalRel.class), planRow(1, FullScanPhysicalRel.class), planRow(1, FullScanPhysicalRel.class) )); From 187c05d4dc04692545b53077d498c38c64cc2588 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 18 Aug 2023 12:16:13 +0300 Subject: [PATCH 03/22] Introduce QueryPlanListener for further optimizer tests Signed-off-by: Sasha Syrotenko --- .../jet/sql/impl/CalciteSqlOptimizer.java | 5 ++++ .../jet/sql/impl/QueryPlanListener.java | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java index 36326eeb3ba2..766b4673d226 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java @@ -236,6 +236,7 @@ public class CalciteSqlOptimizer implements SqlOptimizer { private final IMapResolver iMapResolver; private final List tableResolvers; + private final List queryPlanListeners; private final PlanExecutor planExecutor; private final RelationsStorage relationsStorage; @@ -257,6 +258,7 @@ public CalciteSqlOptimizer(NodeEngine nodeEngine, QueryResultRegistry resultRegi nodeEngine.getHazelcastInstance().getConfig().getSecurityConfig().isEnabled() ); this.tableResolvers = Arrays.asList(tableResolverImpl, dataConnectionResolver); + this.queryPlanListeners = new ArrayList<>(); this.planExecutor = new PlanExecutor( nodeEngine, tableResolverImpl, @@ -802,6 +804,9 @@ private PhysicalRel optimize( if (fineLogOn) { logger.fine("After physical opt:\n" + RelOptUtil.toString(physicalRel)); } + + PhysicalRel finalPhysicalRel = physicalRel; + queryPlanListeners.forEach(l -> l.onQueryPlanBuilt(finalPhysicalRel)); return physicalRel; } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java new file mode 100644 index 000000000000..e79fd9effc2d --- /dev/null +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Hazelcast Inc. + * + * Licensed under the Hazelcast Community License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://hazelcast.com/hazelcast-community-license + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.jet.sql.impl; + +import com.hazelcast.jet.sql.impl.opt.physical.PhysicalRel; + +/** + * Listener which hooks finally optimized query plan by calling + * {@link QueryPlanListener#onQueryPlanBuilt} in {@link CalciteSqlOptimizer#optimize}. + */ +public interface QueryPlanListener { + void onQueryPlanBuilt(PhysicalRel rootRel); +} From 17754470e75e6ce649b8799016527c6a73a544ed Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 18 Aug 2023 14:12:40 +0300 Subject: [PATCH 04/22] Adjust cost model Signed-off-by: Sasha Syrotenko --- .../hazelcast/jet/sql/impl/opt/cost/Cost.java | 16 +++++++---- .../opt/physical/JoinHashPhysicalRel.java | 18 +++++++++++- .../physical/JoinNestedLoopPhysicalRel.java | 28 ++++++++++--------- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java index 4b1caa22c1f3..13c44c74dd00 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java @@ -42,12 +42,18 @@ public class Cost implements RelOptCost { /** * Multiplier to display hash table building actions: - * - row hash computation; - * - probe hash table; - * - worst case scenario : walk through hash chain and compare with each element (assessed as 3 ops in average); - * - add the hash to the bucket. + * - row hash computation; (estimate - 3 ops, the process itself is heavier from CPU ops POV) + * - probe hash table; (estimate - 1 op) + * - walk through hash chain (estimate - 1 op, assuming hash collision may happen) + * - and compare with each element; (estimate - 1 op, assuming hash collision may happen) + * - add the k-v to the table. (estimate - 1 op). */ - public static final double HASH_JOIN_MULTIPLIER = 6; + public static final double HASH_JOIN_MULTIPLIER = 7; + + /** + * Multiplier to display row comparison + */ + public static final double JOIN_ROW_CMP_MULTIPLIER = 2; private final double rows; private final double cpu; diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 47c3346e37ba..4e9cad8ba087 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -58,6 +58,18 @@ public Join copy( return new JoinHashPhysicalRel(getCluster(), traitSet, left, right, conditionExpr, joinType); } + /** + * Cost calculation of Hash Join relation. It does not rely on children cost. + *

+ * Hash Join algorithm is more advanced join algorithm, where it builds a hash table for left row set, and then + * compare each row from the right side + * Speaking of cost estimation, we are accounting the next properties: + * - row count is estimating ans L + R, because we traverse both sides once per JOIN. + * - same for CPU cost estimation multiplied by cost to build a hash table and left and right rows comparison. + *

+ * The perfect assumption also must include memory (what is important in case of hash table) and IO cost estimation, + * as well as a selectivity for a right row set. + */ @Override @Nullable public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { @@ -65,7 +77,11 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double rightRowCount = mq.getRowCount(getRight()); double rowCount = leftRowCount + rightRowCount; - double cpu = Cost.HASH_JOIN_MULTIPLIER * leftRowCount + /* TODO: selectivity */ rightRowCount; + // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. + double cpu = Cost.HASH_JOIN_MULTIPLIER + * leftRowCount + + /* TODO: selectivity */ rightRowCount + * Cost.JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(rowCount, cpu, 0.); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index bdef47fe9efc..672a1e1c1ae1 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.hazelcast.jet.sql.impl.HazelcastPhysicalScan; import com.hazelcast.jet.sql.impl.opt.OptUtils; +import com.hazelcast.jet.sql.impl.opt.cost.Cost; import com.hazelcast.jet.sql.impl.schema.HazelcastTable; import com.hazelcast.jet.sql.impl.validate.HazelcastSqlOperatorTable; import org.apache.calcite.plan.RelOptCluster; @@ -114,32 +115,33 @@ public V accept(CreateDagVisitor visitor) { } /** - * Copied from {@link org.apache.calcite.rel.core.Correlate#computeSelfCost(RelOptPlanner, RelMetadataQuery)} + * Cost calculation of Nested Loop Join relation. + * NLJ algorithm is simple algorithm, where for each left row we are traversing right row set. + * Speaking of cost estimation, we are accounting the next properties: + * - row count is estimating ans L * R, because for each left row we're probing full right row set; + * - same for CPU cost estimation multiplied by cost of row comparison. + * The perfect assumption also must include memory and IO cost estimation, and also + * a selectivity for a right row set. */ @Override @Nullable public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { - double rowCount = mq.getRowCount(this); - - final double rightRowCount = right.estimateRowCount(mq); - final double leftRowCount = left.estimateRowCount(mq); + final double leftRowCount = mq.getRowCount(left); + final double rightRowCount = mq.getRowCount(right); if (Double.isInfinite(leftRowCount) || Double.isInfinite(rightRowCount)) { return planner.getCostFactory().makeInfiniteCost(); } - Double restartCount = mq.getRowCount(getLeft()); - if (restartCount == null) { - return planner.getCostFactory().makeInfiniteCost(); - } - // RelMetadataQuery.getCumulativeCost(getRight()); does not work for - // RelSubset, so we ask planner to cost-estimate right relation RelOptCost rightCost = planner.getCost(getRight(), mq); if (rightCost == null) { return planner.getCostFactory().makeInfiniteCost(); } - RelOptCost rescanCost = rightCost.multiplyBy(Math.max(1.0, restartCount - 1)); - return planner.getCostFactory().makeCost(rowCount + leftRowCount, 0, 0).plus(rescanCost); + // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. + double rowsEstimate = leftRowCount * /* TODO: selectivity */ rightRowCount; + double cpuEstimate = Math.max(1.0, rowsEstimate - 1) * Cost.JOIN_ROW_CMP_MULTIPLIER; + + return planner.getCostFactory().makeCost(rowsEstimate, cpuEstimate, 0); } @Override From 48abe08a44b97dcd09078c55d32e8edf2d39aa66 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 18 Aug 2023 14:29:41 +0300 Subject: [PATCH 05/22] Brackets Signed-off-by: Sasha Syrotenko --- .../jet/sql/impl/opt/physical/JoinHashPhysicalRel.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 4e9cad8ba087..f7d877cfa693 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -78,9 +78,7 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double rowCount = leftRowCount + rightRowCount; // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. - double cpu = Cost.HASH_JOIN_MULTIPLIER - * leftRowCount - + /* TODO: selectivity */ rightRowCount + double cpu = (Cost.HASH_JOIN_MULTIPLIER * leftRowCount + /* TODO: selectivity */ rightRowCount) * Cost.JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(rowCount, cpu, 0.); From 98ad7362a78a1506059e68a3bb2c616311cc317a Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 18 Aug 2023 15:15:50 +0300 Subject: [PATCH 06/22] Revert test change back, and actually it is correct Signed-off-by: Sasha Syrotenko --- .../jet/sql/impl/opt/prunability/RelPrunabilityTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java index fa857b153157..a80debf8f320 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/prunability/RelPrunabilityTest.java @@ -25,7 +25,7 @@ import com.hazelcast.jet.sql.impl.opt.physical.CalcPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.FullScanPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.IndexScanMapPhysicalRel; -import com.hazelcast.jet.sql.impl.opt.physical.JoinHashPhysicalRel; +import com.hazelcast.jet.sql.impl.opt.physical.JoinNestedLoopPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.PhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.SortPhysicalRel; import com.hazelcast.jet.sql.impl.opt.physical.UnionPhysicalRel; @@ -221,7 +221,7 @@ public void test_joinAndCalc_areNotSupported() { ).getPhysical(); assertPlan(root, plan( - planRow(0, JoinHashPhysicalRel.class), + planRow(0, JoinNestedLoopPhysicalRel.class), planRow(1, FullScanPhysicalRel.class), planRow(1, FullScanPhysicalRel.class) )); From d1a1b1656de9dfef1bfdb6f21bc9440543b10eea Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Mon, 4 Sep 2023 11:18:04 +0300 Subject: [PATCH 07/22] Address review comments --- .../jet/sql/impl/CalciteSqlOptimizer.java | 5 +--- .../jet/sql/impl/QueryPlanListener.java | 27 ------------------- .../jet/sql/impl/schema/HazelcastTable.java | 19 ++++--------- 3 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java index 766b4673d226..c6af5f9c09a9 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/CalciteSqlOptimizer.java @@ -236,7 +236,6 @@ public class CalciteSqlOptimizer implements SqlOptimizer { private final IMapResolver iMapResolver; private final List tableResolvers; - private final List queryPlanListeners; private final PlanExecutor planExecutor; private final RelationsStorage relationsStorage; @@ -258,7 +257,6 @@ public CalciteSqlOptimizer(NodeEngine nodeEngine, QueryResultRegistry resultRegi nodeEngine.getHazelcastInstance().getConfig().getSecurityConfig().isEnabled() ); this.tableResolvers = Arrays.asList(tableResolverImpl, dataConnectionResolver); - this.queryPlanListeners = new ArrayList<>(); this.planExecutor = new PlanExecutor( nodeEngine, tableResolverImpl, @@ -805,8 +803,7 @@ private PhysicalRel optimize( logger.fine("After physical opt:\n" + RelOptUtil.toString(physicalRel)); } - PhysicalRel finalPhysicalRel = physicalRel; - queryPlanListeners.forEach(l -> l.onQueryPlanBuilt(finalPhysicalRel)); + // TODO[sasha]: capture final physical rel for listeners here. return physicalRel; } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java deleted file mode 100644 index e79fd9effc2d..000000000000 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/QueryPlanListener.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023 Hazelcast Inc. - * - * Licensed under the Hazelcast Community License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://hazelcast.com/hazelcast-community-license - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hazelcast.jet.sql.impl; - -import com.hazelcast.jet.sql.impl.opt.physical.PhysicalRel; - -/** - * Listener which hooks finally optimized query plan by calling - * {@link QueryPlanListener#onQueryPlanBuilt} in {@link CalciteSqlOptimizer#optimize}. - */ -public interface QueryPlanListener { - void onQueryPlanBuilt(PhysicalRel rootRel); -} diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java index 5ff5b1295fc0..69956e2041b3 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java @@ -85,8 +85,6 @@ * properties, thus making further optimization more complex. */ public class HazelcastTable extends AbstractTable { - - private final HazelcastInstance instance; private final Table target; private final Supplier statisticSupplier; private final RexNode filter; @@ -95,28 +93,21 @@ public class HazelcastTable extends AbstractTable { private RelDataType rowType; private final Set hiddenFieldNames = new HashSet<>(); - public HazelcastTable(Table target, HazelcastInstance instance) { - this.instance = instance; - this.target = target; - this.statisticSupplier = () -> createTableStatistic(target, instance); - this.filter = null; + public HazelcastTable(Table target, Statistic statistic) { + this(target, () -> statistic, null, null, null); } - public HazelcastTable(Table target, Statistic statistic) { - this.instance = null; - this.target = target; - this.statisticSupplier = () -> statistic; - this.filter = null; + public HazelcastTable(Table target, HazelcastInstance instance) { + this(target, () -> createTableStatistic(target, instance), null, null, null); } private HazelcastTable( Table target, Supplier statisticSupplier, - @Nonnull List projects, + List projects, @Nullable RelDataType rowType, @Nullable RexNode filter ) { - this.instance = null; this.target = target; this.statisticSupplier = statisticSupplier; this.projects = projects; From c4bd3fff67b6168e3b114f53a6bc357f883c854d Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Tue, 5 Sep 2023 13:46:46 +0300 Subject: [PATCH 08/22] Divide processed and produced rows --- .../impl/opt/physical/JoinHashPhysicalRel.java | 15 ++++++++------- .../opt/physical/JoinNestedLoopPhysicalRel.java | 7 +++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index f7d877cfa693..7ae84d2e45a7 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -64,8 +64,9 @@ public Join copy( * Hash Join algorithm is more advanced join algorithm, where it builds a hash table for left row set, and then * compare each row from the right side * Speaking of cost estimation, we are accounting the next properties: - * - row count is estimating ans L + R, because we traverse both sides once per JOIN. - * - same for CPU cost estimation multiplied by cost to build a hash table and left and right rows comparison. + * - produced row count is estimated as L * R, with assumption that the JOIN predicate has maximum selectivity. + * - processed row count is estimated as L + R, because we traverse both sides once per JOIN. + * - CPU cost estimation is a processed row multiplied by cost to build a hash table, and left and right rows comparison. *

* The perfect assumption also must include memory (what is important in case of hash table) and IO cost estimation, * as well as a selectivity for a right row set. @@ -75,12 +76,12 @@ public Join copy( public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double leftRowCount = mq.getRowCount(getLeft()); double rightRowCount = mq.getRowCount(getRight()); - double rowCount = leftRowCount + rightRowCount; - // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. - double cpu = (Cost.HASH_JOIN_MULTIPLIER * leftRowCount + /* TODO: selectivity */ rightRowCount) - * Cost.JOIN_ROW_CMP_MULTIPLIER; + double producedRowCount = leftRowCount * /* TODO: selectivity */ rightRowCount; + double processedRowCount = leftRowCount + /* TODO: selectivity */ rightRowCount; + + double cpu = Cost.HASH_JOIN_MULTIPLIER * processedRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; - return planner.getCostFactory().makeCost(rowCount, cpu, 0.); + return planner.getCostFactory().makeCost(producedRowCount, cpu, 0.); } } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 672a1e1c1ae1..32f3c4d41a7f 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -118,10 +118,9 @@ public V accept(CreateDagVisitor visitor) { * Cost calculation of Nested Loop Join relation. * NLJ algorithm is simple algorithm, where for each left row we are traversing right row set. * Speaking of cost estimation, we are accounting the next properties: - * - row count is estimating ans L * R, because for each left row we're probing full right row set; - * - same for CPU cost estimation multiplied by cost of row comparison. - * The perfect assumption also must include memory and IO cost estimation, and also - * a selectivity for a right row set. + * - both processed and produced row cou is estimated an L * R, because for each left row we probe full right row set; + * - for CPU cost estimation, we multiply row count by cost of row comparison. + * The perfect assumption also must include memory and IO cost estimation, and also selectivity for a right row set. */ @Override @Nullable From 83dc9b582f210d174b934217c6b54d5c58056fc0 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Thu, 7 Sep 2023 13:11:04 +0300 Subject: [PATCH 09/22] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Burak Gök --- .../opt/physical/JoinHashPhysicalRel.java | 19 ++++++++----------- .../physical/JoinNestedLoopPhysicalRel.java | 14 +++++++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 7ae84d2e45a7..65f5ae03ff7e 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -61,15 +61,14 @@ public Join copy( /** * Cost calculation of Hash Join relation. It does not rely on children cost. *

- * Hash Join algorithm is more advanced join algorithm, where it builds a hash table for left row set, and then - * compare each row from the right side - * Speaking of cost estimation, we are accounting the next properties: - * - produced row count is estimated as L * R, with assumption that the JOIN predicate has maximum selectivity. - * - processed row count is estimated as L + R, because we traverse both sides once per JOIN. - * - CPU cost estimation is a processed row multiplied by cost to build a hash table, and left and right rows comparison. + * Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left + * row set, and then compare each row from the right side. Cost estimation is the following:

    + *
  1. Produced row count is L * R assuming the join selectivity is 1. + *
  2. Processed row count is L + R because we traverse both sides once per JOIN. + *
  3. CPU is L * (hash table build cost) + R * (row comparison cost).
*

- * The perfect assumption also must include memory (what is important in case of hash table) and IO cost estimation, - * as well as a selectivity for a right row set. + * A perfect estimation must also include memory (occupied by the hash table) and IO costs, + * as well as a selectivity for the right row set. */ @Override @Nullable @@ -78,9 +77,7 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double rightRowCount = mq.getRowCount(getRight()); // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. double producedRowCount = leftRowCount * /* TODO: selectivity */ rightRowCount; - double processedRowCount = leftRowCount + /* TODO: selectivity */ rightRowCount; - - double cpu = Cost.HASH_JOIN_MULTIPLIER * processedRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; + double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER + rightRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(producedRowCount, cpu, 0.); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 32f3c4d41a7f..03cb6e56627d 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -116,11 +116,15 @@ public V accept(CreateDagVisitor visitor) { /** * Cost calculation of Nested Loop Join relation. - * NLJ algorithm is simple algorithm, where for each left row we are traversing right row set. - * Speaking of cost estimation, we are accounting the next properties: - * - both processed and produced row cou is estimated an L * R, because for each left row we probe full right row set; - * - for CPU cost estimation, we multiply row count by cost of row comparison. - * The perfect assumption also must include memory and IO cost estimation, and also selectivity for a right row set. + *

+ * Nested Loop Join algorithm is a simple join algorithm, where for each left row we are + * traversing the whole right row set. Cost estimation is the following:

    + *
  1. Produced row count is L * R assuming the join selectivity is 1. + *
  2. Processed row count is L * R because for each left row we are probing full right row set. + *
  3. CPU is L * R * (row comparison cost).
+ *

+ * A perfect estimation must also include memory and IO costs, as well as a selectivity + * for the right row set. */ @Override @Nullable From d003fc7825464b6aa4945d1d8a3fc82256f8e051 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 8 Sep 2023 11:22:40 +0300 Subject: [PATCH 10/22] Adjust costs after the discussion --- .../sql/impl/opt/physical/JoinHashPhysicalRel.java | 11 +++++++++-- .../opt/physical/JoinNestedLoopPhysicalRel.java | 14 ++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 65f5ae03ff7e..0ee62f495805 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -75,9 +75,16 @@ public Join copy( public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double leftRowCount = mq.getRowCount(getLeft()); double rightRowCount = mq.getRowCount(getRight()); + + Double selectivity = mq.getSelectivity(this, condition); + if (selectivity == null) { + selectivity = 1.; + } + // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. - double producedRowCount = leftRowCount * /* TODO: selectivity */ rightRowCount; - double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER + rightRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; + double producedRowCount = mq.getRowCount(this); + double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER + + rightRowCount * selectivity * Cost.JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(producedRowCount, cpu, 0.); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 03cb6e56627d..36931247abbf 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -119,7 +119,7 @@ public V accept(CreateDagVisitor visitor) { *

* Nested Loop Join algorithm is a simple join algorithm, where for each left row we are * traversing the whole right row set. Cost estimation is the following:

    - *
  1. Produced row count is L * R assuming the join selectivity is 1. + *
  2. Produced row count is L + R assuming the join selectivity is 1. *
  3. Processed row count is L * R because for each left row we are probing full right row set. *
  4. CPU is L * R * (row comparison cost).
*

@@ -140,11 +140,17 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { return planner.getCostFactory().makeInfiniteCost(); } + Double selectivity = mq.getSelectivity(this, condition); + if (selectivity == null) { + selectivity = 1.; + } + // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. - double rowsEstimate = leftRowCount * /* TODO: selectivity */ rightRowCount; - double cpuEstimate = Math.max(1.0, rowsEstimate - 1) * Cost.JOIN_ROW_CMP_MULTIPLIER; + double producedRows = mq.getRowCount(this); + double processedRowsEstimate = leftRowCount * selectivity * rightRowCount; + double cpuEstimate = Math.max(1.0, processedRowsEstimate - 1) * Cost.JOIN_ROW_CMP_MULTIPLIER; - return planner.getCostFactory().makeCost(rowsEstimate, cpuEstimate, 0); + return planner.getCostFactory().makeCost(producedRows, cpuEstimate, 0); } @Override From bd9efeb2f8a099e725f5c25cd779f33e957e4c16 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Mon, 18 Sep 2023 14:57:43 +0300 Subject: [PATCH 11/22] Remove selectivity from cpu calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Burak Gök --- .../jet/sql/impl/opt/physical/JoinHashPhysicalRel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 0ee62f495805..264a74926b32 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -84,7 +84,7 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. double producedRowCount = mq.getRowCount(this); double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER - + rightRowCount * selectivity * Cost.JOIN_ROW_CMP_MULTIPLIER; + + rightRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(producedRowCount, cpu, 0.); } From 50fee0052a373b5dd1f5c21c6d13aa83070e7285 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Mon, 18 Sep 2023 15:13:32 +0300 Subject: [PATCH 12/22] Fix algorithm even better --- .../jet/sql/impl/opt/physical/JoinHashPhysicalRel.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 264a74926b32..702afc960663 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -76,12 +76,6 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double leftRowCount = mq.getRowCount(getLeft()); double rightRowCount = mq.getRowCount(getRight()); - Double selectivity = mq.getSelectivity(this, condition); - if (selectivity == null) { - selectivity = 1.; - } - - // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. double producedRowCount = mq.getRowCount(this); double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER + rightRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; From 6044a6914a936f94d2051dc95bd2bba125473760 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Mon, 18 Sep 2023 15:43:06 +0300 Subject: [PATCH 13/22] Update comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Burak Gök --- .../opt/physical/JoinNestedLoopPhysicalRel.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 36931247abbf..cf639e117328 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -117,14 +117,15 @@ public V accept(CreateDagVisitor visitor) { /** * Cost calculation of Nested Loop Join relation. *

- * Nested Loop Join algorithm is a simple join algorithm, where for each left row we are - * traversing the whole right row set. Cost estimation is the following:

    - *
  1. Produced row count is L + R assuming the join selectivity is 1. - *
  2. Processed row count is L * R because for each left row we are probing full right row set. - *
  3. CPU is L * R * (row comparison cost).
+ * Nested Loop Join algorithm is a simple join algorithm, where for each left row, + * we are traversing the whole right row set. Cost estimation is the following:
    + *
  1. Produced row count is L * R * (join selectivity). + *
  2. Processed row count is L * k * R, where k is 1 for non-equi-join, + * (join selectivity) ≤ k ≤ 1 for equi-join and 1/R for key lookup. + *
  3. CPU is L * (join selectivity) * R * (row comparison cost) assuming k + * converges to the join selectivity on average.
*

- * A perfect estimation must also include memory and IO costs, as well as a selectivity - * for the right row set. + * A perfect estimation must also include memory and IO costs. */ @Override @Nullable From 516ab7b0337998553d2a77e6c6d8cf5556031ee1 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Mon, 18 Sep 2023 16:49:41 +0300 Subject: [PATCH 14/22] Fix HazelcastTable ctor workflow --- .../hazelcast/jet/sql/impl/schema/HazelcastTable.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java index 69956e2041b3..56cf17f9f5d8 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java @@ -93,12 +93,14 @@ public class HazelcastTable extends AbstractTable { private RelDataType rowType; private final Set hiddenFieldNames = new HashSet<>(); - public HazelcastTable(Table target, Statistic statistic) { - this(target, () -> statistic, null, null, null); + public HazelcastTable(Table target, HazelcastInstance instance) { + this(target, createTableStatistic(target, instance)); } - public HazelcastTable(Table target, HazelcastInstance instance) { - this(target, () -> createTableStatistic(target, instance), null, null, null); + public HazelcastTable(Table target, Statistic statistic) { + this.target = target; + this.statisticSupplier = () -> statistic; + this.filter = null; } private HazelcastTable( From 05ec305bebcd4f1ef222484089f7e764a26a1ff5 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Tue, 19 Sep 2023 15:58:10 +0300 Subject: [PATCH 15/22] Update hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Burak Gök --- .../jet/sql/impl/opt/physical/JoinHashPhysicalRel.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 702afc960663..0df7cc02e583 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -63,12 +63,11 @@ public Join copy( *

* Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left * row set, and then compare each row from the right side. Cost estimation is the following:

    - *
  1. Produced row count is L * R assuming the join selectivity is 1. - *
  2. Processed row count is L + R because we traverse both sides once per JOIN. + *
  3. Produced row count is L * R * (join selectivity). + *
  4. Processed row count is L + R because we traverse both sides once per join. *
  5. CPU is L * (hash table build cost) + R * (row comparison cost).
*

- * A perfect estimation must also include memory (occupied by the hash table) and IO costs, - * as well as a selectivity for the right row set. + * A perfect estimation must also include memory (occupied by the hash table) and IO costs. */ @Override @Nullable From 68def6055a69bf848b6e367ddac0b96245f7295f Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Thu, 28 Sep 2023 12:46:13 +0300 Subject: [PATCH 16/22] Multiply --- .../jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index cf639e117328..258ddd75c8db 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableList; import com.hazelcast.jet.sql.impl.HazelcastPhysicalScan; import com.hazelcast.jet.sql.impl.opt.OptUtils; -import com.hazelcast.jet.sql.impl.opt.cost.Cost; import com.hazelcast.jet.sql.impl.schema.HazelcastTable; import com.hazelcast.jet.sql.impl.validate.HazelcastSqlOperatorTable; import org.apache.calcite.plan.RelOptCluster; @@ -41,6 +40,8 @@ import java.util.ArrayList; import java.util.List; +import static com.hazelcast.jet.sql.impl.opt.cost.Cost.JOIN_ROW_CMP_MULTIPLIER; + public class JoinNestedLoopPhysicalRel extends JoinPhysicalRel { private JoinInfo modifiedJoinInfo; @@ -149,7 +150,7 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. double producedRows = mq.getRowCount(this); double processedRowsEstimate = leftRowCount * selectivity * rightRowCount; - double cpuEstimate = Math.max(1.0, processedRowsEstimate - 1) * Cost.JOIN_ROW_CMP_MULTIPLIER; + double cpuEstimate = Math.max(1.0, processedRowsEstimate - 1) * rightCost.getCpu() * JOIN_ROW_CMP_MULTIPLIER; return planner.getCostFactory().makeCost(producedRows, cpuEstimate, 0); } From b06e92b015daa05c43dd70aa4e71556e39e80b8b Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Thu, 28 Sep 2023 19:15:53 +0300 Subject: [PATCH 17/22] Cost readjustment --- .../hazelcast/jet/sql/impl/opt/cost/Cost.java | 11 +++-- .../jet/sql/impl/opt/cost/CostUtils.java | 16 ++----- .../opt/physical/JoinHashPhysicalRel.java | 12 +++-- .../physical/JoinNestedLoopPhysicalRel.java | 46 +++++++++++-------- 4 files changed, 43 insertions(+), 42 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java index 13c44c74dd00..b9399cb1cbaf 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java @@ -30,7 +30,7 @@ * Our implementation still tracks row count, CPU and network, but it doesn't implement unnecessary methods, has proper * comparison semantics, and use CPU and network for cost comparison instead row count. *

- * [1] https://issues.apache.org/jira/browse/CALCITE-3956 + * [1] LINK */ public class Cost implements RelOptCost { @@ -50,10 +50,11 @@ public class Cost implements RelOptCost { */ public static final double HASH_JOIN_MULTIPLIER = 7; - /** - * Multiplier to display row comparison - */ - public static final double JOIN_ROW_CMP_MULTIPLIER = 2; + // Most of the time you compare one field from both sides in join condition. + public static final double NLJ_JOIN_ROW_CMP_MULTIPLIER = 1.5; + + // During the comparison for each right row we do everything, except adding. + public static final double HASH_JOIN_ROW_CMP_MULTIPLIER = HASH_JOIN_MULTIPLIER - 1; private final double rows; private final double cpu; diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java index 3eb965237a4d..38978f9a8eae 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java @@ -17,9 +17,6 @@ package com.hazelcast.jet.sql.impl.opt.cost; import com.hazelcast.config.IndexType; -import com.hazelcast.jet.sql.impl.validate.types.HazelcastTypeUtils; -import org.apache.calcite.rel.RelNode; -import org.apache.calcite.rel.type.RelDataTypeField; /** * Utility methods for cost estimation. @@ -44,6 +41,9 @@ public final class CostUtils { /** Multiplier for the CPU part of the cost. Assumes 1ns per item. */ public static final double CPU_COST_MULTIPLIER = 1.0d; + /** Estimation for average batch size of NLJ' left side. */ + public static final double AVERAGE_NON_EQUI_JOIN_LEFT_BATCH_SIZE = 1.0d; + /** Multiplier for the network part of the cost. Assumes ~10µs per 1Kb that results in ~10ns per byte. */ public static final double NETWORK_COST_MULTIPLIER = CPU_COST_MULTIPLIER * 10; @@ -110,14 +110,4 @@ public static Double adjustFilteredRowCount(Double rowCount, Double selectivity) public static double getProjectCpu(double rowCount, int expressionCount) { return rowCount * expressionCount; } - - public static int getEstimatedRowWidth(RelNode rel) { - int res = 0; - - for (RelDataTypeField field : rel.getRowType().getFieldList()) { - res += HazelcastTypeUtils.toHazelcastType(field.getType()).getTypeFamily().getEstimatedSize(); - } - - return res; - } } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 0df7cc02e583..edc67095c656 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -63,9 +63,8 @@ public Join copy( *

* Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left * row set, and then compare each row from the right side. Cost estimation is the following:

    - *
  1. Produced row count is L * R * (join selectivity). *
  2. Processed row count is L + R because we traverse both sides once per join. - *
  3. CPU is L * (hash table build cost) + R * (row comparison cost).
+ *
  • CPU is L * (hash table build cost) + R * (row comparison cost) + ((L + R) * (row projection cost)) *

    * A perfect estimation must also include memory (occupied by the hash table) and IO costs. */ @@ -75,10 +74,13 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double leftRowCount = mq.getRowCount(getLeft()); double rightRowCount = mq.getRowCount(getRight()); - double producedRowCount = mq.getRowCount(this); + double projectionCost = getRight().getRowType().getFieldCount(); + + double processedRowsCount = leftRowCount + rightRowCount; double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER - + rightRowCount * Cost.JOIN_ROW_CMP_MULTIPLIER; + + rightRowCount * Cost.HASH_JOIN_ROW_CMP_MULTIPLIER + + mq.getRowCount(this) * projectionCost; - return planner.getCostFactory().makeCost(producedRowCount, cpu, 0.); + return planner.getCostFactory().makeCost(processedRowsCount, cpu, 0.); } } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 258ddd75c8db..322868379bb4 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableList; import com.hazelcast.jet.sql.impl.HazelcastPhysicalScan; import com.hazelcast.jet.sql.impl.opt.OptUtils; +import com.hazelcast.jet.sql.impl.opt.cost.Cost; +import com.hazelcast.jet.sql.impl.opt.cost.CostUtils; import com.hazelcast.jet.sql.impl.schema.HazelcastTable; import com.hazelcast.jet.sql.impl.validate.HazelcastSqlOperatorTable; import org.apache.calcite.plan.RelOptCluster; @@ -40,8 +42,6 @@ import java.util.ArrayList; import java.util.List; -import static com.hazelcast.jet.sql.impl.opt.cost.Cost.JOIN_ROW_CMP_MULTIPLIER; - public class JoinNestedLoopPhysicalRel extends JoinPhysicalRel { private JoinInfo modifiedJoinInfo; @@ -120,39 +120,47 @@ public V accept(CreateDagVisitor visitor) { *

    * Nested Loop Join algorithm is a simple join algorithm, where for each left row, * we are traversing the whole right row set. Cost estimation is the following:

      + *
    1. L is a count of left side rows. + *
    2. R is a count of right side rows. *
    3. Produced row count is L * R * (join selectivity). *
    4. Processed row count is L * k * R, where k is 1 for non-equi-join, - * (join selectivity) ≤ k ≤ 1 for equi-join and 1/R for key lookup. - *
    5. CPU is L * (join selectivity) * R * (row comparison cost) assuming k - * converges to the join selectivity on average.
    + * (join selectivity) ≤ k ≤ 1 for equi-join and 1/R for key lookup. + *
  • CPU is L * R * (row comparison cost) + * + L * R * (selectivity) * (row join cost) + * + ((L - 1) * cost of right side scan) + * *

    * A perfect estimation must also include memory and IO costs. */ @Override @Nullable public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { - final double leftRowCount = mq.getRowCount(left); - final double rightRowCount = mq.getRowCount(right); + boolean isEquiJoin = this.analyzeCondition().isEqui(); + + double leftRowCount = mq.getRowCount(left); + double rightRowCount = mq.getRowCount(right); + double projectionUnitCost = this.getRowType().getFieldCount(); + if (Double.isInfinite(leftRowCount) || Double.isInfinite(rightRowCount)) { return planner.getCostFactory().makeInfiniteCost(); } - RelOptCost rightCost = planner.getCost(getRight(), mq); - if (rightCost == null) { - return planner.getCostFactory().makeInfiniteCost(); - } + double producedRows = mq.getRowCount(this); + double processedRowCount = Math.max(1.0, leftRowCount * rightRowCount); - Double selectivity = mq.getSelectivity(this, condition); - if (selectivity == null) { - selectivity = 1.; + double joinComparisonCost = processedRowCount * Cost.NLJ_JOIN_ROW_CMP_MULTIPLIER; + double joinProjectionCost = producedRows * projectionUnitCost; + double rightSideRepetitions = Math.max(.0, leftRowCount - 1); + + if (!isEquiJoin) { + // TODO: reconsider the value for this constant later. + rightSideRepetitions /= CostUtils.AVERAGE_NON_EQUI_JOIN_LEFT_BATCH_SIZE; } - // TODO: introduce selectivity estimator, but ATM we taking the worst case scenario : selectivity = 1.0. - double producedRows = mq.getRowCount(this); - double processedRowsEstimate = leftRowCount * selectivity * rightRowCount; - double cpuEstimate = Math.max(1.0, processedRowsEstimate - 1) * rightCost.getCpu() * JOIN_ROW_CMP_MULTIPLIER; + double rightSideRepetitionsCost = ((Cost) planner.getCost(right, mq)).getCpuInternal() * rightSideRepetitions; + double cpuEstimate = joinComparisonCost + joinProjectionCost + rightSideRepetitionsCost; - return planner.getCostFactory().makeCost(producedRows, cpuEstimate, 0); + return planner.getCostFactory().makeCost(processedRowCount, cpuEstimate, 0); } @Override From fbd5492eab65658e49041889a78874bd12a35150 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Wed, 4 Oct 2023 12:01:49 +0300 Subject: [PATCH 18/22] Docs and constant adjustments --- .../hazelcast/jet/sql/impl/opt/cost/CostUtils.java | 2 +- .../sql/impl/opt/physical/JoinHashPhysicalRel.java | 4 ++-- .../impl/opt/physical/JoinNestedLoopPhysicalRel.java | 11 +++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java index 38978f9a8eae..a69054ad7a4a 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/CostUtils.java @@ -42,7 +42,7 @@ public final class CostUtils { public static final double CPU_COST_MULTIPLIER = 1.0d; /** Estimation for average batch size of NLJ' left side. */ - public static final double AVERAGE_NON_EQUI_JOIN_LEFT_BATCH_SIZE = 1.0d; + public static final double AVERAGE_NON_EQUI_JOIN_LEFT_BATCH_SIZE = 4.0d; /** Multiplier for the network part of the cost. Assumes ~10µs per 1Kb that results in ~10ns per byte. */ public static final double NETWORK_COST_MULTIPLIER = CPU_COST_MULTIPLIER * 10; diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index edc67095c656..82211d9aaa28 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -63,8 +63,8 @@ public Join copy( *

    * Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left * row set, and then compare each row from the right side. Cost estimation is the following:

      - *
    1. Processed row count is L + R because we traverse both sides once per join. - *
    2. CPU is L * (hash table build cost) + R * (row comparison cost) + ((L + R) * (row projection cost))
    + *
  • Processed row count (PR) is L + R because we traverse both sides once per join. + *
  • CPU is L * (hash table build cost) + R * (row comparison cost) + (PR * (row projection cost)) *

    * A perfect estimation must also include memory (occupied by the hash table) and IO costs. */ diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index 322868379bb4..c979891d938d 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -122,12 +122,11 @@ public V accept(CreateDagVisitor visitor) { * we are traversing the whole right row set. Cost estimation is the following:

      *
    1. L is a count of left side rows. *
    2. R is a count of right side rows. - *
    3. Produced row count is L * R * (join selectivity). - *
    4. Processed row count is L * k * R, where k is 1 for non-equi-join, + *
    5. PD is a produced row count is L * R * (join selectivity). + *
    6. PR is Processed row count is L * k * R, where k is 1 for non-equi-join, * (join selectivity) ≤ k ≤ 1 for equi-join and 1/R for key lookup. - *
    7. CPU is L * R * (row comparison cost) - * + L * R * (selectivity) * (row join cost) - * + ((L - 1) * cost of right side scan) + *
    8. CPU cost is estimated as + * PR * (row comparison cost) + PD * (selectivity) * (row join cost) + ((L - 1) * cost of right side scan) *
    *

    * A perfect estimation must also include memory and IO costs. @@ -153,7 +152,7 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double rightSideRepetitions = Math.max(.0, leftRowCount - 1); if (!isEquiJoin) { - // TODO: reconsider the value for this constant later. + // TODO: measure that value for this constant in load tests. rightSideRepetitions /= CostUtils.AVERAGE_NON_EQUI_JOIN_LEFT_BATCH_SIZE; } From ae0bd63fcd15463279302bc0f84afe23c056694f Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Wed, 4 Oct 2023 12:06:16 +0300 Subject: [PATCH 19/22] Typos --- .../jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java index c979891d938d..87ea49dc7078 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinNestedLoopPhysicalRel.java @@ -122,8 +122,8 @@ public V accept(CreateDagVisitor visitor) { * we are traversing the whole right row set. Cost estimation is the following:

      *
    1. L is a count of left side rows. *
    2. R is a count of right side rows. - *
    3. PD is a produced row count is L * R * (join selectivity). - *
    4. PR is Processed row count is L * k * R, where k is 1 for non-equi-join, + *
    5. PD is a produced row count: L * R * (join selectivity). + *
    6. PR is a processed row count: L * k * R, where k is 1 for non-equi-join, * (join selectivity) ≤ k ≤ 1 for equi-join and 1/R for key lookup. *
    7. CPU cost is estimated as * PR * (row comparison cost) + PD * (selectivity) * (row join cost) + ((L - 1) * cost of right side scan) From f65293f63779688a039ff62fe67a27ec787b7447 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Thu, 19 Oct 2023 15:00:19 +0300 Subject: [PATCH 20/22] Revert back rows estimation of IMap; add broadcast factor for hash LEFT JOIN --- .../hazelcast/jet/sql/impl/opt/cost/Cost.java | 15 ++++++++--- .../opt/physical/JoinHashPhysicalRel.java | 13 +++++++--- .../sql/impl/schema/HazelcastSchemaUtils.java | 5 +--- .../jet/sql/impl/schema/HazelcastTable.java | 26 +++++-------------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java index b9399cb1cbaf..427273552e79 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/cost/Cost.java @@ -48,13 +48,22 @@ public class Cost implements RelOptCost { * - and compare with each element; (estimate - 1 op, assuming hash collision may happen) * - add the k-v to the table. (estimate - 1 op). */ - public static final double HASH_JOIN_MULTIPLIER = 7; + public static final double HASH_JOIN_MULTIPLIER = 7.; + + // During the comparison for each right row we do everything, except adding. + public static final double HASH_JOIN_ROW_CMP_MULTIPLIER = HASH_JOIN_MULTIPLIER - 1; // Most of the time you compare one field from both sides in join condition. public static final double NLJ_JOIN_ROW_CMP_MULTIPLIER = 1.5; - // During the comparison for each right row we do everything, except adding. - public static final double HASH_JOIN_ROW_CMP_MULTIPLIER = HASH_JOIN_MULTIPLIER - 1; + /** + * Multiplier to display CPU aspect of network broadcast actions (we do not include network cost yet): + * - row serialization; (avg estimate - 4 ops: 1 ops for __key and 3 ops as avg count of value props) + * - write raw data to the socket (estimate - 4 ops) + * - read raw data from the socket (estimate - 2 ops) + * - row deserialization (avg estimate - 4 ops, same as for serialization) + */ + public static final double NETWORK_BROADCAST_FACTOR = 14.; private final double rows; private final double cpu; diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 82211d9aaa28..733c6d62af2d 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -64,7 +64,9 @@ public Join copy( * Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left * row set, and then compare each row from the right side. Cost estimation is the following:
        *
      1. Processed row count (PR) is L + R because we traverse both sides once per join. - *
      2. CPU is L * (hash table build cost) + R * (row comparison cost) + (PR * (row projection cost))
      + *
    8. CPU is L * (hash table build cost) + R * (row comparison cost) + (PR * (row projection cost)) + *
    9. Also, for the right side, if it is broadcast, we multiply the row comparison + * cost by a (network broadcast factor)
    *

    * A perfect estimation must also include memory (occupied by the hash table) and IO costs. */ @@ -75,10 +77,15 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { double rightRowCount = mq.getRowCount(getRight()); double projectionCost = getRight().getRowType().getFieldCount(); - double processedRowsCount = leftRowCount + rightRowCount; + double networkBroadcastFactor = 1.; + + if (joinType == JoinRelType.LEFT) { + networkBroadcastFactor = Cost.NETWORK_BROADCAST_FACTOR; + } + double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER - + rightRowCount * Cost.HASH_JOIN_ROW_CMP_MULTIPLIER + + rightRowCount * Cost.HASH_JOIN_ROW_CMP_MULTIPLIER * networkBroadcastFactor + mq.getRowCount(this) * projectionCost; return planner.getCostFactory().makeCost(processedRowsCount, cpu, 0.); diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java index 3d3d80167789..a2ecafdc6c9a 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastSchemaUtils.java @@ -20,7 +20,6 @@ import com.hazelcast.sql.impl.QueryUtils; import com.hazelcast.sql.impl.schema.SqlCatalog; import com.hazelcast.sql.impl.schema.Table; -import com.hazelcast.sql.impl.schema.map.PartitionedMapTable; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.Statistic; @@ -70,9 +69,7 @@ public static HazelcastSchema createRootSchema(HazelcastInstance hz, SqlCatalog for (Map.Entry tableEntry : currentSchemaEntry.getValue().entrySet()) { String tableName = tableEntry.getKey(); Table table = tableEntry.getValue(); - HazelcastTable convertedTable = table instanceof PartitionedMapTable - ? new HazelcastTable(table, hz) - : new HazelcastTable(table, createTableStatistic(table)); + HazelcastTable convertedTable = new HazelcastTable(table, createTableStatistic(table)); schemaTables.put(tableName, convertedTable); } diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java index 56cf17f9f5d8..25abb213796a 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/schema/HazelcastTable.java @@ -16,7 +16,6 @@ package com.hazelcast.jet.sql.impl.schema; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.jet.sql.impl.opt.OptUtils; import com.hazelcast.jet.sql.impl.opt.common.CalcIntoScanRule; import com.hazelcast.jet.sql.impl.opt.cost.CostUtils; @@ -47,7 +46,6 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; -import java.util.function.Supplier; import static java.util.stream.Collectors.joining; @@ -86,32 +84,28 @@ */ public class HazelcastTable extends AbstractTable { private final Table target; - private final Supplier statisticSupplier; + private final Statistic statistic; private final RexNode filter; private List projects; private RelDataType rowType; private final Set hiddenFieldNames = new HashSet<>(); - public HazelcastTable(Table target, HazelcastInstance instance) { - this(target, createTableStatistic(target, instance)); - } - public HazelcastTable(Table target, Statistic statistic) { this.target = target; - this.statisticSupplier = () -> statistic; + this.statistic = statistic; this.filter = null; } private HazelcastTable( Table target, - Supplier statisticSupplier, + Statistic statistic, List projects, @Nullable RelDataType rowType, @Nullable RexNode filter ) { this.target = target; - this.statisticSupplier = statisticSupplier; + this.statistic = statistic; this.projects = projects; this.rowType = rowType == null ? computeRowType(projects) : rowType; this.filter = filter; @@ -132,11 +126,11 @@ private void initRowType() { } public HazelcastTable withProject(List projects, @Nullable RelDataType rowType) { - return new HazelcastTable(target, statisticSupplier, projects, rowType, filter); + return new HazelcastTable(target, statistic, projects, rowType, filter); } public HazelcastTable withFilter(RexNode filter) { - return new HazelcastTable(target, statisticSupplier, projects, rowType, filter); + return new HazelcastTable(target, statistic, projects, rowType, filter); } @Nonnull @@ -163,7 +157,6 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { @Override public Statistic getStatistic() { - Statistic statistic = statisticSupplier.get(); if (filter == null) { return statistic; } else { @@ -175,7 +168,7 @@ public Statistic getStatistic() { @SuppressWarnings("DataFlowIssue") public double getTotalRowCount() { - return statisticSupplier.get().getRowCount(); + return statistic.getRowCount(); } public boolean isHidden(String fieldName) { @@ -219,17 +212,12 @@ private RelDataType computeRowType(List projects) { return new RelRecordType(StructKind.PEEK_FIELDS, typeFields, false); } - private static Statistic createTableStatistic(Table table, HazelcastInstance instance) { - return new HazelcastTableStatistic(instance.getMap(table.getSqlName()).size()); - } - /** * Statistics that takes into account the row count after the filter is applied. */ private final class AdjustedStatistic implements Statistic { private final Double rowCount; - private final Statistic statistic = statisticSupplier.get(); private AdjustedStatistic(Double rowCount) { this.rowCount = rowCount; From 491e5fd89a3535ca12ae8f23a4c3f87e8e2bdd03 Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Fri, 12 Jan 2024 13:58:30 +0200 Subject: [PATCH 21/22] Clarify doc for hash join --- .../opt/physical/JoinHashPhysicalRel.java | 10 ++--- .../impl/opt/physical/PhysicalJoinTest.java | 37 ------------------- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java index 733c6d62af2d..2185f5b66f69 100644 --- a/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java +++ b/hazelcast-sql/src/main/java/com/hazelcast/jet/sql/impl/opt/physical/JoinHashPhysicalRel.java @@ -61,10 +61,10 @@ public Join copy( /** * Cost calculation of Hash Join relation. It does not rely on children cost. *

    - * Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the left - * row set, and then compare each row from the right side. Cost estimation is the following:

      + * Hash Join algorithm is a more advanced join algorithm, which builds a hash table for the right + * row set, and then compare each row from the left side. Cost estimation is the following:
        *
      1. Processed row count (PR) is L + R because we traverse both sides once per join. - *
      2. CPU is L * (hash table build cost) + R * (row comparison cost) + (PR * (row projection cost)) + *
      3. CPU is R * (hash table build cost) + L * (row comparison cost) + (PR * (row projection cost)) *
      4. Also, for the right side, if it is broadcast, we multiply the row comparison * cost by a (network broadcast factor)
      *

      @@ -84,8 +84,8 @@ public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { networkBroadcastFactor = Cost.NETWORK_BROADCAST_FACTOR; } - double cpu = leftRowCount * Cost.HASH_JOIN_MULTIPLIER - + rightRowCount * Cost.HASH_JOIN_ROW_CMP_MULTIPLIER * networkBroadcastFactor + double cpu = leftRowCount * Cost.HASH_JOIN_ROW_CMP_MULTIPLIER + + rightRowCount * Cost.HASH_JOIN_MULTIPLIER * networkBroadcastFactor + mq.getRowCount(this) * projectionCost; return planner.getCostFactory().makeCost(processedRowsCount, cpu, 0.); diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java index 4c735c8d05b5..1d584e789532 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java @@ -93,43 +93,6 @@ public void when_rightChildIsNotTableScan_then_useHashJoin() { ); } - @Ignore("Support streaming tables with watermarks in OptimizerTestSupport") - @Test - public void when_bothInputsAreStreamScan_then_useS2SJoin() { - String leftStream = "l"; - TestStreamSqlConnector.create( - instance().getSql(), - leftStream, - asList("a", "b"), - asList(INTEGER, TIMESTAMP), - row(1, timestamp(1L)) - ); - - String rightStream = "r"; - TestStreamSqlConnector.create( - instance().getSql(), - rightStream, - asList("x", "y"), - asList(INTEGER, TIMESTAMP), - row(1, timestamp(1L)) - ); - - assertInstanceOf(TestAbstractSqlConnector.TestTable.class, resolver.getTables().get(0)); - assertInstanceOf(TestAbstractSqlConnector.TestTable.class, resolver.getTables().get(1)); - HazelcastTable tableLeft = streamingTable(resolver.getTables().get(0)); - HazelcastTable tableRight = streamingTable(resolver.getTables().get(1)); - - String query = "SELECT * FROM l JOIN r ON l.b = r.y"; - assertPlan( - optimizePhysical(query, ImmutableList.of(), tableLeft, tableRight).getPhysical(), - plan( - planRow(0, StreamToStreamJoinPhysicalRel.class), - planRow(1, FullScanPhysicalRel.class), - planRow(1, FullScanPhysicalRel.class) - ) - ); - } - private static HazelcastTable streamingTable(Table table) { return new HazelcastTable(table, new HazelcastTableStatistic(1)); } From a716a94f4a3d7b4de02e3f9797e2b68ec1fb96bc Mon Sep 17 00:00:00 2001 From: Sasha Syrotenko Date: Tue, 16 Jan 2024 16:42:11 +0200 Subject: [PATCH 22/22] Checkstyle --- .../jet/sql/impl/opt/physical/PhysicalJoinTest.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java index 1d584e789532..1a99787fc947 100644 --- a/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java +++ b/hazelcast-sql/src/test/java/com/hazelcast/jet/sql/impl/opt/physical/PhysicalJoinTest.java @@ -16,29 +16,23 @@ package com.hazelcast.jet.sql.impl.opt.physical; -import com.google.common.collect.ImmutableList; import com.hazelcast.jet.sql.impl.connector.SqlConnectorCache; -import com.hazelcast.jet.sql.impl.connector.test.TestAbstractSqlConnector; -import com.hazelcast.jet.sql.impl.connector.test.TestStreamSqlConnector; import com.hazelcast.jet.sql.impl.opt.OptimizerTestSupport; import com.hazelcast.jet.sql.impl.schema.HazelcastTable; import com.hazelcast.jet.sql.impl.schema.HazelcastTableStatistic; -import com.hazelcast.jet.sql.impl.schema.TableResolverImpl; import com.hazelcast.jet.sql.impl.schema.RelationsStorage; +import com.hazelcast.jet.sql.impl.schema.TableResolverImpl; import com.hazelcast.spi.impl.NodeEngine; import com.hazelcast.sql.impl.schema.Table; import com.hazelcast.sql.impl.schema.TableResolver; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import static com.hazelcast.jet.impl.util.Util.getNodeEngine; import static com.hazelcast.sql.impl.extract.QueryPath.KEY; import static com.hazelcast.sql.impl.extract.QueryPath.VALUE; import static com.hazelcast.sql.impl.type.QueryDataType.INT; -import static com.hazelcast.sql.impl.type.QueryDataTypeFamily.INTEGER; -import static com.hazelcast.sql.impl.type.QueryDataTypeFamily.TIMESTAMP; import static java.util.Arrays.asList; public class PhysicalJoinTest extends OptimizerTestSupport {