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/06/10 00:08:10 UTC
[6/7] incubator-calcite git commit: [CALCITE-748] Add RelBuilder,
builder for expressions in relational algebra
[CALCITE-748] Add RelBuilder, builder for expressions in relational algebra
Deprecate RelTraitSet argument to SortFactory.createSort.
Add RelProtoBuilder and use it in one planner rule, FilterAggregateTransposeRule.
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/6609cb1a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/6609cb1a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/6609cb1a
Branch: refs/heads/master
Commit: 6609cb1a30bf36b1223078e8ebaf7cc9f7289b7c
Parents: b181851
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jun 1 21:00:59 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jun 5 16:06:13 2015 -0700
----------------------------------------------------------------------
.../java/org/apache/calcite/plan/Contexts.java | 21 +-
.../org/apache/calcite/plan/RelOptRuleCall.java | 12 +-
.../calcite/prepare/CalcitePrepareImpl.java | 8 +-
.../apache/calcite/rel/core/RelFactories.java | 108 ++-
.../rel/rules/FilterAggregateTransposeRule.java | 30 +-
.../java/org/apache/calcite/schema/Path.java | 7 +
.../java/org/apache/calcite/schema/Schemas.java | 27 +-
.../apache/calcite/sql2rel/RelFieldTrimmer.java | 24 +-
.../org/apache/calcite/tools/Frameworks.java | 6 +-
.../org/apache/calcite/tools/RelBuilder.java | 958 +++++++++++++++++++
.../java/org/apache/calcite/util/Stacks.java | 17 +
.../calcite/examples/RelBuilderExample.java | 171 ++++
.../org/apache/calcite/test/CalciteSuite.java | 1 +
.../org/apache/calcite/test/RelBuilderTest.java | 624 ++++++++++++
.../apache/calcite/tools/FrameworksTest.java | 34 +
.../java/org/apache/calcite/util/UtilTest.java | 5 +
site/_docs/algebra.md | 317 ++++++
site/_docs/reference.md | 6 +-
site/_posts/2015-06-05-algebra-builder.md | 89 ++
19 files changed, 2419 insertions(+), 46 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/plan/Contexts.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/Contexts.java b/core/src/main/java/org/apache/calcite/plan/Contexts.java
index 5a4eb74..7612999 100644
--- a/core/src/main/java/org/apache/calcite/plan/Contexts.java
+++ b/core/src/main/java/org/apache/calcite/plan/Contexts.java
@@ -22,6 +22,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -56,7 +57,16 @@ public class Contexts {
return new WrapContext(o);
}
- /** Returns a context that wraps an object.
+ /** Returns a context that wraps an array of objects. */
+ public static Context of(Object... os) {
+ final List<Context> contexts = new ArrayList<>();
+ for (Object o : os) {
+ contexts.add(of(o));
+ }
+ return chain(contexts);
+ }
+
+ /** Returns a context that wraps a list of contexts.
*
* <p>A call to {@code unwrap(C)} will return the first object that is an
* instance of {@code C}.
@@ -65,6 +75,10 @@ public class Contexts {
* object. Thus this method can be used to chain contexts.
*/
public static Context chain(Context... contexts) {
+ return chain(ImmutableList.copyOf(contexts));
+ }
+
+ private static Context chain(Iterable<? extends Context> contexts) {
// Flatten any chain contexts in the list, and remove duplicates
final List<Context> list = Lists.newArrayList();
for (Context context : contexts) {
@@ -82,12 +96,15 @@ public class Contexts {
/** Recursively populates a list of contexts. */
private static void build(List<Context> list, Context context) {
+ if (context == EMPTY_CONTEXT || list.contains(context)) {
+ return;
+ }
if (context instanceof ChainContext) {
ChainContext chainContext = (ChainContext) context;
for (Context child : chainContext.contexts) {
build(list, child);
}
- } else if (!list.contains(context)) {
+ } else {
list.add(context);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
index 7b36cb9..9a1fb0d 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
@@ -17,6 +17,8 @@
package org.apache.calcite.plan;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.util.trace.CalciteTrace;
import com.google.common.collect.ImmutableList;
@@ -116,6 +118,7 @@ public abstract class RelOptRuleCall {
* @return matched relational expressions
* @deprecated Use {@link #getRelList()} or {@link #rel(int)}
*/
+ @Deprecated // to be removed before 2.0
public RelNode[] getRels() {
return rels;
}
@@ -151,7 +154,7 @@ public abstract class RelOptRuleCall {
* {@link org.apache.calcite.plan.RelOptRuleOperandChildPolicy#ANY},
* the children will have their
* own operands and therefore be easily available in the array returned by
- * the {@link #getRels} method, so this method returns null.
+ * the {@link #getRelList()} method, so this method returns null.
*
* <p>This method is for
* {@link org.apache.calcite.plan.RelOptRuleOperandChildPolicy#ANY},
@@ -209,6 +212,13 @@ public abstract class RelOptRuleCall {
public final void transformTo(RelNode rel) {
transformTo(rel, ImmutableMap.<RelNode, RelNode>of());
}
+
+ /** Creates a {@link org.apache.calcite.tools.RelBuilder} to be used by
+ * code within the call. The {@code protoBuilder} argument contains policies
+ * such as what implementation of {@link Filter} to create. */
+ public RelBuilder builder(RelBuilder.ProtoRelBuilder protoBuilder) {
+ return protoBuilder.create(rel(0).getCluster(), null);
+ }
}
// End RelOptRuleCall.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 9ebfe8a..d78f160 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -862,10 +862,14 @@ public class CalcitePrepareImpl implements CalcitePrepare {
final CalcitePrepare.Context prepareContext =
statement.createPrepareContext();
final JavaTypeFactory typeFactory = prepareContext.getTypeFactory();
+ final CalciteSchema schema =
+ action.getConfig().getDefaultSchema() != null
+ ? CalciteSchema.from(action.getConfig().getDefaultSchema())
+ : prepareContext.getRootSchema();
CalciteCatalogReader catalogReader =
- new CalciteCatalogReader(prepareContext.getRootSchema(),
+ new CalciteCatalogReader(schema.root(),
prepareContext.config().caseSensitive(),
- prepareContext.getDefaultSchemaPath(),
+ schema.path(null),
typeFactory);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RelOptPlanner planner =
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
index 2e2225a..787ab1f 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
@@ -17,6 +17,9 @@
package org.apache.calcite.rel.core;
+import org.apache.calcite.plan.Contexts;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
@@ -27,10 +30,15 @@ import org.apache.calcite.rel.logical.LogicalIntersect;
import org.apache.calcite.rel.logical.LogicalJoin;
import org.apache.calcite.rel.logical.LogicalMinus;
import org.apache.calcite.rel.logical.LogicalSort;
+import org.apache.calcite.rel.logical.LogicalTableScan;
import org.apache.calcite.rel.logical.LogicalUnion;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.util.ImmutableBitSet;
import com.google.common.collect.ImmutableList;
@@ -63,6 +71,27 @@ public class RelFactories {
public static final SetOpFactory DEFAULT_SET_OP_FACTORY =
new SetOpFactoryImpl();
+ public static final ValuesFactory DEFAULT_VALUES_FACTORY =
+ new ValuesFactoryImpl();
+
+ public static final TableScanFactory DEFAULT_TABLE_SCAN_FACTORY =
+ new TableScanFactoryImpl();
+
+ /** Creates a {@link RelBuilder} that will create logical relational
+ * expressions for everything.
+ */
+ public static final RelBuilder.ProtoRelBuilder DEFAULT_PROTO =
+ RelBuilder.proto(
+ Contexts.of(DEFAULT_PROJECT_FACTORY,
+ DEFAULT_FILTER_FACTORY,
+ DEFAULT_JOIN_FACTORY,
+ DEFAULT_SEMI_JOIN_FACTORY,
+ DEFAULT_SORT_FACTORY,
+ DEFAULT_AGGREGATE_FACTORY,
+ DEFAULT_SET_OP_FACTORY,
+ DEFAULT_VALUES_FACTORY,
+ DEFAULT_TABLE_SCAN_FACTORY));
+
private RelFactories() {
}
@@ -73,7 +102,7 @@ public class RelFactories {
*/
public interface ProjectFactory {
/** Creates a project. */
- RelNode createProject(RelNode child, List<? extends RexNode> childExprs,
+ RelNode createProject(RelNode input, List<? extends RexNode> childExprs,
List<String> fieldNames);
}
@@ -82,9 +111,9 @@ public class RelFactories {
* {@link org.apache.calcite.rel.logical.LogicalProject}.
*/
private static class ProjectFactoryImpl implements ProjectFactory {
- public RelNode createProject(RelNode child,
+ public RelNode createProject(RelNode input,
List<? extends RexNode> childExprs, List<String> fieldNames) {
- return RelOptUtil.createProject(child, childExprs, fieldNames);
+ return RelOptUtil.createProject(input, childExprs, fieldNames);
}
}
@@ -94,7 +123,11 @@ public class RelFactories {
*/
public interface SortFactory {
/** Creates a sort. */
- RelNode createSort(RelTraitSet traits, RelNode child,
+ RelNode createSort(RelNode input, RelCollation collation, RexNode offset,
+ RexNode fetch);
+
+ @Deprecated // to be removed before 2.0
+ RelNode createSort(RelTraitSet traits, RelNode input,
RelCollation collation, RexNode offset, RexNode fetch);
}
@@ -103,9 +136,15 @@ public class RelFactories {
* returns a vanilla {@link Sort}.
*/
private static class SortFactoryImpl implements SortFactory {
- public RelNode createSort(RelTraitSet traits, RelNode child,
+ public RelNode createSort(RelNode input, RelCollation collation,
+ RexNode offset, RexNode fetch) {
+ return LogicalSort.create(input, collation, offset, fetch);
+ }
+
+ @Deprecated // to be removed before 2.0
+ public RelNode createSort(RelTraitSet traits, RelNode input,
RelCollation collation, RexNode offset, RexNode fetch) {
- return LogicalSort.create(child, collation, offset, fetch);
+ return createSort(input, collation, offset, fetch);
}
}
@@ -146,7 +185,7 @@ public class RelFactories {
*/
public interface AggregateFactory {
/** Creates an aggregate. */
- RelNode createAggregate(RelNode child, boolean indicator,
+ RelNode createAggregate(RelNode input, boolean indicator,
ImmutableBitSet groupSet, ImmutableList<ImmutableBitSet> groupSets,
List<AggregateCall> aggCalls);
}
@@ -156,10 +195,10 @@ public class RelFactories {
* that returns a vanilla {@link LogicalAggregate}.
*/
private static class AggregateFactoryImpl implements AggregateFactory {
- public RelNode createAggregate(RelNode child, boolean indicator,
+ public RelNode createAggregate(RelNode input, boolean indicator,
ImmutableBitSet groupSet, ImmutableList<ImmutableBitSet> groupSets,
List<AggregateCall> aggCalls) {
- return LogicalAggregate.create(child, indicator,
+ return LogicalAggregate.create(input, indicator,
groupSet, groupSets, aggCalls);
}
}
@@ -170,7 +209,7 @@ public class RelFactories {
*/
public interface FilterFactory {
/** Creates a filter. */
- RelNode createFilter(RelNode child, RexNode condition);
+ RelNode createFilter(RelNode input, RexNode condition);
}
/**
@@ -178,8 +217,8 @@ public class RelFactories {
* returns a vanilla {@link LogicalFilter}.
*/
private static class FilterFactoryImpl implements FilterFactory {
- public RelNode createFilter(RelNode child, RexNode condition) {
- return LogicalFilter.create(child, condition);
+ public RelNode createFilter(RelNode input, RexNode condition) {
+ return LogicalFilter.create(input, condition);
}
}
@@ -247,6 +286,51 @@ public class RelFactories {
condition, joinInfo.leftKeys, joinInfo.rightKeys);
}
}
+
+ /**
+ * Can create a {@link Values} of the appropriate type for a rule's calling
+ * convention.
+ */
+ public interface ValuesFactory {
+ /**
+ * Creates a Values.
+ */
+ RelNode createValues(RelOptCluster cluster, RelDataType rowType,
+ List<ImmutableList<RexLiteral>> tuples);
+ }
+
+ /**
+ * Implementation of {@link ValuesFactory} that returns a
+ * {@link LogicalValues}.
+ */
+ private static class ValuesFactoryImpl implements ValuesFactory {
+ public RelNode createValues(RelOptCluster cluster, RelDataType rowType,
+ List<ImmutableList<RexLiteral>> tuples) {
+ return LogicalValues.create(cluster, rowType,
+ ImmutableList.copyOf(tuples));
+ }
+ }
+
+ /**
+ * Can create a {@link TableScan} of the appropriate type for a rule's calling
+ * convention.
+ */
+ public interface TableScanFactory {
+ /**
+ * Creates a {@link TableScan}.
+ */
+ RelNode createScan(RelOptCluster cluster, RelOptTable table);
+ }
+
+ /**
+ * Implementation of {@link TableScanFactory} that returns a
+ * {@link LogicalTableScan}.
+ */
+ private static class TableScanFactoryImpl implements TableScanFactory {
+ public RelNode createScan(RelOptCluster cluster, RelOptTable table) {
+ return LogicalTableScan.create(cluster, table);
+ }
+ }
}
// End RelFactories.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
index 3701a1a..d3de47f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.rel.rules;
+import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptUtil;
@@ -26,6 +27,7 @@ import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.util.ImmutableBitSet;
import com.google.common.collect.ImmutableList;
@@ -46,33 +48,40 @@ public class FilterAggregateTransposeRule extends RelOptRule {
*
* <p>It matches any kind of agg. or filter */
public static final FilterAggregateTransposeRule INSTANCE =
- new FilterAggregateTransposeRule(Filter.class,
- RelFactories.DEFAULT_FILTER_FACTORY,
+ new FilterAggregateTransposeRule(Filter.class, RelFactories.DEFAULT_PROTO,
Aggregate.class);
- private final RelFactories.FilterFactory filterFactory;
+ private final RelBuilder.ProtoRelBuilder protoBuilder;
//~ Constructors -----------------------------------------------------------
/**
- * Creates a PushFilterPastAggRule.
+ * Creates a FilterAggregateTransposeRule.
*
* <p>If {@code filterFactory} is null, creates the same kind of filter as
* matched in the rule. Similarly {@code aggregateFactory}.</p>
*/
public FilterAggregateTransposeRule(
Class<? extends Filter> filterClass,
- RelFactories.FilterFactory filterFactory,
+ RelBuilder.ProtoRelBuilder protoBuilder,
Class<? extends Aggregate> aggregateClass) {
super(
operand(filterClass,
operand(aggregateClass, any())));
- this.filterFactory = filterFactory;
+ this.protoBuilder = protoBuilder;
+ }
+
+ @Deprecated // to be removed before 2.0
+ public FilterAggregateTransposeRule(
+ Class<? extends Filter> filterClass,
+ RelFactories.FilterFactory filterFactory,
+ Class<? extends Aggregate> aggregateClass) {
+ this(filterClass, RelBuilder.proto(Contexts.of(filterFactory)),
+ aggregateClass);
}
//~ Methods ----------------------------------------------------------------
- // implement RelOptRule
public void onMatch(RelOptRuleCall call) {
final Filter filterRel = call.rel(0);
final Aggregate aggRel = call.rel(1);
@@ -112,13 +121,14 @@ public class FilterAggregateTransposeRule extends RelOptRule {
}
}
- RelNode rel = RelOptUtil.createFilter(aggRel.getInput(), pushedConditions,
- filterFactory);
+ final RelBuilder builder = call.builder(protoBuilder);
+ RelNode rel =
+ builder.push(aggRel.getInput()).filter(pushedConditions).build();
if (rel == aggRel.getInput(0)) {
return;
}
rel = aggRel.copy(aggRel.getTraitSet(), ImmutableList.of(rel));
- rel = RelOptUtil.createFilter(rel, remainingConditions, filterFactory);
+ rel = builder.push(rel).filter(remainingConditions).build();
call.transformTo(rel);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/schema/Path.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Path.java b/core/src/main/java/org/apache/calcite/schema/Path.java
index 7c363a0..1575f58 100644
--- a/core/src/main/java/org/apache/calcite/schema/Path.java
+++ b/core/src/main/java/org/apache/calcite/schema/Path.java
@@ -32,7 +32,14 @@ import java.util.RandomAccess;
* </ul>
*/
public interface Path extends List<Pair<String, Schema>>, RandomAccess {
+ /** Returns the parent path, or null if the path is empty. */
Path parent();
+
+ /** Returns the names of this path, not including the name of the root. */
+ List<String> names();
+
+ /** Returns the schemas of this path. */
+ List<Schema> schemas();
}
// End Path.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/schema/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index b634233..7c09ffb 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -514,6 +514,15 @@ public final class Schemas {
return new PathImpl(build);
}
+ /** Returns the path to get to a schema from its root. */
+ public static Path path(SchemaPlus schema) {
+ List<Pair<String, Schema>> list = new ArrayList<>();
+ for (SchemaPlus s = schema; s != null; s = s.getParentSchema()) {
+ list.add(Pair.<String, Schema>of(s.getName(), s));
+ }
+ return new PathImpl(ImmutableList.copyOf(Lists.reverse(list)));
+ }
+
/** Dummy data context that has no variables. */
private static class DummyDataContext implements DataContext {
private final CalciteConnection connection;
@@ -572,12 +581,28 @@ public final class Schemas {
return pairs.size();
}
- @Override public Path parent() {
+ public Path parent() {
if (pairs.isEmpty()) {
throw new IllegalArgumentException("at root");
}
return new PathImpl(pairs.subList(0, pairs.size() - 1));
}
+
+ public List<String> names() {
+ return new AbstractList<String>() {
+ public String get(int index) {
+ return pairs.get(index + 1).left;
+ }
+
+ public int size() {
+ return pairs.size() - 1;
+ }
+ };
+ }
+
+ public List<Schema> schemas() {
+ return Pair.right(pairs);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 99975b3..5e9e333 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -19,7 +19,6 @@ package org.apache.calcite.sql2rel;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
@@ -234,7 +233,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
}
final RelDataType rowType = input.getRowType();
List<RelDataTypeField> fieldList = rowType.getFieldList();
- final List<RexNode> exprList = new ArrayList<RexNode>();
+ final List<RexNode> exprList = new ArrayList<>();
final List<String> nameList = rowType.getFieldNames();
RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
assert trimResult.right.getSourceCount() == fieldList.size();
@@ -331,7 +330,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
// Which fields are required from the input?
final Set<RelDataTypeField> inputExtraFields =
- new LinkedHashSet<RelDataTypeField>(extraFields);
+ new LinkedHashSet<>(extraFields);
RelOptUtil.InputFinder inputFinder =
new RelOptUtil.InputFinder(inputExtraFields);
for (Ord<RexNode> ord : Ord.zip(project.getProjects())) {
@@ -436,7 +435,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
// We use the fields used by the consumer, plus any fields used in the
// filter.
final Set<RelDataTypeField> inputExtraFields =
- new LinkedHashSet<RelDataTypeField>(extraFields);
+ new LinkedHashSet<>(extraFields);
RelOptUtil.InputFinder inputFinder =
new RelOptUtil.InputFinder(inputExtraFields);
inputFinder.inputBitSet.addAll(fieldsUsed);
@@ -513,9 +512,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
final RelCollation newCollation =
sort.getTraitSet().canonize(RexUtil.apply(inputMapping, collation));
- final RelTraitSet newTraitSet = sort.getTraitSet().replace(newCollation);
- final RelNode newSort = sortFactory.createSort(
- newTraitSet, newInput, newCollation, sort.offset, sort.fetch);
+ final RelNode newSort =
+ sortFactory.createSort(newInput, newCollation, sort.offset, sort.fetch);
// The result has the same mapping as the input gave us. Sometimes we
// return fields that the consumer didn't ask for, because the filter
@@ -539,7 +537,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
// Add in fields used in the condition.
final Set<RelDataTypeField> combinedInputExtraFields =
- new LinkedHashSet<RelDataTypeField>(extraFields);
+ new LinkedHashSet<>(extraFields);
RelOptUtil.InputFinder inputFinder =
new RelOptUtil.InputFinder(combinedInputExtraFields);
inputFinder.inputBitSet.addAll(fieldsUsed);
@@ -563,9 +561,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
int offset = systemFieldCount;
int changeCount = 0;
int newFieldCount = newSystemFieldCount;
- List<RelNode> newInputs = new ArrayList<RelNode>(2);
- List<Mapping> inputMappings = new ArrayList<Mapping>();
- List<Integer> inputExtraFieldCounts = new ArrayList<Integer>();
+ final List<RelNode> newInputs = new ArrayList<>(2);
+ final List<Mapping> inputMappings = new ArrayList<>();
+ final List<Integer> inputExtraFieldCounts = new ArrayList<>();
for (RelNode input : join.getInputs()) {
final RelDataType inputRowType = input.getRowType();
final int inputFieldCount = inputRowType.getFieldCount();
@@ -688,7 +686,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
final Mapping mapping = createMapping(fieldsUsed, fieldCount);
// Create input with trimmed columns.
- final List<RelNode> newInputs = new ArrayList<RelNode>();
+ final List<RelNode> newInputs = new ArrayList<>();
for (RelNode input : setOp.getInputs()) {
TrimResult trimResult =
trimChild(setOp, input, fieldsUsed, extraFields);
@@ -914,7 +912,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
Set<RelDataTypeField> extraFields) {
final RelDataType rowType = tabFun.getRowType();
final int fieldCount = rowType.getFieldCount();
- List<RelNode> newInputs = new ArrayList<RelNode>();
+ final List<RelNode> newInputs = new ArrayList<>();
for (RelNode input : tabFun.getInputs()) {
final int inputFieldCount = input.getRowType().getFieldCount();
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/tools/Frameworks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Frameworks.java b/core/src/main/java/org/apache/calcite/tools/Frameworks.java
index a9efcc0..c73a1e2 100644
--- a/core/src/main/java/org/apache/calcite/tools/Frameworks.java
+++ b/core/src/main/java/org/apache/calcite/tools/Frameworks.java
@@ -101,12 +101,14 @@ public class Frameworks {
* @return Return value from action
*/
public static <R> R withPlanner(final PlannerAction<R> action, //
- FrameworkConfig config) {
+ final FrameworkConfig config) {
return withPrepare(
new Frameworks.PrepareAction<R>(config) {
public R apply(RelOptCluster cluster, RelOptSchema relOptSchema,
SchemaPlus rootSchema, CalciteServerStatement statement) {
- return action.apply(cluster, relOptSchema, rootSchema);
+ final CalciteSchema schema =
+ CalciteSchema.from(config.getDefaultSchema());
+ return action.apply(cluster, relOptSchema, schema.root().plus());
}
});
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
new file mode 100644
index 0000000..ce185d8
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -0,0 +1,958 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.tools;
+
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.plan.Context;
+import org.apache.calcite.plan.Contexts;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptSchema;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.server.CalciteServerStatement;
+import org.apache.calcite.sql.SqlAggFunction;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.NlsString;
+import org.apache.calcite.util.Stacks;
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import java.math.BigDecimal;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Builder for relational expressions.
+ *
+ * <p>{@code RelBuilder} does not make possible anything that you could not
+ * also accomplish by calling the factory methods of the particular relational
+ * expression. But it makes common tasks more straightforward and concise.
+ *
+ * <p>{@code RelBuilder} uses factories to create relational expressions.
+ * By default, it uses the default factories, which create logical relational
+ * expressions ({@link org.apache.calcite.rel.logical.LogicalFilter},
+ * {@link org.apache.calcite.rel.logical.LogicalProject} and so forth).
+ * But you could override those factories so that, say, {@code filter} creates
+ * instead a {@code HiveFilter}.
+ *
+ * <p>It is not thread-safe.
+ */
+public class RelBuilder {
+ private static final Function<RexNode, String> FN_TYPE =
+ new Function<RexNode, String>() {
+ public String apply(RexNode input) {
+ return input + ": " + input.getType();
+ }
+ };
+
+ private final RelOptCluster cluster;
+ private final RelOptSchema relOptSchema;
+ private final RelFactories.FilterFactory filterFactory;
+ private final RelFactories.ProjectFactory projectFactory;
+ private final RelFactories.AggregateFactory aggregateFactory;
+ private final RelFactories.SortFactory sortFactory;
+ private final RelFactories.SetOpFactory setOpFactory;
+ private final RelFactories.JoinFactory joinFactory;
+ private final RelFactories.ValuesFactory valuesFactory;
+ private final RelFactories.TableScanFactory scanFactory;
+ private final List<RelNode> stack = new ArrayList<>();
+
+ private RelBuilder(Context context, RelOptCluster cluster,
+ RelOptSchema relOptSchema) {
+ this.cluster = cluster;
+ this.relOptSchema = relOptSchema;
+ if (context == null) {
+ context = Contexts.EMPTY_CONTEXT;
+ }
+ this.aggregateFactory =
+ Util.first(context.unwrap(RelFactories.AggregateFactory.class),
+ RelFactories.DEFAULT_AGGREGATE_FACTORY);
+ this.filterFactory =
+ Util.first(context.unwrap(RelFactories.FilterFactory.class),
+ RelFactories.DEFAULT_FILTER_FACTORY);
+ this.projectFactory =
+ Util.first(context.unwrap(RelFactories.ProjectFactory.class),
+ RelFactories.DEFAULT_PROJECT_FACTORY);
+ this.sortFactory =
+ Util.first(context.unwrap(RelFactories.SortFactory.class),
+ RelFactories.DEFAULT_SORT_FACTORY);
+ this.setOpFactory =
+ Util.first(context.unwrap(RelFactories.SetOpFactory.class),
+ RelFactories.DEFAULT_SET_OP_FACTORY);
+ this.joinFactory =
+ Util.first(context.unwrap(RelFactories.JoinFactory.class),
+ RelFactories.DEFAULT_JOIN_FACTORY);
+ this.valuesFactory =
+ Util.first(context.unwrap(RelFactories.ValuesFactory.class),
+ RelFactories.DEFAULT_VALUES_FACTORY);
+ this.scanFactory =
+ Util.first(context.unwrap(RelFactories.TableScanFactory.class),
+ RelFactories.DEFAULT_TABLE_SCAN_FACTORY);
+ }
+
+ /** Creates a RelBuilder. */
+ public static RelBuilder create(FrameworkConfig config) {
+ final RelOptCluster[] clusters = {null};
+ final RelOptSchema[] relOptSchemas = {null};
+ Frameworks.withPrepare(
+ new Frameworks.PrepareAction<Void>(config) {
+ public Void apply(RelOptCluster cluster, RelOptSchema relOptSchema,
+ SchemaPlus rootSchema, CalciteServerStatement statement) {
+ clusters[0] = cluster;
+ relOptSchemas[0] = relOptSchema;
+ return null;
+ }
+ });
+ return new RelBuilder(config.getContext(), clusters[0], relOptSchemas[0]);
+ }
+
+ /** Returns the type factory. */
+ public RelDataTypeFactory getTypeFactory() {
+ return cluster.getTypeFactory();
+ }
+
+ /** Creates a {@link ProtoRelBuilder}, a partially-created RelBuilder.
+ * Just add a {@link RelOptCluster} and a {@link RelOptSchema} */
+ public static ProtoRelBuilder proto(final Context context) {
+ return new ProtoRelBuilder() {
+ public RelBuilder create(RelOptCluster cluster, RelOptSchema schema) {
+ return new RelBuilder(context, cluster, schema);
+ }
+ };
+ }
+
+ // Methods for manipulating the stack
+
+ /** Adds a relational expression to be the input to the next relational
+ * expression constructed.
+ *
+ * <p>This method is usual when you want to weave in relational expressions
+ * that are not supported by the builder. If, while creating such expressions,
+ * you need to use previously built expressions as inputs, call
+ * {@link #build()} to pop those inputs. */
+ public RelBuilder push(RelNode node) {
+ Stacks.push(stack, node);
+ return this;
+ }
+
+ /** Returns the final relational expression.
+ *
+ * <p>Throws if the stack is empty.
+ */
+ public RelNode build() {
+ if (stack.size() < 1) {
+ throw new IllegalArgumentException("expected stack size 1, but was "
+ + stack.size() + ": " + stack);
+ }
+ return Stacks.pop(stack);
+ }
+
+ /** Returns the relational expression at the top of the stack, but does not
+ * remove it. */
+ public RelNode peek() {
+ return Stacks.peek(stack);
+ }
+
+ /** Returns the relational expression {@code n} positions from the top of the
+ * stack, but does not remove it. */
+ public RelNode peek(int n) {
+ return Stacks.peek(n, stack);
+ }
+
+ // Methods that return scalar expressions
+
+ /** Creates a literal (constant expression). */
+ public RexNode literal(Object value) {
+ final RexBuilder rexBuilder = cluster.getRexBuilder();
+ if (value == null) {
+ return rexBuilder.constantNull();
+ } else if (value instanceof Boolean) {
+ return rexBuilder.makeLiteral((Boolean) value);
+ } else if (value instanceof BigDecimal) {
+ return rexBuilder.makeExactLiteral((BigDecimal) value);
+ } else if (value instanceof Float || value instanceof Double) {
+ return rexBuilder.makeApproxLiteral(
+ BigDecimal.valueOf(((Number) value).doubleValue()));
+ } else if (value instanceof Number) {
+ return rexBuilder.makeExactLiteral(
+ BigDecimal.valueOf(((Number) value).longValue()));
+ } else if (value instanceof String) {
+ return rexBuilder.makeLiteral((String) value);
+ } else {
+ throw new IllegalArgumentException("cannot convert " + value
+ + " (" + value.getClass() + ") to a constant");
+ }
+ }
+
+ /** Creates a reference to a field by name.
+ *
+ * <p>Equivalent to {@code field(1, 0, fieldName)}.
+ *
+ * @param fieldName Field name
+ */
+ public RexInputRef field(String fieldName) {
+ return field(1, 0, fieldName);
+ }
+
+ /** Creates a reference to a field of given input relational expression
+ * by name.
+ *
+ * @param inputCount Number of inputs
+ * @param inputOrdinal Input ordinal
+ * @param fieldName Field name
+ */
+ public RexInputRef field(int inputCount, int inputOrdinal, String fieldName) {
+ final RelNode input = peek(inputCount - 1 - inputOrdinal);
+ final RelDataType rowType = input.getRowType();
+ final int ordinal = rowType.getFieldNames().indexOf(fieldName);
+ if (ordinal < 0) {
+ throw new IllegalArgumentException("field [" + fieldName
+ + "] not found; input fields are: " + rowType.getFieldNames());
+ }
+ return field(inputCount, inputOrdinal, ordinal);
+ }
+
+ /** Creates a reference to an input field by ordinal.
+ *
+ * <p>Equivalent to {@code field(1, 0, ordinal)}.
+ *
+ * @param fieldOrdinal Field ordinal
+ */
+ public RexInputRef field(int fieldOrdinal) {
+ return field(1, 0, fieldOrdinal);
+ }
+
+ /** Creates a reference to a field of a given input relational expression
+ * by ordinal.
+ *
+ * @param inputCount Number of inputs
+ * @param inputOrdinal Input ordinal
+ * @param fieldOrdinal Field ordinal within input
+ */
+ public RexInputRef field(int inputCount, int inputOrdinal, int fieldOrdinal) {
+ final RelNode input = peek(inputCount - 1 - inputOrdinal);
+ final RelDataType rowType = input.getRowType();
+ if (fieldOrdinal < 0 || fieldOrdinal > rowType.getFieldCount()) {
+ throw new IllegalArgumentException("field ordinal [" + fieldOrdinal
+ + "] out of range; input fields are: " + rowType.getFieldNames());
+ }
+ return cluster.getRexBuilder().makeInputRef(input, fieldOrdinal);
+ }
+
+ /** Creates a call to a scalar operator. */
+ public RexNode call(SqlOperator operator, RexNode... operands) {
+ final RexBuilder builder = cluster.getRexBuilder();
+ final List<RexNode> operandList = ImmutableList.copyOf(operands);
+ final RelDataType type = builder.deriveReturnType(operator, operandList);
+ if (type == null) {
+ throw new IllegalArgumentException("cannot derive type: " + operator
+ + "; operands: " + Lists.transform(operandList, FN_TYPE));
+ }
+ return builder.makeCall(type, operator, operandList);
+ }
+
+ /** Creates a call to a scalar operator. */
+ public RexNode call(SqlOperator operator,
+ Iterable<? extends RexNode> operands) {
+ return cluster.getRexBuilder().makeCall(operator,
+ ImmutableList.copyOf(operands));
+ }
+
+ /** Creates an AND. */
+ public RexNode and(RexNode... operands) {
+ return and(ImmutableList.copyOf(operands));
+ }
+
+ /** Creates an AND. */
+ public RexNode and(Iterable<? extends RexNode> operands) {
+ return RexUtil.composeConjunction(cluster.getRexBuilder(), operands, false);
+ }
+
+ /** Creates an OR. */
+ public RexNode or(RexNode... operands) {
+ return or(ImmutableList.copyOf(operands));
+ }
+
+ /** Creates an OR. */
+ public RexNode or(Iterable<? extends RexNode> operands) {
+ return RexUtil.composeDisjunction(cluster.getRexBuilder(), operands, false);
+ }
+
+ /** Creates a NOT. */
+ public RexNode not(RexNode operand) {
+ return call(SqlStdOperatorTable.NOT, operand);
+ }
+
+ /** Creates an =. */
+ public RexNode equals(RexNode operand0, RexNode operand1) {
+ return call(SqlStdOperatorTable.EQUALS, operand0, operand1);
+ }
+
+ /** Creates a IS NULL. */
+ public RexNode isNull(RexNode operand) {
+ return call(SqlStdOperatorTable.IS_NULL, operand);
+ }
+
+ /** Creates a IS NOT NULL. */
+ public RexNode isNotNull(RexNode operand) {
+ return call(SqlStdOperatorTable.IS_NOT_NULL, operand);
+ }
+
+ /** Creates an expression that casts an expression to a given type. */
+ public RexNode cast(RexNode expr, SqlTypeName typeName) {
+ final RelDataType type = cluster.getTypeFactory().createSqlType(typeName);
+ return cluster.getRexBuilder().makeCast(type, expr);
+ }
+
+ /** Creates an expression that casts an expression to a type with a given name
+ * and precision or length. */
+ public RexNode cast(RexNode expr, SqlTypeName typeName, int precision) {
+ final RelDataType type =
+ cluster.getTypeFactory().createSqlType(typeName, precision);
+ return cluster.getRexBuilder().makeCast(type, expr);
+ }
+
+ /** Creates an expression that casts an expression to a type with a given
+ * name, precision and scale. */
+ public RexNode cast(RexNode expr, SqlTypeName typeName, int precision,
+ int scale) {
+ final RelDataType type =
+ cluster.getTypeFactory().createSqlType(typeName, precision, scale);
+ return cluster.getRexBuilder().makeCast(type, expr);
+ }
+
+ /**
+ * Returns an expression wrapped in an alias.
+ *
+ * @see #project
+ */
+ public RexNode alias(RexNode expr, String alias) {
+ return call(SqlStdOperatorTable.AS, expr, literal(alias));
+ }
+
+ /** Converts a sort expression to descending. */
+ public RexNode desc(RexNode node) {
+ return call(SqlStdOperatorTable.DESC, node);
+ }
+
+ /** Converts a sort expression to nulls last. */
+ public RexNode nullsLast(RexNode node) {
+ return call(SqlStdOperatorTable.NULLS_LAST, node);
+ }
+
+ /** Converts a sort expression to nulls first. */
+ public RexNode nullsFirst(RexNode node) {
+ return call(SqlStdOperatorTable.NULLS_FIRST, node);
+ }
+
+ // Methods that create group keys and aggregate calls
+
+ /** Creates an empty group key. */
+ public GroupKey groupKey() {
+ return groupKey(ImmutableList.<RexNode>of());
+ }
+
+ /** Creates a group key. */
+ public GroupKey groupKey(RexNode... nodes) {
+ return groupKey(ImmutableList.copyOf(nodes));
+ }
+
+ /** Creates a group key. */
+ public GroupKey groupKey(Iterable<? extends RexNode> nodes) {
+ return new GroupKeyImpl(ImmutableList.copyOf(nodes));
+ }
+
+ /** Creates a group key of fields identified by ordinal. */
+ public GroupKey groupKey(int... fieldOrdinals) {
+ final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
+ for (int fieldOrdinal : fieldOrdinals) {
+ builder.add(field(fieldOrdinal));
+ }
+ return groupKey(builder.build());
+ }
+
+ /** Creates a group key of fields identified by name. */
+ public GroupKey groupKey(String... fieldNames) {
+ final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
+ for (String fieldName : fieldNames) {
+ builder.add(field(fieldName));
+ }
+ return groupKey(builder.build());
+ }
+
+ /** Creates a call to an aggregate function. */
+ public AggCall aggregateCall(SqlAggFunction aggFunction,
+ boolean distinct, String alias, RexNode... operands) {
+ return new AggCallImpl(aggFunction, distinct, alias,
+ ImmutableList.copyOf(operands));
+ }
+
+ /** Creates a call to the COUNT aggregate function. */
+ public AggCall count(boolean distinct, String alias, RexNode... operands) {
+ return aggregateCall(SqlStdOperatorTable.COUNT, distinct, alias, operands);
+ }
+
+ /** Creates a call to the COUNT(*) aggregate function. */
+ public AggCall countStar(String alias) {
+ return aggregateCall(SqlStdOperatorTable.COUNT, false, alias);
+ }
+
+ /** Creates a call to the SUM aggregate function. */
+ public AggCall sum(boolean distinct, String alias, RexNode operand) {
+ return aggregateCall(SqlStdOperatorTable.SUM, distinct, alias, operand);
+ }
+
+ /** Creates a call to the MIN aggregate function. */
+ public AggCall min(String alias, RexNode operand) {
+ return aggregateCall(SqlStdOperatorTable.MIN, false, alias, operand);
+ }
+
+ /** Creates a call to the MAX aggregate function. */
+ public AggCall max(String alias, RexNode operand) {
+ return aggregateCall(SqlStdOperatorTable.MAX, false, alias, operand);
+ }
+
+ // Methods that create relational expressions
+
+ /** Creates a {@link org.apache.calcite.rel.core.TableScan} of the table
+ * with a given name.
+ *
+ * <p>Throws if the table does not exist within the current schema.
+ *
+ * <p>Returns this builder.
+ *
+ * @param tableName Name of table
+ */
+ public RelBuilder scan(String tableName) {
+ final RelOptTable relOptTable =
+ relOptSchema.getTableForMember(ImmutableList.of(tableName));
+ final RelNode scan = scanFactory.createScan(cluster, relOptTable);
+ push(scan);
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Filter} of an array of
+ * predicates.
+ *
+ * <p>The predicates are combined using AND,
+ * and optimized in a similar way to the {@link #and} method.
+ * If the result is TRUE no filter is created. */
+ public RelBuilder filter(RexNode... predicates) {
+ return filter(ImmutableList.copyOf(predicates));
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Filter} of a list of
+ * predicates.
+ *
+ * <p>The predicates are combined using AND,
+ * and optimized in a similar way to the {@link #and} method.
+ * If the result is TRUE no filter is created. */
+ public RelBuilder filter(Iterable<? extends RexNode> predicates) {
+ final RexNode x = RexUtil.composeConjunction(cluster.getRexBuilder(),
+ predicates, true);
+ if (x != null) {
+ final RelNode filter = filterFactory.createFilter(build(), x);
+ push(filter);
+ }
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Project} of the given list
+ * of expressions.
+ *
+ * <p>Infers all field names.
+ * If an expression projects an input field,
+ * or is a cast an input field,
+ * uses the input field name.
+ * If an expression is a call to
+ * {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#AS}
+ * (see {@link #alias}), removes the
+ * call but uses the intended alias.
+ * After the field names have been inferred, makes the
+ * field names unique by appending numeric suffixes. */
+ public RelBuilder project(List<RexNode> nodes) {
+ final List<String> names = new ArrayList<>();
+ final List<RexNode> exprList = Lists.newArrayList(nodes);
+ for (RexNode node : nodes) {
+ names.add(inferAlias(exprList, node));
+ }
+ final RelNode project =
+ projectFactory.createProject(build(), ImmutableList.copyOf(exprList),
+ names);
+ push(project);
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Project} of the given
+ * expressions. */
+ public RelBuilder project(RexNode... nodes) {
+ return project(ImmutableList.copyOf(nodes));
+ }
+
+ /** Infers the alias of an expression.
+ *
+ * <p>If the expression was created by {@link #alias}, replaces the expression
+ * in the project list.
+ */
+ private String inferAlias(List<RexNode> exprList, RexNode expr) {
+ switch (expr.getKind()) {
+ case INPUT_REF:
+ final RexInputRef ref = (RexInputRef) expr;
+ return peek(0).getRowType().getFieldNames().get(ref.getIndex());
+ case CAST:
+ return inferAlias(exprList, ((RexCall) expr).getOperands().get(0));
+ case AS:
+ final RexCall call = (RexCall) expr;
+ for (;;) {
+ final int i = exprList.indexOf(expr);
+ if (i < 0) {
+ break;
+ }
+ exprList.set(i, call.getOperands().get(0));
+ }
+ return ((NlsString) ((RexLiteral) call.getOperands().get(1)).getValue())
+ .getValue();
+ default:
+ return null;
+ }
+ }
+
+ /** Creates an {@link org.apache.calcite.rel.core.Aggregate} that makes the
+ * relational expression distinct on all fields. */
+ public RelBuilder distinct() {
+ return aggregate(groupKey());
+ }
+
+ /** Creates an {@link org.apache.calcite.rel.core.Aggregate} with an array of
+ * calls. */
+ public RelBuilder aggregate(GroupKey groupKey, AggCall... aggCalls) {
+ return aggregate(groupKey, ImmutableList.copyOf(aggCalls));
+ }
+
+ /** Creates an {@link org.apache.calcite.rel.core.Aggregate} with a list of
+ * calls. */
+ public RelBuilder aggregate(GroupKey groupKey, Iterable<AggCall> aggCalls) {
+ final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
+ final RelDataType inputRowType = peek().getRowType();
+ final List<RexNode> extraNodes = projects(inputRowType);
+ for (RexNode node : ((GroupKeyImpl) groupKey).nodes) {
+ builder.set(registerExpression(extraNodes, node));
+ }
+ final ImmutableBitSet groupSet = builder.build();
+ for (AggCall aggCall : aggCalls) {
+ final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
+ for (RexNode operand : aggCall1.operands) {
+ registerExpression(extraNodes, operand);
+ }
+ }
+ if (extraNodes.size() > inputRowType.getFieldCount()) {
+ project(extraNodes);
+ }
+ final RelNode r = build();
+ final List<AggregateCall> aggregateCalls = new ArrayList<>();
+ for (AggCall aggCall : aggCalls) {
+ final List<Integer> args = new ArrayList<>();
+ final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
+ for (RexNode operand : aggCall1.operands) {
+ args.add(registerExpression(extraNodes, operand));
+ }
+ aggregateCalls.add(
+ AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct,
+ args, -1, groupSet.cardinality(), r, null, aggCall1.alias));
+ }
+
+ RelNode aggregate = aggregateFactory.createAggregate(r, false, groupSet,
+ ImmutableList.of(groupSet), aggregateCalls);
+ push(aggregate);
+ return this;
+ }
+
+ private List<RexNode> projects(RelDataType inputRowType) {
+ final List<RexNode> exprList = new ArrayList<>();
+ for (RelDataTypeField field : inputRowType.getFieldList()) {
+ final RexBuilder rexBuilder = cluster.getRexBuilder();
+ exprList.add(rexBuilder.makeInputRef(field.getType(), field.getIndex()));
+ }
+ return exprList;
+ }
+
+ private static int registerExpression(List<RexNode> exprList, RexNode node) {
+ int i = exprList.indexOf(node);
+ if (i < 0) {
+ i = exprList.size();
+ exprList.add(node);
+ }
+ return i;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Union} of the two most recent
+ * relational expressions on the stack.
+ *
+ * @param all Whether to create UNION ALL
+ */
+ public RelBuilder union(boolean all) {
+ final RelNode left = build();
+ final RelNode right = build();
+ final RelNode union = setOpFactory.createSetOp(SqlKind.UNION,
+ ImmutableList.of(left, right), all);
+ push(union);
+ return this;
+ }
+
+ /** Creates an {@link org.apache.calcite.rel.core.Intersect} of the two most
+ * recent relational expressions on the stack.
+ *
+ * @param all Whether to create INTERSECT ALL
+ */
+ public RelBuilder intersect(boolean all) {
+ final RelNode left = build();
+ final RelNode right = build();
+ final RelNode intersect = setOpFactory.createSetOp(SqlKind.INTERSECT,
+ ImmutableList.of(left, right), all);
+ push(intersect);
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Minus} of the two most recent
+ * relational expressions on the stack.
+ *
+ * @param all Whether to create EXCEPT ALL
+ */
+ public RelBuilder minus(boolean all) {
+ final RelNode left = build();
+ final RelNode right = build();
+ final RelNode except = setOpFactory.createSetOp(SqlKind.EXCEPT,
+ ImmutableList.of(left, right), all);
+ push(except);
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Join}. */
+ public RelBuilder join(JoinRelType joinType, RexNode condition) {
+ final RelNode left = build();
+ final RelNode right = build();
+ final RelNode join = joinFactory.createJoin(left, right, condition,
+ joinType, ImmutableSet.<String>of(), false);
+ push(join);
+ return this;
+ }
+
+ /** Creates a {@link org.apache.calcite.rel.core.Join} using USING syntax.
+ *
+ * <p>For each of the field names, both left and right inputs must have a
+ * field of that name. Constructs a join condition that the left and right
+ * fields are equal.
+ *
+ * @param joinType Join type
+ * @param fieldNames Field names
+ */
+ public RelBuilder join(JoinRelType joinType, String... fieldNames) {
+ final List<RexNode> conditions = new ArrayList<>();
+ for (String fieldName : fieldNames) {
+ conditions.add(
+ call(SqlStdOperatorTable.EQUALS,
+ field(2, 0, fieldName),
+ field(2, 1, fieldName)));
+ }
+ final RexNode condition =
+ RexUtil.composeConjunction(cluster.getRexBuilder(), conditions, false);
+ return join(joinType, condition);
+ }
+
+ /** Creates a {@link Values}.
+ *
+ * <p>The {@code values} array must have the same number of entries as
+ * {@code fieldNames}, or an integer multiple if you wish to create multiple
+ * rows.
+ *
+ * <p>If there are zero rows, or if all values of a any column are
+ * null, this method cannot deduce the type of columns. For these cases,
+ * call {@link #values(RelDataType, Iterable)}.
+ *
+ * @param fieldNames Field names
+ * @param values Values
+ */
+ public RelBuilder values(String[] fieldNames, Object... values) {
+ if (fieldNames == null
+ || fieldNames.length == 0
+ || values.length % fieldNames.length != 0
+ || values.length < fieldNames.length) {
+ throw new IllegalArgumentException(
+ "Value count must be a positive multiple of field count");
+ }
+ final int rowCount = values.length / fieldNames.length;
+ for (Ord<String> fieldName : Ord.zip(fieldNames)) {
+ if (allNull(values, fieldName.i, fieldNames.length)) {
+ throw new IllegalArgumentException("All values of field '" + fieldName.e
+ + "' are null; cannot deduce type");
+ }
+ }
+ final ImmutableList<ImmutableList<RexLiteral>> tupleList =
+ tupleList(fieldNames.length, values);
+ final RelDataTypeFactory.FieldInfoBuilder rowTypeBuilder =
+ cluster.getTypeFactory().builder();
+ for (final Ord<String> fieldName : Ord.zip(fieldNames)) {
+ final String name =
+ fieldName.e != null ? fieldName.e : "expr$" + fieldName.i;
+ final RelDataType type = cluster.getTypeFactory().leastRestrictive(
+ new AbstractList<RelDataType>() {
+ public RelDataType get(int index) {
+ return tupleList.get(index).get(fieldName.i).getType();
+ }
+
+ public int size() {
+ return rowCount;
+ }
+ });
+ rowTypeBuilder.add(name, type);
+ }
+ final RelDataType rowType = rowTypeBuilder.build();
+ return values(rowType, tupleList);
+ }
+
+ private ImmutableList<ImmutableList<RexLiteral>> tupleList(int columnCount,
+ Object[] values) {
+ final ImmutableList.Builder<ImmutableList<RexLiteral>> listBuilder =
+ ImmutableList.builder();
+ final List<RexLiteral> valueList = new ArrayList<>();
+ for (int i = 0; i < values.length; i++) {
+ Object value = values[i];
+ valueList.add((RexLiteral) literal(value));
+ if ((i + 1) % columnCount == 0) {
+ listBuilder.add(ImmutableList.copyOf(valueList));
+ valueList.clear();
+ }
+ }
+ return listBuilder.build();
+ }
+
+ /** Returns whether all values for a given column are null. */
+ private boolean allNull(Object[] values, int column, int columnCount) {
+ for (int i = column; i < values.length; i += columnCount) {
+ if (values[i] != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Creates a {@link Values} with a specified row type.
+ *
+ * <p>This method can handle cases that {@link #values(String[], Object...)}
+ * cannot, such as all values of a column being null, or there being zero
+ * rows.
+ *
+ * @param rowType Row type
+ * @param columnValues Values
+ */
+ public RelBuilder values(RelDataType rowType, Object... columnValues) {
+ final ImmutableList<ImmutableList<RexLiteral>> tupleList =
+ tupleList(rowType.getFieldCount(), columnValues);
+ RelNode values = valuesFactory.createValues(cluster, rowType,
+ ImmutableList.copyOf(tupleList));
+ push(values);
+ return this;
+ }
+
+ /** Creates a {@link Values} with a specified row type.
+ *
+ * <p>This method can handle cases that {@link #values(String[], Object...)}
+ * cannot, such as all values of a column being null, or there being zero
+ * rows.
+ *
+ * @param rowType Row type
+ * @param tupleList Tuple list
+ */
+ protected RelBuilder values(RelDataType rowType,
+ Iterable<ImmutableList<RexLiteral>> tupleList) {
+ RelNode values = valuesFactory.createValues(cluster, rowType,
+ ImmutableList.copyOf(tupleList));
+ push(values);
+ return this;
+ }
+
+ /** Creates a limit without a sort. */
+ public RelBuilder limit(int offset, int fetch) {
+ return sortLimit(offset, fetch, ImmutableList.<RexNode>of());
+ }
+
+ /** Creates a {@link Sort} by field ordinals.
+ *
+ * <p>Negative fields mean descending: -1 means field(0) descending,
+ * -2 means field(1) descending, etc.
+ */
+ public RelBuilder sort(int... fields) {
+ final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
+ for (int field : fields) {
+ builder.add(field < 0 ? desc(field(-field - 1)) : field(field));
+ }
+ return sortLimit(-1, -1, builder.build());
+ }
+
+ /** Creates a {@link Sort} by expressions. */
+ public RelBuilder sort(RexNode... nodes) {
+ return sortLimit(-1, -1, ImmutableList.copyOf(nodes));
+ }
+
+ /** Creates a {@link Sort} by expressions. */
+ public RelBuilder sort(Iterable<? extends RexNode> nodes) {
+ return sortLimit(-1, -1, nodes);
+ }
+
+ /** Creates a {@link Sort} by expressions, with limit and offset. */
+ public RelBuilder sortLimit(int offset, int fetch, RexNode... nodes) {
+ return sortLimit(offset, fetch, ImmutableList.copyOf(nodes));
+ }
+
+ /** Creates a {@link Sort} by a list of expressions, with limit and offset.
+ *
+ * @param offset Number of rows to skip; non-positive means don't skip any
+ * @param fetch Maximum number of rows to fetch; negative means no limit
+ * @param nodes Sort expressions
+ */
+ public RelBuilder sortLimit(int offset, int fetch,
+ Iterable<? extends RexNode> nodes) {
+ final List<RelFieldCollation> fieldCollations = new ArrayList<>();
+ final RelDataType inputRowType = peek().getRowType();
+ final List<RexNode> extraNodes = projects(inputRowType);
+ final List<RexNode> originalExtraNodes = ImmutableList.copyOf(extraNodes);
+ for (RexNode node : nodes) {
+ fieldCollations.add(
+ collation(node, RelFieldCollation.Direction.ASCENDING,
+ RelFieldCollation.NullDirection.UNSPECIFIED, extraNodes));
+ }
+ final RexNode offsetNode = offset <= 0 ? null : literal(offset);
+ final RexNode fetchNode = fetch < 0 ? null : literal(fetch);
+ if (extraNodes.size() > inputRowType.getFieldCount()) {
+ project(extraNodes);
+ }
+ final RelNode sort =
+ sortFactory.createSort(build(), RelCollations.of(fieldCollations),
+ offsetNode, fetchNode);
+ push(sort);
+ if (extraNodes.size() > inputRowType.getFieldCount()) {
+ project(originalExtraNodes);
+ }
+ return this;
+ }
+
+ private static RelFieldCollation collation(RexNode node,
+ RelFieldCollation.Direction direction,
+ RelFieldCollation.NullDirection nullDirection, List<RexNode> extraNodes) {
+ switch (node.getKind()) {
+ case INPUT_REF:
+ return new RelFieldCollation(((RexInputRef) node).getIndex(),
+ direction, nullDirection);
+ case DESCENDING:
+ return collation(((RexCall) node).getOperands().get(0),
+ RelFieldCollation.Direction.DESCENDING,
+ nullDirection, extraNodes);
+ case NULLS_FIRST:
+ return collation(((RexCall) node).getOperands().get(0), direction,
+ RelFieldCollation.NullDirection.FIRST, extraNodes);
+ case NULLS_LAST:
+ return collation(((RexCall) node).getOperands().get(0), direction,
+ RelFieldCollation.NullDirection.LAST, extraNodes);
+ default:
+ final int fieldIndex = extraNodes.size();
+ extraNodes.add(node);
+ return new RelFieldCollation(fieldIndex, direction, nullDirection);
+ }
+ }
+
+ /** Information necessary to create a call to an aggregate function.
+ *
+ * @see RelBuilder#aggregateCall */
+ public interface AggCall {
+ }
+
+ /** Information necessary to create the GROUP BY clause of an Aggregate.
+ *
+ * @see RelBuilder#groupKey */
+ public interface GroupKey {
+ }
+
+ /** Implementation of {@link RelBuilder.GroupKey}. */
+ private static class GroupKeyImpl implements GroupKey {
+ private final ImmutableList<RexNode> nodes;
+
+ GroupKeyImpl(ImmutableList<RexNode> nodes) {
+ this.nodes = nodes;
+ }
+ }
+
+ /** Implementation of {@link RelBuilder.AggCall}. */
+ private static class AggCallImpl implements AggCall {
+ private final SqlAggFunction aggFunction;
+ private final boolean distinct;
+ private final String alias;
+ private final ImmutableList<RexNode> operands;
+
+ public AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
+ String alias, ImmutableList<RexNode> operands) {
+ this.aggFunction = aggFunction;
+ this.distinct = distinct;
+ this.alias = alias;
+ this.operands = operands;
+ }
+ }
+
+ /** A partially-created RelBuilder.
+ *
+ * <p>Add a cluster, and optionally a schema,
+ * when you want to create a builder.
+ *
+ * <p>A {@code ProtoRelBuilder} can be shared among queries, and thus can
+ * be inside a {@link RelOptRule}. It is a nice way to encapsulate the policy
+ * that this particular rule instance should create {@code DrillFilter}
+ * and {@code DrillProject} versus {@code HiveFilter} and {@code HiveProject}.
+ *
+ * @see RelFactories#DEFAULT_PROTO
+ */
+ public interface ProtoRelBuilder {
+ RelBuilder create(RelOptCluster cluster, RelOptSchema schema);
+ }
+}
+
+// End RelBuilder.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/main/java/org/apache/calcite/util/Stacks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Stacks.java b/core/src/main/java/org/apache/calcite/util/Stacks.java
index 70ef045..2a41368 100644
--- a/core/src/main/java/org/apache/calcite/util/Stacks.java
+++ b/core/src/main/java/org/apache/calcite/util/Stacks.java
@@ -34,6 +34,14 @@ public class Stacks {
}
/**
+ * Returns the {@code n}th most recently added element in the stack.
+ * Throws if the stack is empty.
+ */
+ public static <T> T peek(int n, List<T> stack) {
+ return stack.get(stack.size() - n - 1);
+ }
+
+ /**
* Adds an element to the stack.
*/
public static <T> void push(List<T> stack, T element) {
@@ -48,6 +56,15 @@ public class Stacks {
assert stack.get(stack.size() - 1) == element;
stack.remove(stack.size() - 1);
}
+
+ /**
+ * Removes an element from the stack and returns it.
+ *
+ * <p>Throws if the stack is empty.
+ */
+ public static <T> T pop(List<T> stack) {
+ return stack.remove(stack.size() - 1);
+ }
}
// End Stacks.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/test/java/org/apache/calcite/examples/RelBuilderExample.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/examples/RelBuilderExample.java b/core/src/test/java/org/apache/calcite/examples/RelBuilderExample.java
new file mode 100644
index 0000000..12801e8
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/examples/RelBuilderExample.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.examples;
+
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.test.RelBuilderTest;
+import org.apache.calcite.tools.FrameworkConfig;
+import org.apache.calcite.tools.RelBuilder;
+
+/**
+ * Example that uses {@link org.apache.calcite.tools.RelBuilder}
+ * to create various relational expressions.
+ */
+public class RelBuilderExample {
+ private final boolean verbose;
+
+ public RelBuilderExample(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public static void main(String[] args) {
+ new RelBuilderExample(true).runAllExamples();
+ }
+
+ public void runAllExamples() {
+ // Create a builder. The config contains a schema mapped
+ // to the SCOTT database, with tables EMP and DEPT.
+ final FrameworkConfig config = RelBuilderTest.config().build();
+ final RelBuilder builder = RelBuilder.create(config);
+ for (int i = 0; i < 4; i++) {
+ doExample(builder, i);
+ final RelNode node = builder.build();
+ if (verbose) {
+ System.out.println(RelOptUtil.toString(node));
+ }
+ }
+ }
+
+ private RelBuilder doExample(RelBuilder builder, int i) {
+ switch (i) {
+ case 0:
+ return example0(builder);
+ case 1:
+ return example1(builder);
+ case 2:
+ return example2(builder);
+ case 3:
+ return example3(builder);
+ case 4:
+ return example4(builder);
+ default:
+ throw new AssertionError("unknown example " + i);
+ }
+ }
+
+ /**
+ * Creates a relational expression for a table scan.
+ * It is equivalent to
+ *
+ * <pre>
+ * SELECT *
+ * FROM emp</pre>
+ */
+ private RelBuilder example0(RelBuilder builder) {
+ return builder
+ .values(new String[] {"a", "b"}, 1, true, null, false);
+ }
+
+ /**
+ * Creates a relational expression for a table scan.
+ * It is equivalent to
+ *
+ * <pre>
+ * SELECT *
+ * FROM emp</pre>
+ */
+ private RelBuilder example1(RelBuilder builder) {
+ return builder
+ .scan("EMP");
+ }
+
+ /**
+ * Creates a relational expression for a table scan and project.
+ * It is equivalent to
+ *
+ * <pre>
+ * SELECT deptno, ename
+ * FROM emp</pre>
+ */
+ private RelBuilder example2(RelBuilder builder) {
+ return builder
+ .scan("EMP")
+ .project(builder.field("DEPTNO"), builder.field("ENAME"));
+ }
+
+ /**
+ * Creates a relational expression for a table scan, aggregate, filter.
+ * It is equivalent to
+ *
+ * <pre>
+ * SELECT deptno, count(*) AS c, sum(sal) AS s
+ * FROM emp
+ * GROUP BY deptno
+ * HAVING count(*) > 10</pre>
+ */
+ private RelBuilder example3(RelBuilder builder) {
+ return builder
+ .scan("EMP")
+ .aggregate(builder.groupKey("DEPTNO"),
+ builder.count(false, "C"),
+ builder.sum(false, "S", builder.field("SAL")))
+ .filter(
+ builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field("C"),
+ builder.literal(10)));
+ }
+
+ /**
+ * Sometimes the stack becomes so deeply nested it gets confusing. To keep
+ * things straight, you can remove expressions from the stack. For example,
+ * here we are building a bushy join:
+ *
+ * <pre>
+ * join
+ * / \
+ * join join
+ * / \ / \
+ * CUSTOMERS ORDERS LINE_ITEMS PRODUCTS
+ * </pre>
+ *
+ * <p>We build it in three stages. Store the intermediate results in variables
+ * `left` and `right`, and use `push()` to put them back on the stack when it
+ * is time to create the final `Join`.
+ */
+ private RelBuilder example4(RelBuilder builder) {
+ final RelNode left = builder
+ .scan("CUSTOMERS")
+ .scan("ORDERS")
+ .join(JoinRelType.INNER, "ORDER_ID")
+ .build();
+
+ final RelNode right = builder
+ .scan("LINE_ITEMS")
+ .scan("PRODUCTS")
+ .join(JoinRelType.INNER, "PRODUCT_ID")
+ .build();
+
+ return builder
+ .push(left)
+ .push(right)
+ .join(JoinRelType.INNER, "ORDER_ID");
+ }
+}
+
+// End RelBuilderExample.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6609cb1a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
index cad237e..5a87733 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
@@ -113,6 +113,7 @@ import org.junit.runners.Suite;
// slow tests (above 1s)
PlannerTest.class,
+ RelBuilderTest.class,
MaterializationTest.class,
JdbcAdapterTest.class,
LinqFrontJdbcBackTest.class,