use of org.apache.calcite.util.Pair in project calcite by apache.
the class RelOptMaterializations method useMaterializedViews.
/**
* Returns a list of RelNode transformed from all possible combination of
* materialized view uses. Big queries will likely have more than one
* transformed RelNode, e.g., (t1 group by c1) join (t2 group by c2).
* @param rel the original RelNode
* @param materializations the materialized view list
* @return the list of transformed RelNode together with their corresponding
* materialized views used in the transformation.
*/
public static List<Pair<RelNode, List<RelOptMaterialization>>> useMaterializedViews(final RelNode rel, List<RelOptMaterialization> materializations) {
final List<RelOptMaterialization> applicableMaterializations = getApplicableMaterializations(rel, materializations);
final List<Pair<RelNode, List<RelOptMaterialization>>> applied = new ArrayList<>();
applied.add(Pair.<RelNode, List<RelOptMaterialization>>of(rel, ImmutableList.<RelOptMaterialization>of()));
for (RelOptMaterialization m : applicableMaterializations) {
int count = applied.size();
for (int i = 0; i < count; i++) {
Pair<RelNode, List<RelOptMaterialization>> current = applied.get(i);
List<RelNode> sub = substitute(current.left, m);
if (!sub.isEmpty()) {
ImmutableList.Builder<RelOptMaterialization> builder = ImmutableList.builder();
builder.addAll(current.right);
builder.add(m);
List<RelOptMaterialization> uses = builder.build();
for (RelNode rel2 : sub) {
applied.add(Pair.of(rel2, uses));
}
}
}
}
return applied.subList(1, applied.size());
}
use of org.apache.calcite.util.Pair in project calcite by apache.
the class SplunkPushDownRule method appendSearchString.
/**
* Appends a search string.
*
* @param toAppend Search string to append
* @param splunkRel Relational expression
* @param topProj Top projection
* @param bottomProj Bottom projection
*/
protected RelNode appendSearchString(String toAppend, SplunkTableScan splunkRel, LogicalProject topProj, LogicalProject bottomProj, RelDataType topRow, RelDataType bottomRow) {
final RelOptCluster cluster = splunkRel.getCluster();
StringBuilder updateSearchStr = new StringBuilder(splunkRel.search);
if (!toAppend.isEmpty()) {
updateSearchStr.append(" ").append(toAppend);
}
List<RelDataTypeField> bottomFields = bottomRow == null ? null : bottomRow.getFieldList();
List<RelDataTypeField> topFields = topRow == null ? null : topRow.getFieldList();
if (bottomFields == null) {
bottomFields = splunkRel.getRowType().getFieldList();
}
// handle bottom projection (ie choose a subset of the table fields)
if (bottomProj != null) {
List<RelDataTypeField> tmp = new ArrayList<RelDataTypeField>();
List<RelDataTypeField> dRow = bottomProj.getRowType().getFieldList();
for (RexNode rn : bottomProj.getProjects()) {
RelDataTypeField rdtf;
if (rn instanceof RexSlot) {
RexSlot rs = (RexSlot) rn;
rdtf = bottomFields.get(rs.getIndex());
} else {
rdtf = dRow.get(tmp.size());
}
tmp.add(rdtf);
}
bottomFields = tmp;
}
// field renaming: to -> from
List<Pair<String, String>> renames = new LinkedList<Pair<String, String>>();
// handle top projection (ie reordering and renaming)
List<RelDataTypeField> newFields = bottomFields;
if (topProj != null) {
LOGGER.debug("topProj: {}", String.valueOf(topProj.getPermutation()));
newFields = new ArrayList<RelDataTypeField>();
int i = 0;
for (RexNode rn : topProj.getProjects()) {
RexInputRef rif = (RexInputRef) rn;
RelDataTypeField field = bottomFields.get(rif.getIndex());
if (!bottomFields.get(rif.getIndex()).getName().equals(topFields.get(i).getName())) {
renames.add(Pair.of(bottomFields.get(rif.getIndex()).getName(), topFields.get(i).getName()));
field = topFields.get(i);
}
newFields.add(field);
}
}
if (!renames.isEmpty()) {
updateSearchStr.append("| rename ");
for (Pair<String, String> p : renames) {
updateSearchStr.append(p.left).append(" AS ").append(p.right).append(" ");
}
}
RelDataType resultType = cluster.getTypeFactory().createStructType(newFields);
String searchWithFilter = updateSearchStr.toString();
RelNode rel = new SplunkTableScan(cluster, splunkRel.getTable(), splunkRel.splunkTable, searchWithFilter, splunkRel.earliest, splunkRel.latest, resultType.getFieldNames());
LOGGER.debug("end of appendSearchString fieldNames: {}", rel.getRowType().getFieldNames());
return rel;
}
use of org.apache.calcite.util.Pair in project flink by apache.
the class RelDecorrelator method projectJoinOutputWithNullability.
/**
* Pulls project above the join from its RHS input. Enforces nullability for join output.
*
* @param join Join
* @param project Original project as the right-hand input of the join
* @param nullIndicatorPos Position of null indicator
* @return the subtree with the new Project at the root
*/
private RelNode projectJoinOutputWithNullability(Join join, Project project, int nullIndicatorPos) {
final RelDataTypeFactory typeFactory = join.getCluster().getTypeFactory();
final RelNode left = join.getLeft();
final JoinRelType joinType = join.getJoinType();
RexInputRef nullIndicator = new RexInputRef(nullIndicatorPos, typeFactory.createTypeWithNullability(join.getRowType().getFieldList().get(nullIndicatorPos).getType(), true));
// now create the new project
List<Pair<RexNode, String>> newProjExprs = new ArrayList<>();
// project everything from the LHS and then those from the original
// projRel
List<RelDataTypeField> leftInputFields = left.getRowType().getFieldList();
for (int i = 0; i < leftInputFields.size(); i++) {
newProjExprs.add(RexInputRef.of2(i, leftInputFields));
}
// Marked where the projected expr is coming from so that the types will
// become nullable for the original projections which are now coming out
// of the nullable side of the OJ.
boolean projectPulledAboveLeftCorrelator = joinType.generatesNullsOnRight();
for (Pair<RexNode, String> pair : project.getNamedProjects()) {
RexNode newProjExpr = removeCorrelationExpr(pair.left, projectPulledAboveLeftCorrelator, nullIndicator);
newProjExprs.add(Pair.of(newProjExpr, pair.right));
}
return relBuilder.push(join).projectNamed(Pair.left(newProjExprs), Pair.right(newProjExprs), true).build();
}
use of org.apache.calcite.util.Pair in project flink by apache.
the class RelDecorrelator method decorrelateRel.
public Frame decorrelateRel(Project rel) {
//
// Rewrite logic:
//
// 1. Pass along any correlated variables coming from the input.
//
final RelNode oldInput = rel.getInput();
Frame frame = getInvoke(oldInput, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
}
final List<RexNode> oldProjects = rel.getProjects();
final List<RelDataTypeField> relOutput = rel.getRowType().getFieldList();
// Project projects the original expressions,
// plus any correlated variables the input wants to pass along.
final List<Pair<RexNode, String>> projects = new ArrayList<>();
// and produce the correlated variables in the new output.
if (cm.mapRefRelToCorRef.containsKey(rel)) {
frame = decorrelateInputWithValueGenerator(rel, frame);
}
// Project projects the original expressions
final Map<Integer, Integer> mapOldToNewOutputs = new HashMap<>();
int newPos;
for (newPos = 0; newPos < oldProjects.size(); newPos++) {
projects.add(newPos, Pair.of(decorrelateExpr(currentRel, map, cm, oldProjects.get(newPos)), relOutput.get(newPos).getName()));
mapOldToNewOutputs.put(newPos, newPos);
}
// Project any correlated variables the input wants to pass along.
final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
for (Map.Entry<CorDef, Integer> entry : frame.corDefOutputs.entrySet()) {
projects.add(RexInputRef.of2(entry.getValue(), frame.r.getRowType().getFieldList()));
corDefOutputs.put(entry.getKey(), newPos);
newPos++;
}
RelNode newProject = relBuilder.push(frame.r).projectNamed(Pair.left(projects), Pair.right(projects), true).build();
newProject = RelOptUtil.copyRelHints(rel, newProject);
return register(rel, newProject, mapOldToNewOutputs, corDefOutputs);
}
use of org.apache.calcite.util.Pair in project flink by apache.
the class RelDecorrelator method decorrelateRel.
public Frame decorrelateRel(Aggregate rel) {
// Aggregate itself should not reference corVars.
assert !cm.mapRefRelToCorRef.containsKey(rel);
final RelNode oldInput = rel.getInput();
final Frame frame = getInvoke(oldInput, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
}
final RelNode newInput = frame.r;
// aggregate outputs mapping: group keys and aggregates
final Map<Integer, Integer> outputMap = new HashMap<>();
// map from newInput
final Map<Integer, Integer> mapNewInputToProjOutputs = new HashMap<>();
final int oldGroupKeyCount = rel.getGroupSet().cardinality();
// Project projects the original expressions,
// plus any correlated variables the input wants to pass along.
final List<Pair<RexNode, String>> projects = new ArrayList<>();
List<RelDataTypeField> newInputOutput = newInput.getRowType().getFieldList();
int newPos = 0;
// oldInput has the original group by keys in the front.
final NavigableMap<Integer, RexLiteral> omittedConstants = new TreeMap<>();
for (int i = 0; i < oldGroupKeyCount; i++) {
final RexLiteral constant = projectedLiteral(newInput, i);
if (constant != null) {
// Exclude constants. Aggregate({true}) occurs because Aggregate({})
// would generate 1 row even when applied to an empty table.
omittedConstants.put(i, constant);
continue;
}
// add mapping of group keys.
outputMap.put(i, newPos);
int newInputPos = frame.oldToNewOutputs.get(i);
projects.add(RexInputRef.of2(newInputPos, newInputOutput));
mapNewInputToProjOutputs.put(newInputPos, newPos);
newPos++;
}
final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
if (!frame.corDefOutputs.isEmpty()) {
// position oldGroupKeyCount.
for (Map.Entry<CorDef, Integer> entry : frame.corDefOutputs.entrySet()) {
projects.add(RexInputRef.of2(entry.getValue(), newInputOutput));
corDefOutputs.put(entry.getKey(), newPos);
mapNewInputToProjOutputs.put(entry.getValue(), newPos);
newPos++;
}
}
// add the remaining fields
final int newGroupKeyCount = newPos;
for (int i = 0; i < newInputOutput.size(); i++) {
if (!mapNewInputToProjOutputs.containsKey(i)) {
projects.add(RexInputRef.of2(i, newInputOutput));
mapNewInputToProjOutputs.put(i, newPos);
newPos++;
}
}
assert newPos == newInputOutput.size();
// This Project will be what the old input maps to,
// replacing any previous mapping from old input).
RelNode newProject = relBuilder.push(newInput).projectNamed(Pair.left(projects), Pair.right(projects), true).build();
newProject = RelOptUtil.copyRelHints(newInput, newProject);
// update mappings:
// oldInput ----> newInput
//
// newProject
// |
// oldInput ----> newInput
//
// is transformed to
//
// oldInput ----> newProject
// |
// newInput
Map<Integer, Integer> combinedMap = new HashMap<>();
for (Integer oldInputPos : frame.oldToNewOutputs.keySet()) {
combinedMap.put(oldInputPos, mapNewInputToProjOutputs.get(frame.oldToNewOutputs.get(oldInputPos)));
}
register(oldInput, newProject, combinedMap, corDefOutputs);
// now it's time to rewrite the Aggregate
final ImmutableBitSet newGroupSet = ImmutableBitSet.range(newGroupKeyCount);
List<AggregateCall> newAggCalls = new ArrayList<>();
List<AggregateCall> oldAggCalls = rel.getAggCallList();
final Iterable<ImmutableBitSet> newGroupSets;
if (rel.getGroupType() == Aggregate.Group.SIMPLE) {
newGroupSets = null;
} else {
final ImmutableBitSet addedGroupSet = ImmutableBitSet.range(oldGroupKeyCount, newGroupKeyCount);
newGroupSets = ImmutableBitSet.ORDERING.immutableSortedCopy(Util.transform(rel.getGroupSets(), bitSet -> bitSet.union(addedGroupSet)));
}
int oldInputOutputFieldCount = rel.getGroupSet().cardinality();
int newInputOutputFieldCount = newGroupSet.cardinality();
int i = -1;
for (AggregateCall oldAggCall : oldAggCalls) {
++i;
List<Integer> oldAggArgs = oldAggCall.getArgList();
List<Integer> aggArgs = new ArrayList<>();
// for the argument.
for (int oldPos : oldAggArgs) {
aggArgs.add(combinedMap.get(oldPos));
}
final int filterArg = oldAggCall.filterArg < 0 ? oldAggCall.filterArg : combinedMap.get(oldAggCall.filterArg);
newAggCalls.add(oldAggCall.adaptTo(newProject, aggArgs, filterArg, oldGroupKeyCount, newGroupKeyCount));
// The old to new output position mapping will be the same as that
// of newProject, plus any aggregates that the oldAgg produces.
outputMap.put(oldInputOutputFieldCount + i, newInputOutputFieldCount + i);
}
relBuilder.push(newProject).aggregate(newGroupSets == null ? relBuilder.groupKey(newGroupSet) : relBuilder.groupKey(newGroupSet, newGroupSets), newAggCalls);
if (!omittedConstants.isEmpty()) {
final List<RexNode> postProjects = new ArrayList<>(relBuilder.fields());
for (Map.Entry<Integer, RexLiteral> entry : omittedConstants.descendingMap().entrySet()) {
int index = entry.getKey() + frame.corDefOutputs.size();
postProjects.add(index, entry.getValue());
// Shift the outputs whose index equals with or bigger than the added index
// with 1 offset.
shiftMapping(outputMap, index, 1);
// Then add the constant key mapping.
outputMap.put(entry.getKey(), index);
}
relBuilder.project(postProjects);
}
RelNode newRel = RelOptUtil.copyRelHints(rel, relBuilder.build());
// located at the same position as the input newProject.
return register(rel, newRel, outputMap, corDefOutputs);
}
Aggregations