/*
* 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.dataproperties;
import java.util.Arrays;
import org.apache.flink.api.common.operators.Ordering;
import org.apache.flink.api.common.operators.SemanticProperties;
import org.apache.flink.api.common.operators.util.FieldSet;
import org.apache.flink.optimizer.plan.Channel;
import org.apache.flink.optimizer.util.Utils;
import org.apache.flink.runtime.operators.util.LocalStrategy;
/**
* This class represents the local properties of the data that are requested by an operator.
* Local properties are the properties within one partition.
* Operators request the local properties they need for correct execution. Here are some example local
* properties requested by certain operators:
* <ul>
* <li>"groupBy/reduce" will request the data to be grouped on the key fields.</li>
* <li>A sort-merge join will request the data from each input to be sorted on the respective join key.</li>
* </ul>
*/
public class RequestedLocalProperties implements Cloneable {
private Ordering ordering; // order inside a partition, null if not ordered
private FieldSet groupedFields; // fields by which the stream is grouped. null if not grouped.
// --------------------------------------------------------------------------------------------
/**
* Default constructor for trivial local properties. No order, no grouping, no uniqueness.
*/
public RequestedLocalProperties() {}
/**
* Creates interesting properties for the given ordering.
*
* @param ordering The interesting ordering.
*/
public RequestedLocalProperties(Ordering ordering) {
this.ordering = ordering;
}
/**
* Creates interesting properties for the given grouping.
*
* @param groupedFields The set of fields whose grouping is interesting.
*/
public RequestedLocalProperties(FieldSet groupedFields) {
this.groupedFields = groupedFields;
}
/**
* This constructor is used only for internal copy creation.
*
* @param ordering The ordering represented by these local properties.
* @param groupedFields The grouped fields for these local properties.
*/
private RequestedLocalProperties(Ordering ordering, FieldSet groupedFields) {
this.ordering = ordering;
this.groupedFields = groupedFields;
}
// --------------------------------------------------------------------------------------------
/**
* Gets the key order.
*
* @return The key order, or <code>null</code> if nothing is ordered.
*/
public Ordering getOrdering() {
return ordering;
}
/**
* Sets the order for these interesting local properties.
*
* @param ordering The order to set.
*/
public void setOrdering(Ordering ordering) {
this.ordering = ordering;
}
/**
* Gets the grouped fields.
*
* @return The grouped fields, or <code>null</code> if nothing is grouped.
*/
public FieldSet getGroupedFields() {
return this.groupedFields;
}
/**
* Sets the fields that are grouped in these data properties.
*
* @param groupedFields The fields that are grouped in these data properties.
*/
public void setGroupedFields(FieldSet groupedFields) {
this.groupedFields = groupedFields;
}
/**
* Checks, if the properties in this object are trivial, i.e. only standard values.
*/
public boolean isTrivial() {
return ordering == null && this.groupedFields == null;
}
/**
* This method resets the local properties to a state where no properties are given.
*/
public void reset() {
this.ordering = null;
this.groupedFields = null;
}
// --------------------------------------------------------------------------------------------
/**
* Filters these properties by what can be preserved by the given SemanticProperties when propagated down
* to the given input.
*
* @param props The SemanticProperties which define which fields are preserved.
* @param input The index of the operator's input.
* @return The filtered RequestedLocalProperties
*/
public RequestedLocalProperties filterBySemanticProperties(SemanticProperties props, int input) {
// no semantic properties, all local properties are filtered
if (props == null) {
throw new NullPointerException("SemanticProperties may not be null.");
}
if (this.ordering != null) {
Ordering newOrdering = new Ordering();
for (int i = 0; i < this.ordering.getInvolvedIndexes().size(); i++) {
int targetField = this.ordering.getInvolvedIndexes().get(i);
int sourceField = props.getForwardingSourceField(input, targetField);
if (sourceField >= 0) {
newOrdering.appendOrdering(sourceField, this.ordering.getType(i), this.ordering.getOrder(i));
} else {
return null;
}
}
return new RequestedLocalProperties(newOrdering);
} else if (this.groupedFields != null) {
FieldSet newGrouping = new FieldSet();
// check, whether the local key grouping is preserved
for (Integer targetField : this.groupedFields) {
int sourceField = props.getForwardingSourceField(input, targetField);
if (sourceField >= 0) {
newGrouping = newGrouping.addField(sourceField);
} else {
return null;
}
}
return new RequestedLocalProperties(newGrouping);
} else {
return null;
}
}
/**
* Checks, if this set of properties, as interesting properties, is met by the given
* properties.
*
* @param other
* The properties for which to check whether they meet these properties.
* @return True, if the properties are met, false otherwise.
*/
public boolean isMetBy(LocalProperties other) {
if (this.ordering != null) {
// we demand an ordering
return other.getOrdering() != null && this.ordering.isMetBy(other.getOrdering());
} else if (this.groupedFields != null) {
// check if the other fields are unique
if (other.getGroupedFields() != null && other.getGroupedFields().isValidUnorderedPrefix(this.groupedFields)) {
return true;
} else {
return other.areFieldsUnique(this.groupedFields);
}
} else {
return true;
}
}
/**
* Parametrizes the local strategy fields of a channel such that the channel produces the desired local properties.
*
* @param channel The channel to parametrize.
*/
public void parameterizeChannel(Channel channel) {
LocalProperties current = channel.getLocalProperties();
if (isMetBy(current)) {
// we are met, all is good
channel.setLocalStrategy(LocalStrategy.NONE);
}
else if (this.ordering != null) {
channel.setLocalStrategy(LocalStrategy.SORT, this.ordering.getInvolvedIndexes(), this.ordering.getFieldSortDirections());
}
else if (this.groupedFields != null) {
boolean[] dirs = new boolean[this.groupedFields.size()];
Arrays.fill(dirs, true);
channel.setLocalStrategy(LocalStrategy.SORT, Utils.createOrderedFromSet(this.groupedFields), dirs);
}
else {
channel.setLocalStrategy(LocalStrategy.NONE);
}
}
// --------------------------------------------------------------------------------------------
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (this.ordering == null ? 0 : this.ordering.hashCode());
result = prime * result + (this.groupedFields == null ? 0 : this.groupedFields.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof RequestedLocalProperties) {
final RequestedLocalProperties other = (RequestedLocalProperties) obj;
return (this.ordering == other.ordering || (this.ordering != null && this.ordering.equals(other.ordering))) &&
(this.groupedFields == other.groupedFields || (this.groupedFields != null && this.groupedFields.equals(other.groupedFields)));
} else {
return false;
}
}
@Override
public String toString() {
return "Requested Local Properties [ordering=" + this.ordering + ", grouped=" + this.groupedFields + "]";
}
@Override
public RequestedLocalProperties clone() {
return new RequestedLocalProperties(this.ordering, this.groupedFields);
}
}