blob: a47b58ea096f3ae9167d3f1e1a3fd89598f87bd2 [file] [log] [blame]
/*
* 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.test.enumerable;
import org.apache.calcite.adapter.enumerable.EnumerableRepeatUnion;
import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.test.CalciteAssert;
import org.apache.calcite.test.schemata.hr.HierarchySchema;
import org.apache.calcite.tools.RelBuilder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* Unit tests for
* {@link EnumerableRepeatUnion}
* <a href="https://issues.apache.org/jira/browse/CALCITE-2812">[CALCITE-2812]
* Add algebraic operators to allow expressing recursive queries</a>.
*/
class EnumerableRepeatUnionHierarchyTest {
// Tests for the following hierarchy:
// Emp1
// / \
// Emp2 Emp4
// / \
// Emp3 Emp5
private static final String EMP1 = "empid=1; name=Emp1";
private static final String EMP2 = "empid=2; name=Emp2";
private static final String EMP3 = "empid=3; name=Emp3";
private static final String EMP4 = "empid=4; name=Emp4";
private static final String EMP5 = "empid=5; name=Emp5";
private static final int[] ID1 = new int[]{1};
private static final String ID1_STR = Arrays.toString(ID1);
private static final int[] ID2 = new int[]{2};
private static final String ID2_STR = Arrays.toString(ID2);
private static final int[] ID3 = new int[]{3};
private static final String ID3_STR = Arrays.toString(ID3);
private static final int[] ID4 = new int[]{4};
private static final String ID4_STR = Arrays.toString(ID4);
private static final int[] ID5 = new int[]{5};
private static final String ID5_STR = Arrays.toString(ID5);
private static final int[] ID3_5 = new int[]{3, 5};
private static final String ID3_5_STR = Arrays.toString(ID3_5);
private static final int[] ID1_3 = new int[]{1, 3};
private static final String ID1_3_STR = Arrays.toString(ID1_3);
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] {
{ true, ID1, ID1_STR, true, -1, new String[]{EMP1} },
{ true, ID2, ID2_STR, true, -2, new String[]{EMP2, EMP1} },
{ true, ID3, ID3_STR, true, -1, new String[]{EMP3, EMP2, EMP1} },
{ true, ID4, ID4_STR, true, -5, new String[]{EMP4, EMP1} },
{ true, ID5, ID5_STR, true, -1, new String[]{EMP5, EMP2, EMP1} },
{ true, ID3, ID3_STR, true, 0, new String[]{EMP3} },
{ true, ID3, ID3_STR, true, 1, new String[]{EMP3, EMP2} },
{ true, ID3, ID3_STR, true, 2, new String[]{EMP3, EMP2, EMP1} },
{ true, ID3, ID3_STR, true, 10, new String[]{EMP3, EMP2, EMP1} },
{ true, ID1, ID1_STR, false, -1, new String[]{EMP1, EMP2, EMP4, EMP3, EMP5} },
{ true, ID2, ID2_STR, false, -10, new String[]{EMP2, EMP3, EMP5} },
{ true, ID3, ID3_STR, false, -100, new String[]{EMP3} },
{ true, ID4, ID4_STR, false, -1, new String[]{EMP4} },
{ true, ID1, ID1_STR, false, 0, new String[]{EMP1} },
{ true, ID1, ID1_STR, false, 1, new String[]{EMP1, EMP2, EMP4} },
{ true, ID1, ID1_STR, false, 2, new String[]{EMP1, EMP2, EMP4, EMP3, EMP5} },
{ true, ID1, ID1_STR, false, 20, new String[]{EMP1, EMP2, EMP4, EMP3, EMP5} },
// tests to verify all=true vs all=false
{ true, ID3_5, ID3_5_STR, true, -1, new String[]{EMP3, EMP5, EMP2, EMP2, EMP1, EMP1} },
{ false, ID3_5, ID3_5_STR, true, -1, new String[]{EMP3, EMP5, EMP2, EMP1} },
{ true, ID3_5, ID3_5_STR, true, 0, new String[]{EMP3, EMP5} },
{ false, ID3_5, ID3_5_STR, true, 0, new String[]{EMP3, EMP5} },
{ true, ID3_5, ID3_5_STR, true, 1, new String[]{EMP3, EMP5, EMP2, EMP2} },
{ false, ID3_5, ID3_5_STR, true, 1, new String[]{EMP3, EMP5, EMP2} },
{ true, ID1_3, ID1_3_STR, false, -1, new String[]{EMP1, EMP3, EMP2, EMP4, EMP3, EMP5} },
{ false, ID1_3, ID1_3_STR, false, -1, new String[]{EMP1, EMP3, EMP2, EMP4, EMP5} },
});
}
@ParameterizedTest(name = "{index} : hierarchy(startIds:{2}, ascendant:{3}, "
+ "maxDepth:{4}, all:{0})")
@MethodSource("data")
public void testHierarchy(
boolean all,
int[] startIds,
String startIdsStr,
boolean ascendant,
int maxDepth,
String[] expected) {
final String fromField;
final String toField;
if (ascendant) {
fromField = "subordinateid";
toField = "managerid";
} else {
fromField = "managerid";
toField = "subordinateid";
}
final Schema schema = new ReflectiveSchema(new HierarchySchema());
CalciteAssert.that()
.withSchema("s", schema)
.query("?")
.withRel(buildHierarchy(all, startIds, fromField, toField, maxDepth))
.returnsOrdered(expected);
}
private Function<RelBuilder, RelNode> buildHierarchy(
boolean all,
int[] startIds,
String fromField,
String toField,
int maxDepth) {
// WITH RECURSIVE delta(empid, name) as (
// SELECT empid, name FROM emps WHERE empid IN (<startIds>)
// UNION [ALL]
// SELECT e.empid, e.name FROM delta d
// JOIN hierarchies h ON d.empid = h.<fromField>
// JOIN emps e ON h.<toField> = e.empid
// )
// SELECT empid, name FROM delta
return builder -> {
builder
.scan("s", "emps");
final List<RexNode> filters = new ArrayList<>();
for (int startId : startIds) {
filters.add(
builder.equals(
builder.field("empid"),
builder.literal(startId)));
}
builder
.filter(
builder.or(filters))
.project(
builder.field("emps", "empid"),
builder.field("emps", "name"))
.transientScan("#DELTA#")
.scan("s", "hierarchies")
.join(
JoinRelType.INNER,
builder.equals(
builder.field(2, "#DELTA#", "empid"),
builder.field(2, "hierarchies", fromField)))
.scan("s", "emps")
.join(
JoinRelType.INNER,
builder.equals(
builder.field(2, "hierarchies", toField),
builder.field(2, "emps", "empid")))
.project(
builder.field("emps", "empid"),
builder.field("emps", "name"))
.repeatUnion("#DELTA#", all, maxDepth);
return builder.build();
};
}
}