/*
// 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.eigenbase.rel;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.eigenbase.relopt.RelOptUtil;
import org.eigenbase.reltype.RelDataType;
import org.eigenbase.rex.RexBuilder;
import org.eigenbase.rex.RexNode;
import org.eigenbase.rex.RexUtil;
import org.eigenbase.sql.fun.SqlStdOperatorTable;
import org.eigenbase.util.ImmutableIntList;
import org.eigenbase.util.mapping.IntPair;
import net.hydromatic.optiq.util.BitSets;
import com.google.common.base.Preconditions;
/** An analyzed join condition.
*
* <p>It is useful for the many algorithms that care whether a join is an
* equi-join.
*
* <p>You can create one using {@link #of}, or call {@link JoinRelBase#analyzeCondition()};
* many kinds of join cache their join info, especially those that are
* equi-joins and sub-class {@link org.eigenbase.rel.rules.EquiJoinRel}.</p>
*
* @see JoinRelBase#analyzeCondition() */
public abstract class JoinInfo {
public final ImmutableIntList leftKeys;
public final ImmutableIntList rightKeys;
/** Creates a JoinInfo. */
protected JoinInfo(ImmutableIntList leftKeys, ImmutableIntList rightKeys) {
this.leftKeys = Preconditions.checkNotNull(leftKeys);
this.rightKeys = Preconditions.checkNotNull(rightKeys);
assert leftKeys.size() == rightKeys.size();
}
/** Creates a {@code JoinInfo} by analyzing a condition. */
public static JoinInfo of(RelNode left, RelNode right, RexNode condition) {
final List<Integer> leftKeys = new ArrayList<Integer>();
final List<Integer> rightKeys = new ArrayList<Integer>();
RexNode remaining =
RelOptUtil.splitJoinCondition(left, right, condition, leftKeys,
rightKeys);
if (remaining.isAlwaysTrue()) {
return new EquiJoinInfo(ImmutableIntList.copyOf(leftKeys),
ImmutableIntList.copyOf(rightKeys));
} else {
return new NonEquiJoinInfo(ImmutableIntList.copyOf(leftKeys),
ImmutableIntList.copyOf(rightKeys), remaining);
}
}
/** Creates an equi-join. */
public static JoinInfo of(ImmutableIntList leftKeys,
ImmutableIntList rightKeys) {
return new EquiJoinInfo(leftKeys, rightKeys);
}
/** Returns whether this is an equi-join. */
public abstract boolean isEqui();
/** Returns a list of (left, right) key ordinals. */
public List<IntPair> pairs() {
return IntPair.zip(leftKeys, rightKeys);
}
public BitSet leftSet() {
return BitSets.of(leftKeys);
}
public BitSet rightSet() {
return BitSets.of(rightKeys);
}
public abstract RexNode getRemaining(RexBuilder rexBuilder);
public RexNode getEquiCondition(final RelNode left, final RelNode right,
final RexBuilder rexBuilder) {
final List<RelDataType> leftTypes =
RelOptUtil.getFieldTypeList(left.getRowType());
final List<RelDataType> rightTypes =
RelOptUtil.getFieldTypeList(right.getRowType());
return RexUtil.composeConjunction(rexBuilder,
new AbstractList<RexNode>() {
@Override public RexNode get(int index) {
final int leftKey = leftKeys.get(index);
final int rightKey = rightKeys.get(index);
return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
rexBuilder.makeInputRef(leftTypes.get(leftKey), leftKey),
rexBuilder.makeInputRef(rightTypes.get(rightKey),
leftTypes.size() + rightKey));
}
@Override public int size() {
return leftKeys.size();
}
},
false);
}
/** JoinInfo that represents an equi-join. */
private static class EquiJoinInfo extends JoinInfo {
protected EquiJoinInfo(ImmutableIntList leftKeys,
ImmutableIntList rightKeys) {
super(leftKeys, rightKeys);
}
@Override public boolean isEqui() {
return true;
}
@Override public RexNode getRemaining(RexBuilder rexBuilder) {
return rexBuilder.makeLiteral(true);
}
}
/** JoinInfo that represents a non equi-join. */
private static class NonEquiJoinInfo extends JoinInfo {
public final RexNode remaining;
protected NonEquiJoinInfo(ImmutableIntList leftKeys,
ImmutableIntList rightKeys, RexNode remaining) {
super(leftKeys, rightKeys);
this.remaining = Preconditions.checkNotNull(remaining);
assert !remaining.isAlwaysTrue();
}
@Override public boolean isEqui() {
return false;
}
@Override public RexNode getRemaining(RexBuilder rexBuilder) {
return remaining;
}
}
}
// End JoinInfo.java