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 2014/08/30 00:04:27 UTC
git commit: [OPTIQ-393] If no fields are projected from a table,
field trimmer should project a dummy expression
Repository: incubator-optiq
Updated Branches:
refs/heads/master acee9632f -> e60fa76e2
[OPTIQ-393] If no fields are projected from a table, field trimmer should project a dummy expression
Be careful not to create a dummy on top of a dummy.
Close apache/incubator-optiq#8 which was the original attempt to solve the problem of zero-field relational expressions.
Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/e60fa76e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/e60fa76e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/e60fa76e
Branch: refs/heads/master
Commit: e60fa76e207f7a9d8c7bdaf0a5dea951f9b561a5
Parents: acee963
Author: Ashutosh Chauhan <ha...@apache.org>
Authored: Wed Aug 27 14:24:23 2014 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Aug 29 14:32:40 2014 -0700
----------------------------------------------------------------------
.../org/eigenbase/sql2rel/RelFieldTrimmer.java | 73 ++++++++++++++------
.../net/hydromatic/optiq/test/JdbcTest.java | 16 ++++-
.../net/hydromatic/optiq/test/LatticeTest.java | 3 +-
core/src/test/resources/sql/misc.oq | 37 ++++++++++
4 files changed, 104 insertions(+), 25 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e60fa76e/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
index 75c04b1..14ae9be 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
@@ -33,6 +33,8 @@ import net.hydromatic.linq4j.Ord;
import net.hydromatic.optiq.util.BitSets;
+import com.google.common.collect.ImmutableList;
+
/**
* Transformer that walks over a tree of relational expressions, replacing each
* {@link RelNode} with a 'slimmed down' relational expression that projects
@@ -277,26 +279,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
// Some parts of the system can't handle rows with zero fields, so
// pretend that one field is used.
if (fieldsUsed.cardinality() == 0) {
- final Mapping mapping =
- Mappings.create(
- MappingType.INVERSE_SURJECTION,
- fieldCount,
- 1);
- final RexLiteral expr =
- project.getCluster().getRexBuilder().makeExactLiteral(
- BigDecimal.ZERO);
- RelDataType newRowType =
- project.getCluster().getTypeFactory().createStructType(
- Collections.singletonList(expr.getType()),
- Collections.singletonList("DUMMY"));
- ProjectRel newProject = new ProjectRel(
- project.getCluster(),
- project.getCluster().traitSetOf(RelCollationImpl.EMPTY),
- newInput,
- Collections.<RexNode>singletonList(expr),
- newRowType,
- project.getFlags());
- return new TrimResult(newProject, mapping);
+ return dummyProject(fieldCount, newInput);
}
// Build new project expressions, and populate the mapping.
@@ -349,6 +332,34 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
return new TrimResult(newProject, mapping);
}
+ /** Creates a project with a dummy column, to protect the parts of the system
+ * that cannot handle a relational expression with no columns.
+ *
+ * @param fieldCount Number of fields in the original relational expression
+ * @param input Trimmed input
+ * @return Dummy project, or null if no dummy is required
+ */
+ private TrimResult dummyProject(int fieldCount, RelNode input) {
+ final RelOptCluster cluster = input.getCluster();
+ final Mapping mapping =
+ Mappings.create(MappingType.INVERSE_SURJECTION, fieldCount, 1);
+ if (input.getRowType().getFieldCount() == 1) {
+ // Input already has one field (and may in fact be a dummy project we
+ // created for the child). We can't do better.
+ return new TrimResult(input, mapping);
+ }
+ final RexLiteral expr =
+ cluster.getRexBuilder().makeExactLiteral(
+ BigDecimal.ZERO);
+ RelDataType newRowType =
+ cluster.getTypeFactory().builder().add("DUMMY", expr.getType()).build();
+ ProjectRel newProject = new ProjectRel(cluster,
+ cluster.traitSetOf(RelCollationImpl.EMPTY), input,
+ ImmutableList.<RexNode>of(expr), newRowType,
+ ProjectRelBase.Flags.BOXED);
+ return new TrimResult(newProject, mapping);
+ }
+
/**
* Variant of {@link #trimFields(RelNode, BitSet, Set)} for
* {@link FilterRel}.
@@ -930,13 +941,31 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
BitSet fieldsUsed,
Set<RelDataTypeField> extraFields) {
final int fieldCount = tableAccessRel.getRowType().getFieldCount();
- if (fieldsUsed.equals(BitSets.range(fieldCount))
- && extraFields.isEmpty()) {
+ if (fieldsUsed.equals(BitSets.range(fieldCount)) && extraFields.isEmpty()) {
+ // if there is nothing to project or if we are projecting everything
+ // then no need to introduce another RelNode
return trimFields(
(RelNode) tableAccessRel, fieldsUsed, extraFields);
}
final RelNode newTableAccessRel =
tableAccessRel.project(fieldsUsed, extraFields);
+
+ // Some parts of the system can't handle rows with zero fields, so
+ // pretend that one field is used.
+ if (fieldsUsed.cardinality() == 0) {
+ RelNode input = newTableAccessRel;
+ if (input instanceof ProjectRelBase) {
+ // The table has implemented the project in the obvious way - by
+ // creating project with 0 fields. Strip it away, and create our own
+ // project with one field.
+ ProjectRelBase project = (ProjectRelBase) input;
+ if (project.getRowType().getFieldCount() == 0) {
+ input = project.getChild();
+ }
+ }
+ return dummyProject(fieldCount, input);
+ }
+
final Mapping mapping = createMapping(fieldsUsed, fieldCount);
return new TrimResult(newTableAccessRel, mapping);
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e60fa76e/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index 53e5cdc..9c624d5 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -2962,6 +2962,20 @@ public class JdbcTest {
.returnsCount(0);
}
+ /** Query that reads no columns from either underlying table. */
+ @Test public void testCountStar() {
+ OptiqAssert.that()
+ .with(OptiqAssert.Config.REGULAR)
+ .query("select count(*) c from \"hr\".\"emps\", \"hr\".\"depts\"")
+ .convertContains("AggregateRel(group=[{}], C=[COUNT()])\n"
+ + " ProjectRel(DUMMY=[0])\n"
+ + " JoinRel(condition=[true], joinType=[inner])\n"
+ + " ProjectRel(DUMMY=[0])\n"
+ + " EnumerableTableAccessRel(table=[[hr, emps]])\n"
+ + " ProjectRel(DUMMY=[0])\n"
+ + " EnumerableTableAccessRel(table=[[hr, depts]])");
+ }
+
/** Same result (and plan) as {@link #testSelectDistinct}. */
@Test public void testCountUnionAll() {
OptiqAssert.that()
@@ -3009,7 +3023,7 @@ public class JdbcTest {
.explainContains(
"PLAN=EnumerableCalcRel(expr#0=[{inputs}], CS=[$t0], CS2=[$t0])\n"
+ " EnumerableAggregateRel(group=[{}], CS=[COUNT()])\n"
- + " EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[0], expr#6=[<($t1, $t5)], DUMMY=[$t5], $condition=[$t6])\n"
+ + " EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[0], expr#6=[<($t1, $t5)], deptno=[$t1], $condition=[$t6])\n"
+ " EnumerableTableAccessRel(table=[[hr, emps]])\n")
.returns("CS=0; CS2=0\n");
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e60fa76e/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java b/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
index 80b4cde..2c473c9 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
@@ -146,8 +146,7 @@ public class LatticeTest {
.convertContains(
"AggregateRel(group=[{}], EXPR$0=[COUNT()])\n"
+ " ProjectRel(DUMMY=[0])\n"
- + " ProjectRel\n"
- + " StarTableScan(table=[[adhoc, star]])\n");
+ + " StarTableScan(table=[[adhoc, star]])\n");
} catch (RuntimeException e) {
assertThat(Util.getStackTrace(e), containsString("CannotPlanException"));
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e60fa76e/core/src/test/resources/sql/misc.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.oq b/core/src/test/resources/sql/misc.oq
index fb635a5..f5b7e1c 100644
--- a/core/src/test/resources/sql/misc.oq
+++ b/core/src/test/resources/sql/misc.oq
@@ -324,6 +324,43 @@ where e."deptno" >= 10 and e."name" = 'Sebastian';
!ok
+# [OPTIQ-393] If no fields are projected from a table, field trimmer should
+# project a dummy expression
+select 1 from "hr"."emps";
++--------+
+| EXPR$0 |
++--------+
+| 1 |
+| 1 |
+| 1 |
+| 1 |
++--------+
+(4 rows)
+
+!ok
+EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[1], EXPR$0=[$t5])
+ EnumerableTableAccessRel(table=[[hr, emps]])
+!plan
+
+# [OPTIQ-393] for table scan under join
+select count(*) as c from "hr"."emps", "hr"."depts";
++----+
+| C |
++----+
+| 12 |
++----+
+(1 row)
+
+!ok
+EnumerableAggregateRel(group=[{}], C=[COUNT()])
+ EnumerableCalcRel(expr#0..1=[{inputs}], expr#2=[0], DUMMY=[$t2])
+ EnumerableJoinRel(condition=[true], joinType=[inner])
+ EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[0], DUMMY=[$t5])
+ EnumerableTableAccessRel(table=[[hr, emps]])
+ EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[0], DUMMY=[$t3])
+ EnumerableTableAccessRel(table=[[hr, depts]])
+!plan
+
# [OPTIQ-345] AssertionError in RexToLixTranslator comparing to date literal
!use catchall
select count(*) as c from "everyTypes" where "sqlDate" = DATE '1970-01-01';