/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.planner.projection.builder; import com.google.common.collect.ImmutableList; import io.crate.analyze.OrderBy; import io.crate.analyze.QueryClause; import io.crate.analyze.symbol.*; import io.crate.metadata.ColumnIdent; import io.crate.metadata.FunctionInfo; import io.crate.metadata.Functions; import io.crate.metadata.RowGranularity; import io.crate.operation.aggregation.AggregationFunction; import io.crate.operation.projectors.TopN; import io.crate.planner.projection.*; import io.crate.types.DataType; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; public class ProjectionBuilder { private final Functions functions; public ProjectionBuilder(Functions functions) { this.functions = functions; } public AggregationProjection aggregationProjection(Collection<? extends Symbol> inputs, Collection<Function> aggregates, AggregateMode mode, RowGranularity granularity) { InputColumns.Context context = new InputColumns.Context(inputs); ArrayList<Aggregation> aggregations = getAggregations(aggregates, mode, context); return new AggregationProjection(aggregations, granularity, mode); } public GroupProjection groupProjection( Collection<? extends Symbol> inputs, Collection<? extends Symbol> keys, Collection<Function> values, AggregateMode mode, RowGranularity requiredGranularity) { InputColumns.Context context = new InputColumns.Context(inputs); ArrayList<Aggregation> aggregations = getAggregations(values, mode, context); return new GroupProjection(InputColumns.create(keys, context), aggregations, mode, requiredGranularity); } private ArrayList<Aggregation> getAggregations(Collection<Function> functions, AggregateMode mode, InputColumns.Context context) { ArrayList<Aggregation> aggregations = new ArrayList<>(functions.size()); for (Function function : functions) { assert function.info().type() == FunctionInfo.Type.AGGREGATE : "function type must be " + FunctionInfo.Type.AGGREGATE; List<Symbol> aggregationInputs; switch (mode) { case ITER_FINAL: case ITER_PARTIAL: // ITER means that there is no aggregation part upfront, therefore the input // symbols need to be in arguments aggregationInputs = InputColumns.create(function.arguments(), context); break; case PARTIAL_FINAL: aggregationInputs = ImmutableList.of(context.inputs.get(function)); break; default: throw new AssertionError("Invalid mode: " + mode.name()); } Aggregation aggregation = new Aggregation( function.info(), mode.returnType(((AggregationFunction) this.functions.getQualified(function.info().ident()))), aggregationInputs ); aggregations.add(aggregation); } return aggregations; } public static FilterProjection filterProjection(Collection<? extends Symbol> inputs, QueryClause queryClause) { Symbol query; if (queryClause.hasQuery()) { query = InputColumns.create(queryClause.query(), inputs); } else if (queryClause.noMatch()) { query = Literal.BOOLEAN_FALSE; } else { query = Literal.BOOLEAN_TRUE; } // FilterProjection can only pass-through rows as is; create inputColumns which preserve the type: return new FilterProjection(query, InputColumn.fromSymbols(inputs)); } /** * Create a {@link OrderedTopNProjection}, {@link TopNProjection} or {@link EvalProjection}. * * @param inputs Symbols which describe the inputs the projection will receive * @param outputs Symbols which describe the outputs. * If these symbols differ from the inputs the projection will evaluate the rows to produce * the desired outputs. (That is, evaluate functions or re-order the columns) */ public static Projection topNOrEval(Collection<? extends Symbol> inputs, @Nullable OrderBy orderBy, int offset, int limit, @Nullable Collection<? extends Symbol> outputs) { InputColumns.Context context = new InputColumns.Context(inputs); List<Symbol> inputsProcessed = InputColumns.create(inputs, context); List<Symbol> outputsProcessed; if (outputs == null) { outputsProcessed = inputsProcessed; } else { outputsProcessed = InputColumns.create(outputs, context); } if (orderBy == null) { if (limit == TopN.NO_LIMIT && offset == 0) { return new EvalProjection(outputsProcessed); } return new TopNProjection(limit, offset, outputsProcessed); } return new OrderedTopNProjection( limit, offset, outputsProcessed, InputColumns.create(orderBy.orderBySymbols(), context), orderBy.reverseFlags(), orderBy.nullsFirst()); } /** * Create a {@link TopNProjection} or {@link EvalProjection} if required, otherwise null is returned. * <p> * The output symbols will consist of InputColumns. * </p> * @param numOutputs number of outputs this projection should have. * If inputTypes is longer this projection will cut off superfluous columns */ @Nullable public static Projection topNOrEvalIfNeeded(Integer limit, int offset, int numOutputs, List<DataType> inputTypes) { if (limit == null) { limit = TopN.NO_LIMIT; } int numInputTypes = inputTypes.size(); List<DataType> strippedInputs = inputTypes; if (numOutputs < numInputTypes) { strippedInputs = inputTypes.subList(0, numOutputs); } if (limit == TopN.NO_LIMIT && offset == 0) { if (numOutputs >= numInputTypes) { return null; } return new EvalProjection(InputColumn.fromTypes(strippedInputs)); } return new TopNProjection(limit, offset, InputColumn.fromTypes(strippedInputs)); } public static WriterProjection writerProjection(Collection<? extends Symbol> inputs, Symbol uri, @Nullable WriterProjection.CompressionType compressionType, Map<ColumnIdent, Symbol> overwrites, @Nullable List<String> outputNames, WriterProjection.OutputFormat outputFormat) { return new WriterProjection( InputColumn.fromSymbols(inputs), uri, compressionType, overwrites, outputNames, outputFormat); } }