/*
* 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.flink.optimizer.operators;
import java.util.List;
import org.apache.flink.api.common.operators.Ordering;
import org.apache.flink.api.common.operators.util.FieldList;
import org.apache.flink.optimizer.CompilerException;
import org.apache.flink.optimizer.dag.TwoInputNode;
import org.apache.flink.optimizer.dataproperties.GlobalProperties;
import org.apache.flink.optimizer.dataproperties.LocalProperties;
import org.apache.flink.optimizer.dataproperties.RequestedGlobalProperties;
import org.apache.flink.optimizer.dataproperties.RequestedLocalProperties;
import org.apache.flink.optimizer.plan.Channel;
import org.apache.flink.optimizer.plan.DualInputPlanNode;
/**
*
*/
public abstract class OperatorDescriptorDual implements AbstractOperatorDescriptor {
protected final FieldList keys1;
protected final FieldList keys2;
private List<GlobalPropertiesPair> globalProps;
private List<LocalPropertiesPair> localProps;
protected OperatorDescriptorDual() {
this(null, null);
}
protected OperatorDescriptorDual(FieldList keys1, FieldList keys2) {
this.keys1 = keys1;
this.keys2 = keys2;
}
public List<GlobalPropertiesPair> getPossibleGlobalProperties() {
if (this.globalProps == null) {
this.globalProps = createPossibleGlobalProperties();
}
return this.globalProps;
}
public List<LocalPropertiesPair> getPossibleLocalProperties() {
if (this.localProps == null) {
this.localProps = createPossibleLocalProperties();
}
return this.localProps;
}
protected abstract List<GlobalPropertiesPair> createPossibleGlobalProperties();
protected abstract List<LocalPropertiesPair> createPossibleLocalProperties();
public abstract boolean areCompatible(RequestedGlobalProperties requested1, RequestedGlobalProperties requested2,
GlobalProperties produced1, GlobalProperties produced2);
public abstract boolean areCoFulfilled(RequestedLocalProperties requested1, RequestedLocalProperties requested2,
LocalProperties produced1, LocalProperties produced2);
public abstract DualInputPlanNode instantiate(Channel in1, Channel in2, TwoInputNode node);
public abstract GlobalProperties computeGlobalProperties(GlobalProperties in1, GlobalProperties in2);
public abstract LocalProperties computeLocalProperties(LocalProperties in1, LocalProperties in2);
protected boolean checkEquivalentFieldPositionsInKeyFields(FieldList fields1, FieldList fields2) {
// check number of produced partitioning fields
if(fields1.size() != fields2.size()) {
return false;
} else {
return checkEquivalentFieldPositionsInKeyFields(fields1, fields2, fields1.size());
}
}
protected boolean checkEquivalentFieldPositionsInKeyFields(FieldList fields1, FieldList fields2, int numRelevantFields) {
// check number of produced partitioning fields
if(fields1.size() < numRelevantFields || fields2.size() < numRelevantFields) {
return false;
}
else {
for(int i=0; i<numRelevantFields; i++) {
int pField1 = fields1.get(i);
int pField2 = fields2.get(i);
// check if position of both produced fields is the same in both requested fields
int j;
for(j=0; j<this.keys1.size(); j++) {
if(this.keys1.get(j) == pField1 && this.keys2.get(j) == pField2) {
break;
}
else if(this.keys1.get(j) != pField1 && this.keys2.get(j) != pField2) {
// do nothing
}
else {
return false;
}
}
if(j == this.keys1.size()) {
throw new CompilerException("Fields were not found in key fields.");
}
}
}
return true;
}
protected boolean checkSameOrdering(GlobalProperties produced1, GlobalProperties produced2, int numRelevantFields) {
Ordering prod1 = produced1.getPartitioningOrdering();
Ordering prod2 = produced2.getPartitioningOrdering();
if (prod1 == null || prod2 == null) {
throw new CompilerException("The given properties do not meet this operators requirements.");
}
// check that order of fields is equivalent
if (!checkEquivalentFieldPositionsInKeyFields(
prod1.getInvolvedIndexes(), prod2.getInvolvedIndexes(), numRelevantFields)) {
return false;
}
// check that both inputs have the same directions of order
for (int i = 0; i < numRelevantFields; i++) {
if (prod1.getOrder(i) != prod2.getOrder(i)) {
return false;
}
}
return true;
}
protected boolean checkSameOrdering(LocalProperties produced1, LocalProperties produced2, int numRelevantFields) {
Ordering prod1 = produced1.getOrdering();
Ordering prod2 = produced2.getOrdering();
if (prod1 == null || prod2 == null) {
throw new CompilerException("The given properties do not meet this operators requirements.");
}
// check that order of fields is equivalent
if (!checkEquivalentFieldPositionsInKeyFields(
prod1.getInvolvedIndexes(), prod2.getInvolvedIndexes(), numRelevantFields)) {
return false;
}
// check that both inputs have the same directions of order
for (int i = 0; i < numRelevantFields; i++) {
if (prod1.getOrder(i) != prod2.getOrder(i)) {
return false;
}
}
return true;
}
// --------------------------------------------------------------------------------------------
public static final class GlobalPropertiesPair {
private final RequestedGlobalProperties props1, props2;
public GlobalPropertiesPair(RequestedGlobalProperties props1, RequestedGlobalProperties props2) {
this.props1 = props1;
this.props2 = props2;
}
public RequestedGlobalProperties getProperties1() {
return this.props1;
}
public RequestedGlobalProperties getProperties2() {
return this.props2;
}
@Override
public int hashCode() {
return (this.props1 == null ? 0 : this.props1.hashCode()) ^ (this.props2 == null ? 0 : this.props2.hashCode());
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() == GlobalPropertiesPair.class) {
final GlobalPropertiesPair other = (GlobalPropertiesPair) obj;
return (this.props1 == null ? other.props1 == null : this.props1.equals(other.props1)) &&
(this.props2 == null ? other.props2 == null : this.props2.equals(other.props2));
}
return false;
}
@Override
public String toString() {
return "{" + this.props1 + " / " + this.props2 + "}";
}
}
public static final class LocalPropertiesPair {
private final RequestedLocalProperties props1, props2;
public LocalPropertiesPair(RequestedLocalProperties props1, RequestedLocalProperties props2) {
this.props1 = props1;
this.props2 = props2;
}
public RequestedLocalProperties getProperties1() {
return this.props1;
}
public RequestedLocalProperties getProperties2() {
return this.props2;
}
@Override
public int hashCode() {
return (this.props1 == null ? 0 : this.props1.hashCode()) ^ (this.props2 == null ? 0 : this.props2.hashCode());
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() == LocalPropertiesPair.class) {
final LocalPropertiesPair other = (LocalPropertiesPair) obj;
return (this.props1 == null ? other.props1 == null : this.props1.equals(other.props1)) &&
(this.props2 == null ? other.props2 == null : this.props2.equals(other.props2));
}
return false;
}
@Override
public String toString() {
return "{" + this.props1 + " / " + this.props2 + "}";
}
}
}