ygerzhedovich commented on code in PR #5211: URL: https://github.com/apache/ignite-3/pull/5211#discussion_r1954237785
########## modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/metadata/IgniteMdSelectivity.java: ########## @@ -17,49 +17,226 @@ package org.apache.ignite.internal.sql.engine.metadata; +import static org.apache.calcite.rex.RexUtil.expandSearch; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider; import org.apache.calcite.rel.metadata.RelMdSelectivity; import org.apache.calcite.rel.metadata.RelMdUtil; import org.apache.calcite.rel.metadata.RelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rex.RexCall; +import org.apache.calcite.rex.RexLocalRef; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.rex.RexVisitor; +import org.apache.calcite.rex.RexVisitorImpl; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.util.BuiltInMethod; +import org.apache.calcite.util.Util; import org.apache.ignite.internal.sql.engine.prepare.bounds.ExactBounds; import org.apache.ignite.internal.sql.engine.prepare.bounds.MultiBounds; import org.apache.ignite.internal.sql.engine.prepare.bounds.RangeBounds; import org.apache.ignite.internal.sql.engine.prepare.bounds.SearchBounds; import org.apache.ignite.internal.sql.engine.rel.IgniteHashIndexSpool; import org.apache.ignite.internal.sql.engine.rel.IgniteSortedIndexSpool; import org.apache.ignite.internal.sql.engine.rel.ProjectableFilterableTableScan; +import org.apache.ignite.internal.sql.engine.util.Commons; import org.apache.ignite.internal.sql.engine.util.RexUtils; +import org.checkerframework.checker.nullness.qual.Nullable; /** - * IgniteMdSelectivity. - * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859 + * IgniteMdSelectivity supplies implementation of {@link RelMetadataQuery#getSelectivity}. */ public class IgniteMdSelectivity extends RelMdSelectivity { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.SELECTIVITY.method, new IgniteMdSelectivity()); + private static final double EQ_SELECTIVITY = 0.333; + + private static double computeOpsSelectivity(Map<RexNode, List<SqlKind>> operands, double baseSelectivity) { + double result = baseSelectivity; + + for (Map.Entry<RexNode, List<SqlKind>> e : operands.entrySet()) { + int eqNum = 0; + double result0 = 0.0; + + for (SqlKind kind : e.getValue()) { + switch (kind) { + case IS_NOT_NULL: + result0 = Math.max(result0, 0.9); + break; + case EQUALS: + // Take into account Zipf`s distribution. + result0 = Math.min(result0 + (EQ_SELECTIVITY / Math.sqrt(++eqNum)), 1.0); + break; + case GREATER_THAN: + case LESS_THAN: + case GREATER_THAN_OR_EQUAL: + case LESS_THAN_OR_EQUAL: + result0 = Math.min(result0 + 0.5, 1.0); + break; + default: + // Not clear here, proceed with default. + result0 += 0.05; + } + } + + result = Math.max(result, result0); + } + + return result; + } + + /** + * Implements selectivity prediction algorithm. <br> + * Current implementation work as follows: <br><br> + * 1. If mixed OR-related operands are processed i.e: OR(=($t1, 'D'), =($t1, 'M'), =($t2, 'W')) selectivity computes separately + * for each local ref and a big one is chosen <br> + * 2. If mixed OR or AND related operands are processed i.e: + * OR(<($t3, 110), >($t3, 150), AND(>=($t2, -($t1, 2)), <=($t2, +($t3, 2))), >($t4, $t2), <($t4, $t3)) selectivity computes separately + * for each local ref with AND selectivity adjustment. <br> + */ + private static double computeSelectivity(RexCall call) { + ImmutableList<RexNode> operands = call.operands; + List<RexNode> andOperands = null; + List<RexNode> otherOperands = null; + double baseSelectivity = 0.0; + Map<RexNode, List<SqlKind>> processOperands = new HashMap<>(); + + assert !operands.isEmpty(); + + boolean andConsist = operands.stream().anyMatch(op -> op.isA(SqlKind.AND)); + + if (andConsist) { + for (RexNode op : operands) { + if (op.isA(SqlKind.AND)) { + if (andOperands == null) { Review Comment: I fully understand it, however, simple ArrayList has optimization for create it with default constructor and it will not allocate a new arrays, just a new ArrayList object which would be lightweight and will be collected by GC very soon - so will not affect performance. Alsoб we removing the check - that can be also take a little time - I thinks еhey must cancel each other out -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@ignite.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org