/*
* Licensed 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 com.facebook.presto.operator.window;
import com.facebook.presto.metadata.Signature;
import com.facebook.presto.operator.aggregation.Accumulator;
import com.facebook.presto.operator.aggregation.AccumulatorFactory;
import com.facebook.presto.operator.aggregation.InternalAggregationFunction;
import com.facebook.presto.spi.block.BlockBuilder;
import com.facebook.presto.spi.function.WindowFunction;
import com.facebook.presto.spi.function.WindowIndex;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
public class AggregateWindowFunction
implements WindowFunction
{
private final InternalAggregationFunction function;
private final List<Integer> argumentChannels;
private final AccumulatorFactory accumulatorFactory;
private WindowIndex windowIndex;
private Accumulator accumulator;
private int currentStart;
private int currentEnd;
private AggregateWindowFunction(InternalAggregationFunction function, List<Integer> argumentChannels)
{
this.function = requireNonNull(function, "function is null");
this.argumentChannels = ImmutableList.copyOf(argumentChannels);
this.accumulatorFactory = function.bind(createArgs(function), Optional.empty());
}
@Override
public void reset(WindowIndex windowIndex)
{
this.windowIndex = windowIndex;
resetAccumulator();
}
@Override
public void processRow(BlockBuilder output, int peerGroupStart, int peerGroupEnd, int frameStart, int frameEnd)
{
if (frameStart < 0) {
// empty frame
resetAccumulator();
}
else if ((frameStart == currentStart) && (frameEnd >= currentEnd)) {
// same or expanding frame
accumulate(currentEnd + 1, frameEnd);
currentEnd = frameEnd;
}
else {
// different frame
resetAccumulator();
accumulate(frameStart, frameEnd);
currentStart = frameStart;
currentEnd = frameEnd;
}
accumulator.evaluateFinal(output);
}
private void accumulate(int start, int end)
{
accumulator.addInput(windowIndex, argumentChannels, start, end);
}
private void resetAccumulator()
{
if (currentStart >= 0) {
accumulator = accumulatorFactory.createAccumulator();
currentStart = -1;
currentEnd = -1;
}
}
public static WindowFunctionSupplier supplier(Signature signature, final InternalAggregationFunction function)
{
requireNonNull(function, "function is null");
return new AbstractWindowFunctionSupplier(signature, null)
{
@Override
protected WindowFunction newWindowFunction(List<Integer> inputs)
{
return new AggregateWindowFunction(function, inputs);
}
};
}
private static List<Integer> createArgs(InternalAggregationFunction function)
{
ImmutableList.Builder<Integer> list = ImmutableList.builder();
for (int i = 0; i < function.getParameterTypes().size(); i++) {
list.add(i);
}
return list.build();
}
}