/**
* 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.hadoop.hive.ql.optimizer.calcite.rules;
import org.apache.calcite.adapter.druid.DruidQuery;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.RelFactories.FilterFactory;
import org.apache.calcite.rel.core.RelFactories.ProjectFactory;
import org.apache.calcite.rel.core.TableScan;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexOver;
import org.apache.calcite.rex.RexUtil;
import org.apache.hadoop.hive.ql.optimizer.calcite.HiveRelFactories;
import org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveProject;
//TODO: Remove this once Calcite FilterProjectTransposeRule can take rule operand
public class HiveFilterProjectTSTransposeRule extends RelOptRule {
public final static HiveFilterProjectTSTransposeRule INSTANCE =
new HiveFilterProjectTSTransposeRule(
Filter.class, HiveRelFactories.HIVE_FILTER_FACTORY, HiveProject.class,
HiveRelFactories.HIVE_PROJECT_FACTORY, TableScan.class);
public final static HiveFilterProjectTSTransposeRule INSTANCE_DRUID =
new HiveFilterProjectTSTransposeRule(
Filter.class, HiveRelFactories.HIVE_FILTER_FACTORY, HiveProject.class,
HiveRelFactories.HIVE_PROJECT_FACTORY, DruidQuery.class);
private final RelFactories.FilterFactory filterFactory;
private final RelFactories.ProjectFactory projectFactory;
private HiveFilterProjectTSTransposeRule(Class<? extends Filter> filterClass,
FilterFactory filterFactory, Class<? extends Project> projectClass,
ProjectFactory projectFactory, Class<? extends RelNode> tsClass) {
super(operand(filterClass, operand(projectClass, operand(tsClass, none()))));
this.filterFactory = filterFactory;
this.projectFactory = projectFactory;
}
@Override
public boolean matches(RelOptRuleCall call) {
final HiveProject projRel = call.rel(1);
// Assumption:
// 1. This will be run last after PP, Col Pruning in the PreJoinOrder
// optimizations.
// 2. If ProjectRel is not synthetic then PPD would have already pushed
// relevant pieces down and hence no point in running PPD again.
// 3. For synthetic Projects we don't care about non deterministic UDFs
if (!projRel.isSynthetic()) {
return false;
}
return super.matches(call);
}
// ~ Methods ----------------------------------------------------------------
// implement RelOptRule
public void onMatch(RelOptRuleCall call) {
final Filter filter = call.rel(0);
final Project project = call.rel(1);
if (RexOver.containsOver(project.getProjects(), null)) {
// In general a filter cannot be pushed below a windowing calculation.
// Applying the filter before the aggregation function changes
// the results of the windowing invocation.
//
// When the filter is on the PARTITION BY expression of the OVER clause
// it can be pushed down. For now we don't support this.
return;
}
if (RexUtil.containsCorrelation(filter.getCondition())) {
// If there is a correlation condition anywhere in the filter, don't
// push this filter past project since in some cases it can prevent a
// Correlate from being de-correlated.
return;
}
// convert the filter to one that references the child of the project
RexNode newCondition = RelOptUtil.pushPastProject(filter.getCondition(), project);
// Remove cast of BOOLEAN NOT NULL to BOOLEAN or vice versa. Filter accepts
// nullable and not-nullable conditions, but a CAST might get in the way of
// other rewrites.
final RelDataTypeFactory typeFactory = filter.getCluster().getTypeFactory();
if (RexUtil.isNullabilityCast(typeFactory, newCondition)) {
newCondition = ((RexCall) newCondition).getOperands().get(0);
}
RelNode newFilterRel = filterFactory == null ? filter.copy(filter.getTraitSet(),
project.getInput(), newCondition) : filterFactory.createFilter(project.getInput(),
newCondition);
RelNode newProjRel = projectFactory == null ? project.copy(project.getTraitSet(), newFilterRel,
project.getProjects(), project.getRowType()) : projectFactory.createProject(newFilterRel,
project.getProjects(), project.getRowType().getFieldNames());
call.transformTo(newProjRel);
}
}