You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/12/02 00:55:32 UTC
calcite git commit: Following [CALCITE-987],
improve rowCount and maxRowCount formulae
Repository: calcite
Updated Branches:
refs/heads/master d3c5acd3a -> 24b074715
Following [CALCITE-987], improve rowCount and maxRowCount formulae
Enable tests for rowCount and add tests for maxRowCount.
Move 2 methods into RelMdUtil.
Add Util.first methods, for unboxing to primitive values.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/24b07471
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/24b07471
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/24b07471
Branch: refs/heads/master
Commit: 24b074715c2663d78c0b2a2d42e0711c733b1f40
Parents: d3c5acd
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Nov 30 16:22:27 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Dec 1 14:43:45 2015 -0800
----------------------------------------------------------------------
.../java/org/apache/calcite/rel/core/Join.java | 33 ++-
.../java/org/apache/calcite/rel/core/Minus.java | 12 +-
.../org/apache/calcite/rel/core/SemiJoin.java | 10 +-
.../rel/metadata/RelMdDistinctRowCount.java | 35 +++
.../calcite/rel/metadata/RelMdMaxRowCount.java | 71 ++++--
.../calcite/rel/metadata/RelMdRowCount.java | 60 +++--
.../apache/calcite/rel/metadata/RelMdUtil.java | 61 ++++-
.../calcite/rel/metadata/RelMetadataQuery.java | 4 +-
.../calcite/sql/validate/SqlValidator.java | 4 +-
.../main/java/org/apache/calcite/util/Util.java | 63 ++++-
.../org/apache/calcite/test/CalciteAssert.java | 2 +-
.../apache/calcite/test/MockCatalogReader.java | 33 +--
.../apache/calcite/test/RelMetadataTest.java | 242 ++++++++++++++++---
.../apache/calcite/test/RelOptRulesTest.java | 18 +-
14 files changed, 510 insertions(+), 138 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/core/Join.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Join.java b/core/src/main/java/org/apache/calcite/rel/core/Join.java
index 1a6b5c1..5a0990d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Join.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Join.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.BiRel;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -31,6 +32,7 @@ import org.apache.calcite.rex.RexChecker;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -98,7 +100,7 @@ public abstract class Join extends BiRel {
return ImmutableList.of(condition);
}
- public RelNode accept(RexShuttle shuttle) {
+ @Override public RelNode accept(RexShuttle shuttle) {
RexNode condition = shuttle.apply(this.condition);
if (this.condition == condition) {
return this;
@@ -155,34 +157,29 @@ public abstract class Join extends BiRel {
return true;
}
- // implement RelNode
- public RelOptCost computeSelfCost(RelOptPlanner planner) {
+ @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
// REVIEW jvs 9-Apr-2006: Just for now...
double rowCount = RelMetadataQuery.getRowCount(this);
return planner.getCostFactory().makeCost(rowCount, 0, 0);
}
+ /** @deprecated Use {@link RelMdUtil#getJoinRowCount(Join, RexNode)}. */
+ @Deprecated // to be removed before 2.0
public static double estimateJoinedRows(
Join joinRel,
RexNode condition) {
- double product =
- RelMetadataQuery.getRowCount(joinRel.getLeft())
- * RelMetadataQuery.getRowCount(joinRel.getRight());
-
- // TODO: correlation factor
- return product * RelMetadataQuery.getSelectivity(joinRel, condition);
+ return Util.first(RelMdUtil.getJoinRowCount(joinRel, condition), 1D);
}
- // implement RelNode
- public double getRows() {
- return estimateJoinedRows(this, condition);
+ @Override public double getRows() {
+ return Util.first(RelMdUtil.getJoinRowCount(this, condition), 1D);
}
- public Set<String> getVariablesStopped() {
+ @Override public Set<String> getVariablesStopped() {
return variablesStopped;
}
- public RelWriter explainTerms(RelWriter pw) {
+ @Override public RelWriter explainTerms(RelWriter pw) {
return super.explainTerms(pw)
.item("condition", condition)
.item("joinType", joinType.name().toLowerCase())
@@ -192,7 +189,7 @@ public abstract class Join extends BiRel {
!getSystemFieldList().isEmpty());
}
- protected RelDataType deriveRowType() {
+ @Override protected RelDataType deriveRowType() {
return deriveJoinRowType(
left.getRowType(),
right.getRowType(),
@@ -295,14 +292,14 @@ public abstract class Join extends BiRel {
== (systemFieldList.size()
+ leftType.getFieldCount()
+ rightType.getFieldCount()));
- List<String> nameList = new ArrayList<String>();
- List<RelDataType> typeList = new ArrayList<RelDataType>();
+ List<String> nameList = new ArrayList<>();
+ final List<RelDataType> typeList = new ArrayList<>();
// use a hashset to keep track of the field names; this is needed
// to ensure that the contains() call to check for name uniqueness
// runs in constant time; otherwise, if the number of fields is large,
// doing a contains() on a list can be expensive
- HashSet<String> uniqueNameList = new HashSet<String>();
+ final HashSet<String> uniqueNameList = new HashSet<>();
addFields(systemFieldList, typeList, nameList, uniqueNameList);
addFields(leftType.getFieldList(), typeList, nameList, uniqueNameList);
if (rightType != null) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/core/Minus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Minus.java b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
index 23c1ee8..18c6f60 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Minus.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
@@ -20,7 +20,7 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.sql.SqlKind;
import java.util.List;
@@ -49,15 +49,7 @@ public abstract class Minus extends SetOp {
}
@Override public double getRows() {
- // REVIEW jvs 30-May-2005: I just pulled this out of a hat.
- double dRows = RelMetadataQuery.getRowCount(inputs.get(0));
- for (int i = 1; i < inputs.size(); i++) {
- dRows -= 0.5 * RelMetadataQuery.getRowCount(inputs.get(i));
- }
- if (dRows < 0) {
- dRows = 0;
- }
- return dRows;
+ return RelMdUtil.getMinusRowCount(this);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
index 77129b1..4570919 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
@@ -22,12 +22,12 @@ 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.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -100,9 +100,9 @@ public class SemiJoin extends EquiJoin {
}
@Override public double getRows() {
- // TODO: correlation factor
- return RelMetadataQuery.getRowCount(left)
- * RexUtil.getSelectivity(condition);
+ return Util.first(RelMdUtil.getSemiJoinRowCount(left, right, joinType, condition),
+ 1D);
+
}
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
index 784e131..2fb2b25 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
@@ -112,6 +112,11 @@ public class RelMdDistinctRowCount {
Filter rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
// REVIEW zfong 4/18/06 - In the Broadbase code, duplicates are not
// removed from the two filter lists. However, the code below is
// doing so.
@@ -131,6 +136,11 @@ public class RelMdDistinctRowCount {
Join rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
return RelMdUtil.getJoinDistinctRowCount(
rel,
rel.getJoinType(),
@@ -143,6 +153,11 @@ public class RelMdDistinctRowCount {
SemiJoin rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
// create a RexNode representing the selectivity of the
// semijoin filter and pass it to getDistinctRowCount
RexNode newPred = RelMdUtil.makeSemiJoinSelectivityRexNode(rel);
@@ -165,6 +180,11 @@ public class RelMdDistinctRowCount {
Aggregate rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
// determine which predicates can be applied on the child of the
// aggregate
List<RexNode> notPushable = new ArrayList<RexNode>();
@@ -202,6 +222,11 @@ public class RelMdDistinctRowCount {
Values rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
Double selectivity = RelMdUtil.guessSelectivity(predicate);
// assume half the rows are duplicates
@@ -213,6 +238,11 @@ public class RelMdDistinctRowCount {
Project rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
ImmutableBitSet.Builder baseCols = ImmutableBitSet.builder();
ImmutableBitSet.Builder projCols = ImmutableBitSet.builder();
List<RexNode> projExprs = rel.getProjects();
@@ -278,6 +308,11 @@ public class RelMdDistinctRowCount {
RelNode rel,
ImmutableBitSet groupKey,
RexNode predicate) {
+ if (predicate == null || predicate.isAlwaysTrue()) {
+ if (groupKey.isEmpty()) {
+ return 1D;
+ }
+ }
// REVIEW zfong 4/19/06 - Broadbase code does not take into
// consideration selectivity of predicates passed in. Also, they
// assume the rows are unique even if the table is not
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
index 85ff11a..c8e84cc 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
@@ -19,11 +19,14 @@ package org.apache.calcite.rel.metadata;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Intersect;
import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Minus;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.core.TableScan;
import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.core.Values;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.util.BuiltInMethod;
@@ -39,16 +42,32 @@ public class RelMdMaxRowCount {
//~ Methods ----------------------------------------------------------------
public Double getMaxRowCount(Union rel) {
- double nRows = 0.0;
-
+ double rowCount = 0.0;
for (RelNode input : rel.getInputs()) {
Double partialRowCount = RelMetadataQuery.getMaxRowCount(input);
if (partialRowCount == null) {
return null;
}
- nRows += partialRowCount;
+ rowCount += partialRowCount;
+ }
+ return rowCount;
+ }
+
+ public Double getMaxRowCount(Intersect rel) {
+ // max row count is the smallest of the inputs
+ Double rowCount = null;
+ for (RelNode input : rel.getInputs()) {
+ Double partialRowCount = RelMetadataQuery.getMaxRowCount(input);
+ if (rowCount == null
+ || partialRowCount != null && partialRowCount < rowCount) {
+ rowCount = partialRowCount;
+ }
}
- return nRows;
+ return rowCount;
+ }
+
+ public Double getMaxRowCount(Minus rel) {
+ return RelMetadataQuery.getMaxRowCount(rel.getInput(0));
}
public Double getMaxRowCount(Filter rel) {
@@ -60,36 +79,54 @@ public class RelMdMaxRowCount {
}
public Double getMaxRowCount(Sort rel) {
- final Double rowCount = RelMetadataQuery.getMaxRowCount(rel.getInput());
- if (rowCount != null && rel.fetch != null) {
- final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+ Double rowCount = RelMetadataQuery.getMaxRowCount(rel.getInput());
+ if (rowCount == null) {
+ return null;
+ }
+ final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+ rowCount = Math.max(rowCount - offset, 0D);
+
+ if (rel.fetch != null) {
final int limit = RexLiteral.intValue(rel.fetch);
- final Double offsetLimit = new Double(offset + limit);
- // offsetLimit is smaller than rowCount of the input operator
- // thus, we return the offsetLimit
- if (offsetLimit < rowCount) {
- return offsetLimit;
+ if (limit < rowCount) {
+ return (double) limit;
}
}
return rowCount;
}
public Double getMaxRowCount(Aggregate rel) {
- return RelMetadataQuery.getMaxRowCount(rel.getInput());
+ if (rel.getGroupSet().isEmpty()) {
+ return 1D;
+ }
+ return RelMetadataQuery.getMaxRowCount(rel.getInput())
+ * rel.getGroupSets().size();
}
public Double getMaxRowCount(Join rel) {
Double left = RelMetadataQuery.getMaxRowCount(rel.getLeft());
- Double right = RelMetadataQuery.getMaxRowCount(rel.getLeft());
+ Double right = RelMetadataQuery.getMaxRowCount(rel.getRight());
if (left == null || right == null) {
return null;
- } else {
- return left * right;
}
+ if (left < 1D && rel.getJoinType().generatesNullsOnLeft()) {
+ left = 1D;
+ }
+ if (right < 1D && rel.getJoinType().generatesNullsOnRight()) {
+ right = 1D;
+ }
+ return left * right;
}
public Double getMaxRowCount(TableScan rel) {
- return rel.getRows();
+ // For typical tables, there is no upper bound to the number of rows.
+ return Double.POSITIVE_INFINITY;
+ }
+
+ public Double getMaxRowCount(Values values) {
+ // For Values, the maximum row count is the actual row count.
+ // This is especially useful if Values is empty.
+ return (double) values.getTuples().size();
}
// Catch-all rule when none of the others apply.
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
index 2d7beaf..aac3aae 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
@@ -19,6 +19,8 @@ package org.apache.calcite.rel.metadata;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Intersect;
+import org.apache.calcite.rel.core.Minus;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.SemiJoin;
import org.apache.calcite.rel.core.Sort;
@@ -41,16 +43,39 @@ public class RelMdRowCount {
//~ Methods ----------------------------------------------------------------
public Double getRowCount(Union rel) {
- double nRows = 0.0;
-
+ double rowCount = 0D;
for (RelNode input : rel.getInputs()) {
Double partialRowCount = RelMetadataQuery.getRowCount(input);
if (partialRowCount == null) {
return null;
}
- nRows += partialRowCount;
+ rowCount += partialRowCount;
+ }
+ return rowCount;
+ }
+
+ public Double getRowCount(Intersect rel) {
+ Double rowCount = null;
+ for (RelNode input : rel.getInputs()) {
+ Double partialRowCount = RelMetadataQuery.getRowCount(input);
+ if (rowCount == null
+ || partialRowCount != null && partialRowCount < rowCount) {
+ rowCount = partialRowCount;
+ }
+ }
+ return rowCount;
+ }
+
+ public Double getRowCount(Minus rel) {
+ Double rowCount = null;
+ for (RelNode input : rel.getInputs()) {
+ Double partialRowCount = RelMetadataQuery.getRowCount(input);
+ if (rowCount == null
+ || partialRowCount != null && partialRowCount < rowCount) {
+ rowCount = partialRowCount;
+ }
}
- return nRows;
+ return rowCount;
}
public Double getRowCount(Filter rel) {
@@ -66,15 +91,17 @@ public class RelMdRowCount {
}
public Double getRowCount(Sort rel) {
- final Double rowCount = RelMetadataQuery.getRowCount(rel.getInput());
- if (rowCount != null && rel.fetch != null) {
- final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+ Double rowCount = RelMetadataQuery.getRowCount(rel.getInput());
+ if (rowCount == null) {
+ return null;
+ }
+ final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+ rowCount = Math.max(rowCount - offset, 0D);
+
+ if (rel.fetch != null) {
final int limit = RexLiteral.intValue(rel.fetch);
- final Double offsetLimit = new Double(offset + limit);
- // offsetLimit is smaller than rowCount of the input operator
- // thus, we return the offsetLimit
- if (offsetLimit < rowCount) {
- return offsetLimit;
+ if (limit < rowCount) {
+ return (double) limit;
}
}
return rowCount;
@@ -103,10 +130,13 @@ public class RelMdRowCount {
groupKey,
null);
if (distinctRowCount == null) {
- return RelMetadataQuery.getRowCount(rel.getInput()) / 10;
- } else {
- return distinctRowCount;
+ distinctRowCount = RelMetadataQuery.getRowCount(rel.getInput()) / 10D;
}
+
+ // Grouping sets multiply
+ distinctRowCount *= rel.getGroupSets().size();
+
+ return distinctRowCount;
}
// Catch-all rule when none of the others apply.
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
index 24bef2f..d9ea2a2 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
@@ -22,7 +22,9 @@ import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Minus;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.SemiJoin;
import org.apache.calcite.rex.RexBuilder;
@@ -445,8 +447,8 @@ public class RelMdUtil {
RexBuilder rexBuilder,
RexNode pred1,
RexNode pred2) {
- final List<RexNode> unionList = new ArrayList<RexNode>();
- final Set<String> strings = new HashSet<String>();
+ final List<RexNode> unionList = new ArrayList<>();
+ final Set<String> strings = new HashSet<>();
for (RexNode rex : RelOptUtil.conjunctions(pred1)) {
if (strings.add(rex.toString())) {
@@ -477,7 +479,7 @@ public class RelMdUtil {
RexNode pred2) {
List<RexNode> list1 = RelOptUtil.conjunctions(pred1);
List<RexNode> list2 = RelOptUtil.conjunctions(pred2);
- List<RexNode> minusList = new ArrayList<RexNode>();
+ List<RexNode> minusList = new ArrayList<>();
for (RexNode rex1 : list1) {
boolean add = true;
@@ -629,9 +631,9 @@ public class RelMdUtil {
RexNode leftPred = null;
RexNode rightPred = null;
if (predicate != null) {
- List<RexNode> leftFilters = new ArrayList<RexNode>();
- List<RexNode> rightFilters = new ArrayList<RexNode>();
- List<RexNode> joinFilters = new ArrayList<RexNode>();
+ List<RexNode> leftFilters = new ArrayList<>();
+ List<RexNode> rightFilters = new ArrayList<>();
+ List<RexNode> joinFilters = new ArrayList<>();
List<RexNode> predList = RelOptUtil.conjunctions(predicate);
RelOptUtil.classifyFilters(
@@ -676,6 +678,53 @@ public class RelMdUtil {
RelMetadataQuery.getRowCount(joinRel));
}
+ /** Returns an estimate of the number of rows returned by a {@link Minus}. */
+ public static double getMinusRowCount(Minus minus) {
+ // REVIEW jvs 30-May-2005: I just pulled this out of a hat.
+ final List<RelNode> inputs = minus.getInputs();
+ double dRows = RelMetadataQuery.getRowCount(inputs.get(0));
+ for (int i = 1; i < inputs.size(); i++) {
+ dRows -= 0.5 * RelMetadataQuery.getRowCount(inputs.get(i));
+ }
+ if (dRows < 0) {
+ dRows = 0;
+ }
+ return dRows;
+ }
+
+ /** Returns an estimate of the number of rows returned by a {@link Join}. */
+ public static Double getJoinRowCount(Join join, RexNode condition) {
+ // Row count estimates of 0 will be rounded up to 1.
+ // So, use maxRowCount where the product is very small.
+ final Double left = RelMetadataQuery.getRowCount(join.getLeft());
+ final Double right = RelMetadataQuery.getRowCount(join.getRight());
+ if (left == null || right == null) {
+ return null;
+ }
+ if (left <= 1D || right <= 1D) {
+ Double max = RelMetadataQuery.getMaxRowCount(join);
+ if (max != null && max <= 1D) {
+ return max;
+ }
+ }
+ double product = left * right;
+
+ // TODO: correlation factor
+ return product * RelMetadataQuery.getSelectivity(join, condition);
+ }
+
+ /** Returns an estimate of the number of rows returned by a
+ * {@link SemiJoin}. */
+ public static Double getSemiJoinRowCount(RelNode left, RelNode right,
+ JoinRelType joinType, RexNode condition) {
+ // TODO: correlation factor
+ final Double leftCount = RelMetadataQuery.getRowCount(left);
+ if (leftCount == null) {
+ return null;
+ }
+ return leftCount * RexUtil.getSelectivity(condition);
+ }
+
//~ Inner Classes ----------------------------------------------------------
/** Visitor that walks over a scalar expression and computes the
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
index 43bbefe..44d0724 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
@@ -89,7 +89,7 @@ public abstract class RelMetadataQuery {
/**
* Returns the
- * {@link BuiltInMetadata.RowCount#getRowCount()}
+ * {@link BuiltInMetadata.MaxRowCount#getMaxRowCount()}
* statistic.
*
* @param rel the relational expression
@@ -97,7 +97,7 @@ public abstract class RelMetadataQuery {
*/
public static Double getMaxRowCount(RelNode rel) {
final BuiltInMetadata.MaxRowCount metadata =
- rel.metadata(BuiltInMetadata.MaxRowCount.class);
+ rel.metadata(BuiltInMetadata.MaxRowCount.class);
return metadata.getMaxRowCount();
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
index 962668f..7878917 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
@@ -103,7 +103,7 @@ import java.util.Map;
*/
public interface SqlValidator {
/** Whether to follow the SQL standard strictly. */
- boolean STRICT = Util.first(Boolean.getBoolean("calcite.strict.sql"), false);
+ boolean STRICT = Util.getBooleanProperty("calcite.strict.sql");
//~ Methods ----------------------------------------------------------------
@@ -478,7 +478,7 @@ public interface SqlValidator {
* <li>In FROM ({@link #getFromScope} , you can only see 'foo'.
*
* <li>In WHERE ({@link #getWhereScope}), GROUP BY ({@link #getGroupScope}),
- * SELECT ({@link #getSelectScope}), and the ON clause of the JOIN
+ * SELECT ({@code getSelectScope}), and the ON clause of the JOIN
* ({@link #getJoinScope}) you can see 'emp', 'dept', and 'foo'.
*
* <li>In ORDER BY ({@link #getOrderScope}), you can see the column alias 'x';
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/main/java/org/apache/calcite/util/Util.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java
index 7091855..eea3e47 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -1913,12 +1913,67 @@ public class Util {
};
}
- public static <T> T first(T t0, T t1) {
- return t0 != null ? t0 : t1;
+ /** Returns the first value if it is not null,
+ * otherwise the second value.
+ *
+ * <p>The result may be null.
+ *
+ * <p>Equivalent to the Elvis operator ({@code ?:}) of languages such as
+ * Groovy or PHP. */
+ public static <T> T first(T v0, T v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Double} value,
+ * using a given default value if it is null. */
+ public static double first(Double v0, double v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Float} value,
+ * using a given default value if it is null. */
+ public static float first(Float v0, float v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Integer} value,
+ * using a given default value if it is null. */
+ public static int first(Integer v0, int v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Long} value,
+ * using a given default value if it is null. */
+ public static long first(Long v0, long v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Boolean} value,
+ * using a given default value if it is null. */
+ public static boolean first(Boolean v0, boolean v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Short} value,
+ * using a given default value if it is null. */
+ public static short first(Short v0, short v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Character} value,
+ * using a given default value if it is null. */
+ public static char first(Character v0, char v1) {
+ return v0 != null ? v0 : v1;
+ }
+
+ /** Unboxes a {@link Byte} value,
+ * using a given default value if it is null. */
+ public static byte first(Byte v0, byte v1) {
+ return v0 != null ? v0 : v1;
}
- public static <T> Iterable<T> orEmpty(Iterable<T> t0) {
- return t0 != null ? t0 : ImmutableList.<T>of();
+ public static <T> Iterable<T> orEmpty(Iterable<T> v0) {
+ return v0 != null ? v0 : ImmutableList.<T>of();
}
/** Returns the last element of a list.
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index aebf192..f9e2bea 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -112,7 +112,7 @@ public class CalciteAssert {
/** Whether to enable slow tests. Default is false. */
public static final boolean ENABLE_SLOW =
- Util.first(Boolean.getBoolean("calcite.test.slow"), false);
+ Util.getBooleanProperty("calcite.test.slow");
private static final DateFormat UTC_DATE_FORMAT;
private static final DateFormat UTC_TIME_FORMAT;
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
index eccedb2..71b3115 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -170,7 +170,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "EMP" table.
final MockTable empTable =
- MockTable.create(this, salesSchema, "EMP", false);
+ MockTable.create(this, salesSchema, "EMP", false, 14);
empTable.addColumn("EMPNO", intType);
empTable.addColumn("ENAME", varchar20Type);
empTable.addColumn("JOB", varchar10Type);
@@ -183,13 +183,13 @@ public class MockCatalogReader implements Prepare.CatalogReader {
registerTable(empTable);
// Register "DEPT" table.
- MockTable deptTable = MockTable.create(this, salesSchema, "DEPT", false);
+ MockTable deptTable = MockTable.create(this, salesSchema, "DEPT", false, 4);
deptTable.addColumn("DEPTNO", intType);
deptTable.addColumn("NAME", varchar10Type);
registerTable(deptTable);
// Register "BONUS" table.
- MockTable bonusTable = MockTable.create(this, salesSchema, "BONUS", false);
+ MockTable bonusTable = MockTable.create(this, salesSchema, "BONUS", false, 0);
bonusTable.addColumn("ENAME", varchar20Type);
bonusTable.addColumn("JOB", varchar10Type);
bonusTable.addColumn("SAL", intType);
@@ -197,8 +197,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
registerTable(bonusTable);
// Register "SALGRADE" table.
- MockTable salgradeTable = MockTable.create(this, salesSchema, "SALGRADE",
- false);
+ MockTable salgradeTable = MockTable.create(this, salesSchema, "SALGRADE", false, 5);
salgradeTable.addColumn("GRADE", intType);
salgradeTable.addColumn("LOSAL", intType);
salgradeTable.addColumn("HISAL", intType);
@@ -206,7 +205,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "EMP_ADDRESS" table
MockTable contactAddressTable =
- MockTable.create(this, salesSchema, "EMP_ADDRESS", false);
+ MockTable.create(this, salesSchema, "EMP_ADDRESS", false, 26);
contactAddressTable.addColumn("EMPNO", intType);
contactAddressTable.addColumn("HOME_ADDRESS", addressType);
contactAddressTable.addColumn("MAILING_ADDRESS", addressType);
@@ -218,7 +217,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "CONTACT" table.
MockTable contactTable = MockTable.create(this, customerSchema, "CONTACT",
- false);
+ false, 1000);
contactTable.addColumn("CONTACTNO", intType);
contactTable.addColumn("FNAME", varchar10Type);
contactTable.addColumn("LNAME", varchar10Type);
@@ -228,7 +227,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "ACCOUNT" table.
MockTable accountTable = MockTable.create(this, customerSchema, "ACCOUNT",
- false);
+ false, 457);
accountTable.addColumn("ACCTNO", intType);
accountTable.addColumn("TYPE", varchar20Type);
accountTable.addColumn("BALANCE", intType);
@@ -236,7 +235,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "ORDERS" stream.
MockTable ordersStream = MockTable.create(this, salesSchema, "ORDERS",
- true);
+ true, Double.POSITIVE_INFINITY);
ordersStream.addColumn("ROWTIME", timestampType);
ordersStream.addMonotonic("ROWTIME");
ordersStream.addColumn("PRODUCTID", intType);
@@ -245,7 +244,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// Register "SHIPMENTS" stream.
MockTable shipmentsStream = MockTable.create(this, salesSchema, "SHIPMENTS",
- true);
+ true, Double.POSITIVE_INFINITY);
shipmentsStream.addColumn("ROWTIME", timestampType);
shipmentsStream.addMonotonic("ROWTIME");
shipmentsStream.addColumn("ORDERID", intType);
@@ -256,7 +255,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
// but "DEPTNO" not visible and set to 20 by default
// and "SAL" is visible but must be greater than 1000
MockTable emp20View = new MockTable(this, salesSchema.getCatalogName(),
- salesSchema.name, "EMP_20", false) {
+ salesSchema.name, "EMP_20", false, 600) {
private final Table table = empTable.unwrap(Table.class);
private final ImmutableIntList mapping =
ImmutableIntList.of(0, 1, 2, 3, 4, 5, 6, 8);
@@ -552,6 +551,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
public static class MockTable implements Prepare.PreparingTable {
protected final MockCatalogReader catalogReader;
private final boolean stream;
+ private final double rowCount;
private final List<Map.Entry<String, RelDataType>> columnList =
Lists.newArrayList();
private RelDataType rowType;
@@ -560,17 +560,18 @@ public class MockCatalogReader implements Prepare.CatalogReader {
private final Set<String> monotonicColumnSet = Sets.newHashSet();
public MockTable(MockCatalogReader catalogReader, String catalogName,
- String schemaName, String name, boolean stream) {
+ String schemaName, String name, boolean stream, double rowCount) {
this.catalogReader = catalogReader;
this.stream = stream;
+ this.rowCount = rowCount;
this.names = ImmutableList.of(catalogName, schemaName, name);
}
public static MockTable create(MockCatalogReader catalogReader,
- MockSchema schema, String name, boolean stream) {
+ MockSchema schema, String name, boolean stream, double rowCount) {
MockTable table =
new MockTable(catalogReader, schema.getCatalogName(), schema.name,
- name, stream);
+ name, stream, rowCount);
schema.addTable(name);
return table;
}
@@ -611,7 +612,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
}
public double getRowCount() {
- return 0;
+ return rowCount;
}
public RelOptSchema getRelOptSchema() {
@@ -676,7 +677,7 @@ public class MockCatalogReader implements Prepare.CatalogReader {
public RelOptTable extend(List<RelDataTypeField> extendedFields) {
final MockTable table = new MockTable(catalogReader, names.get(0),
- names.get(1), names.get(2), stream);
+ names.get(1), names.get(2), stream, rowCount);
table.columnList.addAll(columnList);
table.columnList.addAll(extendedFields);
table.onRegister(catalogReader.typeFactory);
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index 6b0451e..98b3a42 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -85,6 +85,7 @@ import java.util.Set;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -113,9 +114,9 @@ public class RelMetadataTest extends SqlToRelTestBase {
private static final double DEFAULT_SELECTIVITY = 0.25;
- private static final double EMP_SIZE = 1000.0;
+ private static final double EMP_SIZE = 14d;
- private static final double DEPT_SIZE = 100.0;
+ private static final double DEPT_SIZE = 4d;
//~ Methods ----------------------------------------------------------------
@@ -431,57 +432,222 @@ public class RelMetadataTest extends SqlToRelTestBase {
double expected) {
RelNode rel = convertSql(sql);
Double result = RelMetadataQuery.getRowCount(rel);
- assertTrue(result != null);
+ assertThat(result, notNullValue());
+ assertEquals(expected, result, 0d);
+ }
+
+ private void checkMaxRowCount(
+ String sql,
+ double expected) {
+ RelNode rel = convertSql(sql);
+ Double result = RelMetadataQuery.getMaxRowCount(rel);
+ assertThat(result, notNullValue());
assertEquals(expected, result, 0d);
}
- @Ignore
@Test public void testRowCountEmp() {
- checkRowCount(
- "select * from emp",
- EMP_SIZE);
+ final String sql = "select * from emp";
+ checkRowCount(sql, EMP_SIZE);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
}
- @Ignore
@Test public void testRowCountDept() {
- checkRowCount(
- "select * from dept",
- DEPT_SIZE);
+ final String sql = "select * from dept";
+ checkRowCount(sql, DEPT_SIZE);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountValues() {
+ final String sql = "select * from (values (1), (2)) as t(c)";
+ checkRowCount(sql, 2);
+ checkMaxRowCount(sql, 2);
}
- @Ignore
@Test public void testRowCountCartesian() {
- checkRowCount(
- "select * from emp,dept",
- EMP_SIZE * DEPT_SIZE);
+ final String sql = "select * from emp,dept";
+ checkRowCount(sql, EMP_SIZE * DEPT_SIZE);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
}
- @Ignore
@Test public void testRowCountJoin() {
- checkRowCount(
- "select * from emp inner join dept on emp.deptno = dept.deptno",
- EMP_SIZE * DEPT_SIZE * DEFAULT_EQUAL_SELECTIVITY);
+ final String sql = "select * from emp\n"
+ + "inner join dept on emp.deptno = dept.deptno";
+ checkRowCount(sql, EMP_SIZE * DEPT_SIZE * DEFAULT_EQUAL_SELECTIVITY);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountJoinFinite() {
+ final String sql = "select * from (select * from emp limit 14) as emp\n"
+ + "inner join (select * from dept limit 4) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, EMP_SIZE * DEPT_SIZE * DEFAULT_EQUAL_SELECTIVITY);
+ checkMaxRowCount(sql, 56D); // 4 * 14
+ }
+
+ @Test public void testRowCountJoinEmptyFinite() {
+ final String sql = "select * from (select * from emp limit 0) as emp\n"
+ + "inner join (select * from dept limit 4) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, 1D); // 0, rounded up to row count's minimum 1
+ checkMaxRowCount(sql, 0D); // 0 * 4
+ }
+
+ @Test public void testRowCountLeftJoinEmptyFinite() {
+ final String sql = "select * from (select * from emp limit 0) as emp\n"
+ + "left join (select * from dept limit 4) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, 1D); // 0, rounded up to row count's minimum 1
+ checkMaxRowCount(sql, 0D); // 0 * 4
+ }
+
+ @Test public void testRowCountRightJoinEmptyFinite() {
+ final String sql = "select * from (select * from emp limit 0) as emp\n"
+ + "right join (select * from dept limit 4) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, 1D); // 0, rounded up to row count's minimum 1
+ checkMaxRowCount(sql, 4D); // 1 * 4
+ }
+
+ @Test public void testRowCountJoinFiniteEmpty() {
+ final String sql = "select * from (select * from emp limit 7) as emp\n"
+ + "inner join (select * from dept limit 0) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, 1D); // 0, rounded up to row count's minimum 1
+ checkMaxRowCount(sql, 0D); // 7 * 0
+ }
+
+ @Test public void testRowCountJoinEmptyEmpty() {
+ final String sql = "select * from (select * from emp limit 0) as emp\n"
+ + "inner join (select * from dept limit 0) as dept\n"
+ + "on emp.deptno = dept.deptno";
+ checkRowCount(sql, 1D); // 0, rounded up to row count's minimum 1
+ checkMaxRowCount(sql, 0D); // 0 * 0
}
- @Ignore
@Test public void testRowCountUnion() {
- checkRowCount(
- "select ename from emp union all select name from dept",
- EMP_SIZE + DEPT_SIZE);
+ final String sql = "select ename from emp\n"
+ + "union all\n"
+ + "select name from dept";
+ checkRowCount(sql, EMP_SIZE + DEPT_SIZE);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountUnionOnFinite() {
+ final String sql = "select ename from (select * from emp limit 100)\n"
+ + "union all\n"
+ + "select name from (select * from dept limit 40)";
+ checkRowCount(sql, EMP_SIZE + DEPT_SIZE);
+ checkMaxRowCount(sql, 140D);
+ }
+
+ @Test public void testRowCountIntersectOnFinite() {
+ final String sql = "select ename from (select * from emp limit 100)\n"
+ + "intersect\n"
+ + "select name from (select * from dept limit 40)";
+ checkRowCount(sql, Math.min(EMP_SIZE, DEPT_SIZE));
+ checkMaxRowCount(sql, 40D);
+ }
+
+ @Test public void testRowCountMinusOnFinite() {
+ final String sql = "select ename from (select * from emp limit 100)\n"
+ + "except\n"
+ + "select name from (select * from dept limit 40)";
+ checkRowCount(sql, 4D);
+ checkMaxRowCount(sql, 100D);
}
- @Ignore
@Test public void testRowCountFilter() {
- checkRowCount(
- "select * from emp where ename='Mathilda'",
- EMP_SIZE * DEFAULT_EQUAL_SELECTIVITY);
+ final String sql = "select * from emp where ename='Mathilda'";
+ checkRowCount(sql, EMP_SIZE * DEFAULT_EQUAL_SELECTIVITY);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountFilterOnFinite() {
+ final String sql = "select * from (select * from emp limit 10)\n"
+ + "where ename='Mathilda'";
+ checkRowCount(sql, 10D * DEFAULT_EQUAL_SELECTIVITY);
+ checkMaxRowCount(sql, 10D);
}
- @Ignore
@Test public void testRowCountSort() {
- checkRowCount(
- "select * from emp order by ename",
- EMP_SIZE);
+ final String sql = "select * from emp order by ename";
+ checkRowCount(sql, EMP_SIZE);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountSortHighLimit() {
+ final String sql = "select * from emp order by ename limit 123456";
+ checkRowCount(sql, EMP_SIZE);
+ checkMaxRowCount(sql, 123456D);
+ }
+
+ @Test public void testRowCountSortHighOffset() {
+ final String sql = "select * from emp order by ename offset 123456";
+ checkRowCount(sql, 1D);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountSortHighOffsetLimit() {
+ final String sql = "select * from emp order by ename limit 5 offset 123456";
+ checkRowCount(sql, 1D);
+ checkMaxRowCount(sql, 5D);
+ }
+
+ @Test public void testRowCountSortLimit() {
+ final String sql = "select * from emp order by ename limit 10";
+ checkRowCount(sql, 10d);
+ checkMaxRowCount(sql, 10d);
+ }
+
+ @Test public void testRowCountSortLimit0() {
+ final String sql = "select * from emp order by ename limit 10";
+ checkRowCount(sql, 10d);
+ checkMaxRowCount(sql, 10d);
+ }
+
+ @Test public void testRowCountSortLimitOffset() {
+ final String sql = "select * from emp order by ename limit 10 offset 5";
+ checkRowCount(sql, 9D); // 14 - 5
+ checkMaxRowCount(sql, 10d);
+ }
+
+ @Test public void testRowCountSortLimitOffsetOnFinite() {
+ final String sql = "select * from (select * from emp limit 12)\n"
+ + "order by ename limit 20 offset 5";
+ checkRowCount(sql, 7d);
+ checkMaxRowCount(sql, 7d);
+ }
+
+ @Test public void testRowCountAggregate() {
+ final String sql = "select deptno from emp group by deptno";
+ checkRowCount(sql, 1.4D);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountAggregateGroupingSets() {
+ final String sql = "select deptno from emp\n"
+ + "group by grouping sets ((deptno), (empno, deptno))";
+ checkRowCount(sql, 2.8D); // EMP_SIZE / 10 * 2
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountAggregateGroupingSetsOneEmpty() {
+ final String sql = "select deptno from emp\n"
+ + "group by grouping sets ((deptno), ())";
+ checkRowCount(sql, 2.8D);
+ checkMaxRowCount(sql, Double.POSITIVE_INFINITY);
+ }
+
+ @Test public void testRowCountAggregateEmptyKey() {
+ final String sql = "select count(*) from emp";
+ checkRowCount(sql, 1D);
+ checkMaxRowCount(sql, 1D);
+ }
+
+ @Test public void testRowCountAggregateEmptyKeyOnEmptyTable() {
+ final String sql = "select count(*) from (select * from emp limit 0)";
+ checkRowCount(sql, 1D);
+ checkMaxRowCount(sql, 1D);
}
private void checkFilterSelectivity(
@@ -584,11 +750,21 @@ public class RelMetadataTest extends SqlToRelTestBase {
@Test public void testDistinctRowCountTable() {
// no unique key information is available so return null
RelNode rel = convertSql("select * from emp where deptno = 10");
- ImmutableBitSet groupKey = ImmutableBitSet.of();
+ ImmutableBitSet groupKey =
+ ImmutableBitSet.of(rel.getRowType().getFieldNames().indexOf("DEPTNO"));
+ Double result =
+ RelMetadataQuery.getDistinctRowCount(
+ rel, groupKey, null);
+ assertThat(result, nullValue());
+ }
+
+ @Test public void testDistinctRowCountTableEmptyKey() {
+ RelNode rel = convertSql("select * from emp where deptno = 10");
+ ImmutableBitSet groupKey = ImmutableBitSet.of(); // empty key
Double result =
RelMetadataQuery.getDistinctRowCount(
rel, groupKey, null);
- assertTrue(result == null);
+ assertThat(result, is(1D));
}
/** Asserts that {@link RelMetadataQuery#getUniqueKeys(RelNode)}
http://git-wip-us.apache.org/repos/asf/calcite/blob/24b07471/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index ab21c8c..4c72ba5 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -413,17 +413,17 @@ public class RelOptRulesTest extends RelOptTestBase {
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-987">[CALCITE-987]
- * Implement SortUnionTransposeRule</a>. */
+ * Push limit 0 will result in an infinite loop</a>. */
@Test public void testSortUnionTranspose3() {
final HepProgram program =
- HepProgram.builder()
- .addRuleInstance(ProjectSetOpTransposeRule.INSTANCE)
- .addRuleInstance(SortUnionTransposeRule.MATCH_NULL_FETCH)
- .build();
+ HepProgram.builder()
+ .addRuleInstance(ProjectSetOpTransposeRule.INSTANCE)
+ .addRuleInstance(SortUnionTransposeRule.MATCH_NULL_FETCH)
+ .build();
final String sql = "select a.name from dept a\n"
- + "union all\n"
- + "select b.name from dept b\n"
- + "order by name limit 0";
+ + "union all\n"
+ + "select b.name from dept b\n"
+ + "order by name limit 0";
checkPlanning(program, sql);
}
@@ -1098,7 +1098,7 @@ public class RelOptRulesTest extends RelOptTestBase {
typeFactory.createSqlType(SqlTypeName.INTEGER);
for (int i = 0; i < 10; i++) {
String t = String.valueOf((char) ('A' + i));
- MockTable table = MockTable.create(this, schema, t, false);
+ MockTable table = MockTable.create(this, schema, t, false, 100);
table.addColumn(t, intType);
registerTable(table);
}