Skip to content

Commit ada6cc4

Browse files
xndaihsyuan
authored andcommitted
[CALCITE-3972] Allow RelBuilder to create RelNode with convention (Xiening Dai)
1. Provide Convention.transformRelBuilder() to transform an existing RelBuilder into one with specific convention. 2. RelBuilder provides withRelFactories() method to allow caller swap the underlying RelFactories and create a new builder. We can avoid ~1/3 of total rule firings in a N way join case with this change. Close #1884
1 parent b166b9a commit ada6cc4

File tree

8 files changed

+203
-2
lines changed

8 files changed

+203
-2
lines changed

core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.calcite.adapter.enumerable;
1818

19+
import org.apache.calcite.plan.Contexts;
1920
import org.apache.calcite.plan.Convention;
2021
import org.apache.calcite.plan.ConventionTraitDef;
2122
import org.apache.calcite.plan.RelOptPlanner;
@@ -25,6 +26,7 @@
2526
import org.apache.calcite.rel.RelCollation;
2627
import org.apache.calcite.rel.RelCollationTraitDef;
2728
import org.apache.calcite.rel.RelNode;
29+
import org.apache.calcite.rel.core.RelFactories;
2830

2931
/**
3032
* Family of calling conventions that return results as an
@@ -84,4 +86,13 @@ public boolean useAbstractConvertersForConversion(RelTraitSet fromTraits,
8486
RelTraitSet toTraits) {
8587
return true;
8688
}
89+
90+
public RelFactories.Struct getRelFactories() {
91+
return RelFactories.Struct.fromContext(
92+
Contexts.of(
93+
EnumerableRelFactories.ENUMERABLE_TABLE_SCAN_FACTORY,
94+
EnumerableRelFactories.ENUMERABLE_PROJECT_FACTORY,
95+
EnumerableRelFactories.ENUMERABLE_FILTER_FACTORY,
96+
EnumerableRelFactories.ENUMERABLE_SORT_FACTORY));
97+
}
8798
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.calcite.adapter.enumerable;
18+
19+
import org.apache.calcite.plan.RelOptTable;
20+
import org.apache.calcite.rel.RelCollation;
21+
import org.apache.calcite.rel.RelNode;
22+
import org.apache.calcite.rel.core.CorrelationId;
23+
import org.apache.calcite.rel.hint.RelHint;
24+
import org.apache.calcite.rel.type.RelDataType;
25+
import org.apache.calcite.rex.RexNode;
26+
import org.apache.calcite.rex.RexUtil;
27+
import org.apache.calcite.sql.validate.SqlValidatorUtil;
28+
29+
import java.util.List;
30+
import java.util.Set;
31+
32+
/**
33+
* Contains factory interface and default implementation for creating various
34+
* rel nodes.
35+
*/
36+
public class EnumerableRelFactories {
37+
38+
public static final org.apache.calcite.rel.core.RelFactories.TableScanFactory
39+
ENUMERABLE_TABLE_SCAN_FACTORY = new TableScanFactoryImpl();
40+
41+
public static final org.apache.calcite.rel.core.RelFactories.ProjectFactory
42+
ENUMERABLE_PROJECT_FACTORY = new ProjectFactoryImpl();
43+
44+
public static final org.apache.calcite.rel.core.RelFactories.FilterFactory
45+
ENUMERABLE_FILTER_FACTORY = new FilterFactoryImpl();
46+
47+
public static final org.apache.calcite.rel.core.RelFactories.SortFactory
48+
ENUMERABLE_SORT_FACTORY = new SortFactoryImpl();
49+
50+
/**
51+
* Implementation of {@link org.apache.calcite.rel.core.RelFactories.TableScanFactory} that
52+
* returns a vanilla {@link EnumerableTableScan}.
53+
*/
54+
private static class TableScanFactoryImpl
55+
implements org.apache.calcite.rel.core.RelFactories.TableScanFactory {
56+
public RelNode createScan(RelOptTable.ToRelContext toRelContext, RelOptTable table) {
57+
return EnumerableTableScan.create(toRelContext.getCluster(), table);
58+
}
59+
}
60+
61+
/**
62+
* Implementation of {@link org.apache.calcite.rel.core.RelFactories.ProjectFactory} that
63+
* returns a vanilla {@link EnumerableProject}.
64+
*/
65+
private static class ProjectFactoryImpl
66+
implements org.apache.calcite.rel.core.RelFactories.ProjectFactory {
67+
public RelNode createProject(RelNode input, List<RelHint> hints,
68+
List<? extends RexNode> childExprs, List<String> fieldNames) {
69+
final RelDataType rowType =
70+
RexUtil.createStructType(input.getCluster().getTypeFactory(), childExprs,
71+
fieldNames, SqlValidatorUtil.F_SUGGESTER);
72+
return EnumerableProject.create(input, childExprs, rowType);
73+
}
74+
}
75+
76+
/**
77+
* Implementation of {@link org.apache.calcite.rel.core.RelFactories.FilterFactory} that
78+
* returns a vanilla {@link EnumerableFilter}.
79+
*/
80+
private static class FilterFactoryImpl
81+
implements org.apache.calcite.rel.core.RelFactories.FilterFactory {
82+
public RelNode createFilter(RelNode input, RexNode condition,
83+
Set<CorrelationId> variablesSet) {
84+
return EnumerableFilter.create(input, condition);
85+
}
86+
}
87+
88+
/**
89+
* Implementation of {@link org.apache.calcite.rel.core.RelFactories.SortFactory} that
90+
* returns a vanilla {@link EnumerableSort}.
91+
*/
92+
private static class SortFactoryImpl
93+
implements org.apache.calcite.rel.core.RelFactories.SortFactory {
94+
public RelNode createSort(RelNode input, RelCollation collation,
95+
RexNode offset, RexNode fetch) {
96+
return EnumerableSort.create(input, collation, offset, fetch);
97+
}
98+
}
99+
100+
private EnumerableRelFactories() {
101+
}
102+
}

core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java

+10
Original file line numberDiff line numberDiff line change
@@ -518,13 +518,23 @@ public String dump() {
518518
sb.append(String
519519
.format(Locale.ROOT, "%n%-60s%20s%20s%n", "Rules", "Attempts", "Time (us)"));
520520
NumberFormat usFormat = NumberFormat.getNumberInstance(Locale.US);
521+
long totalAttempts = 0;
522+
long totalTime = 0;
521523
for (Map.Entry<String, Pair<Long, Long>> entry : list) {
522524
sb.append(
523525
String.format(Locale.ROOT, "%-60s%20s%20s%n",
524526
entry.getKey(),
525527
usFormat.format(entry.getValue().left),
526528
usFormat.format(entry.getValue().right)));
529+
totalAttempts += entry.getValue().left;
530+
totalTime += entry.getValue().right;
527531
}
532+
sb.append(
533+
String.format(Locale.ROOT, "%-60s%20s%20s%n",
534+
"* Total",
535+
usFormat.format(totalAttempts),
536+
usFormat.format(totalTime)));
537+
528538
return sb.toString();
529539
}
530540
}

core/src/main/java/org/apache/calcite/plan/Convention.java

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.calcite.plan;
1818

1919
import org.apache.calcite.rel.RelNode;
20+
import org.apache.calcite.rel.core.RelFactories;
2021

2122
/**
2223
* Calling convention trait.
@@ -81,6 +82,13 @@ default boolean useAbstractConvertersForConversion(RelTraitSet fromTraits,
8182
return false;
8283
}
8384

85+
/**
86+
* Return RelFactories struct for the convention which can be used to build RelNode
87+
*/
88+
default RelFactories.Struct getRelFactories() {
89+
return RelFactories.DEFAULT_STRUCT;
90+
}
91+
8492
/**
8593
* Default implementation.
8694
*/

core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public JoinPushThroughJoinRule(String description, boolean right,
8787
super(
8888
operand(clazz,
8989
operand(clazz, any()),
90-
operand(RelNode.class, any())),
90+
operandJ(RelNode.class, null, n -> !n.isEnforcer(), any())),
9191
relBuilderFactory, description);
9292
this.right = right;
9393
}

core/src/main/java/org/apache/calcite/tools/RelBuilder.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public class RelBuilder {
152152
private final RexSimplify simplifier;
153153
private final Config config;
154154
private final RelOptTable.ViewExpander viewExpander;
155-
private final RelFactories.Struct struct;
155+
private RelFactories.Struct struct;
156156

157157
protected RelBuilder(Context context, RelOptCluster cluster,
158158
RelOptSchema relOptSchema) {
@@ -230,6 +230,13 @@ public RelDataTypeFactory getTypeFactory() {
230230
return cluster.getTypeFactory();
231231
}
232232

233+
/** Returns new RelBuilder that adopts the convention provided.
234+
* RelNode will be created with such convention if corresponding factory is provided. */
235+
public RelBuilder adoptConvention(Convention convention) {
236+
this.struct = convention.getRelFactories();
237+
return this;
238+
}
239+
233240
/** Returns the builder for {@link RexNode} expressions. */
234241
public RexBuilder getRexBuilder() {
235242
return cluster.getRexBuilder();
@@ -2497,6 +2504,15 @@ public RelBuilder sortLimit(int offset, int fetch, RexNode... nodes) {
24972504
return sortLimit(offset, fetch, ImmutableList.copyOf(nodes));
24982505
}
24992506

2507+
/** Creates a {@link Sort} by specifying collations.
2508+
*/
2509+
public RelBuilder sort(RelCollation collation) {
2510+
final RelNode sort =
2511+
struct.sortFactory.createSort(peek(), collation, null, null);
2512+
replaceTop(sort);
2513+
return this;
2514+
}
2515+
25002516
/** Creates a {@link Sort} by a list of expressions, with limit and offset.
25012517
*
25022518
* @param offset Number of rows to skip; non-positive means don't skip any

core/src/test/java/org/apache/calcite/test/RelBuilderTest.java

+39
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
*/
1717
package org.apache.calcite.test;
1818

19+
import org.apache.calcite.adapter.enumerable.EnumerableConvention;
1920
import org.apache.calcite.jdbc.CalciteConnection;
2021
import org.apache.calcite.plan.Contexts;
22+
import org.apache.calcite.plan.Convention;
2123
import org.apache.calcite.plan.RelOptTable;
2224
import org.apache.calcite.plan.RelTraitDef;
2325
import org.apache.calcite.rel.RelCollations;
@@ -3357,6 +3359,43 @@ private void checkExpandTable(RelBuilder builder, Matcher<RelNode> matcher) {
33573359
assertThat(root, hasTree(expected));
33583360
}
33593361

3362+
@Test void testAdoptConventionEnumerable() {
3363+
final RelBuilder builder = RelBuilder.create(config().build());
3364+
RelNode root = builder
3365+
.adoptConvention(EnumerableConvention.INSTANCE)
3366+
.scan("DEPT")
3367+
.filter(
3368+
builder.equals(builder.field("DEPTNO"), builder.literal(20)))
3369+
.sort(builder.field(2), builder.desc(builder.field(0)))
3370+
.project(builder.field(0))
3371+
.build();
3372+
String expected = ""
3373+
+ "EnumerableProject(DEPTNO=[$0])\n"
3374+
+ " EnumerableSort(sort0=[$2], sort1=[$0], dir0=[ASC], dir1=[DESC])\n"
3375+
+ " EnumerableFilter(condition=[=($0, 20)])\n"
3376+
+ " EnumerableTableScan(table=[[scott, DEPT]])\n";
3377+
assertThat(root, hasTree(expected));
3378+
}
3379+
3380+
@Test void testSwitchConventions() {
3381+
final RelBuilder builder = RelBuilder.create(config().build());
3382+
RelNode root = builder
3383+
.scan("DEPT")
3384+
.adoptConvention(EnumerableConvention.INSTANCE)
3385+
.filter(
3386+
builder.equals(builder.field("DEPTNO"), builder.literal(20)))
3387+
.sort(builder.field(2), builder.desc(builder.field(0)))
3388+
.adoptConvention(Convention.NONE)
3389+
.project(builder.field(0))
3390+
.build();
3391+
String expected = ""
3392+
+ "LogicalProject(DEPTNO=[$0])\n"
3393+
+ " EnumerableSort(sort0=[$2], sort1=[$0], dir0=[ASC], dir1=[DESC])\n"
3394+
+ " EnumerableFilter(condition=[=($0, 20)])\n"
3395+
+ " LogicalTableScan(table=[[scott, DEPT]])\n";
3396+
assertThat(root, hasTree(expected));
3397+
}
3398+
33603399
@Test void testHints() {
33613400
final RelHint indexHint = RelHint.builder("INDEX")
33623401
.hintOption("_idx1")

site/_docs/algebra.md

+15
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ final RelNode result = builder
187187
.build();
188188
{% endhighlight %}
189189

190+
### Switch Convention
191+
192+
The default RelBuilder creates logical RelNode without coventions. But you could
193+
switch to use a different convention through `adoptConvention()`:
194+
195+
{% highlight java %}
196+
final RelNode result = builder
197+
.push(input)
198+
.adoptConvention(EnumerableConvention.INSTANCE)
199+
.sort(toCollation)
200+
.build();
201+
{% endhighlight %}
202+
203+
In this case, we create an EnumerableSort on top of the input RelNode.
204+
190205
### Field names and ordinals
191206

192207
You can reference a field by name or ordinal.

0 commit comments

Comments
 (0)