/* * Licensed to 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.operation; import com.carrotsearch.hppc.IntObjectHashMap; import com.carrotsearch.hppc.IntObjectMap; import io.crate.analyze.symbol.Aggregation; import io.crate.analyze.symbol.InputColumn; import io.crate.analyze.symbol.Symbol; import io.crate.analyze.symbol.SymbolVisitor; import io.crate.data.Input; import io.crate.data.Row; import io.crate.metadata.FunctionImplementation; import io.crate.metadata.Functions; import io.crate.metadata.Reference; import io.crate.operation.aggregation.AggregationFunction; import io.crate.operation.collect.CollectExpression; import io.crate.operation.collect.InputCollectExpression; import io.crate.operation.reference.ReferenceResolver; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Factory which can be used to create {@link Input}s from symbols. * * <p> * Some inputs will be linked to some kind of expression. * * E.g. InputColumns will have a corresponding {@code CollectExpression<Row, ?>}. * The state on these CollectExpression needs to be set in order for {@link Input#value()} to return the correct value. * <br /> * * Inputs from symbols like Functions or Literals are "standalone" and won't have a linked expression. * </p> * * * <p> * Note: * Symbols may either contain References OR InputColumn. Not both. * The appropriate method {@link #ctxForRefs(ReferenceResolver)} or {@link #ctxForInputColumns()} must be used. * * This is due to the fact that References will result in a different kind of Expression class than InputColumns do. * The expression class for references depends on the used ReferenceResolver. * </p> */ public class InputFactory { private final Functions functions; public InputFactory(Functions functions) { this.functions = functions; } public <T extends Input<?>> Context<T> ctxForRefs(ReferenceResolver<T> referenceResolver) { List<T> expressions = new ArrayList<>(); return new Context<>(expressions, new RefVisitor<>( functions, new GatheringRefResolver<>(expressions::add, referenceResolver))); } public Context<CollectExpression<Row, ?>> ctxForInputColumns() { List<CollectExpression<Row, ?>> expressions = new ArrayList<>(); return new Context<>(expressions, new InputColumnVisitor(functions, expressions)); } public Context<CollectExpression<Row, ?>> ctxForInputColumns(Iterable<? extends Symbol> symbols) { Context<CollectExpression<Row, ?>> context = ctxForInputColumns(); context.add(symbols); return context; } public Context<CollectExpression<Row, ?>> ctxForAggregations() { List<CollectExpression<Row, ?>> expressions = new ArrayList<>(); List<AggregationContext> aggregationContexts = new ArrayList<>(); return new Context<>( expressions, aggregationContexts, new AggregationVisitor(functions, expressions, aggregationContexts)); } public static class Context<T extends Input<?>> { private final List<Input<?>> topLevelInputs = new ArrayList<>(); private final List<T> expressions; private final List<AggregationContext> aggregationContexts; private final SymbolVisitor<?, Input<?>> visitor; private Context(List<T> expressions, List<AggregationContext> aggregationContexts, SymbolVisitor<?, Input<?>> visitor) { this.expressions = expressions; this.aggregationContexts = aggregationContexts; this.visitor = visitor; } private Context(List<T> expressions, SymbolVisitor<?, Input<?>> visitor) { this(expressions, Collections.emptyList(), visitor); } public List<Input<?>> topLevelInputs() { return topLevelInputs; } public List<T> expressions() { return expressions; } /** * Create inputs for all symbols, The inputs will the added to {@link #topLevelInputs()} */ public void add(Iterable<? extends Symbol> symbols) { for (Symbol symbol : symbols) { Input<?> input = add(symbol); if (input != null) { topLevelInputs.add(input); } } } /** * Create an input from a symbol. * If the input contains any linked expressions they'll be available in {@link #expressions()}. * <p> * The Input WON'T be part of {@link #topLevelInputs()} * </p> */ public Input<?> add(Symbol symbol) { return visitor.process(symbol, null); } public List<AggregationContext> aggregations() { return aggregationContexts; } } private static class InputColumnVisitor extends BaseImplementationSymbolVisitor<Void> { private final List<CollectExpression<Row, ?>> expressions; private final IntObjectMap<InputCollectExpression> inputCollectExpressions = new IntObjectHashMap<>(); InputColumnVisitor(Functions functions, List<CollectExpression<Row, ?>> expressions) { super(functions); this.expressions = expressions; } @Override public Input<?> visitInputColumn(InputColumn inputColumn, Void context) { int index = inputColumn.index(); InputCollectExpression inputCollectExpression = inputCollectExpressions.get(index); if (inputCollectExpression == null) { inputCollectExpression = new InputCollectExpression(index); inputCollectExpressions.put(index, inputCollectExpression); expressions.add(inputCollectExpression); } return inputCollectExpression; } } private static class AggregationVisitor extends InputColumnVisitor { private final List<AggregationContext> aggregationContexts; AggregationVisitor(Functions functions, List<CollectExpression<Row, ?>> expressions, List<AggregationContext> aggregationContexts) { super(functions, expressions); this.aggregationContexts = aggregationContexts; } @Override public Input<?> visitAggregation(Aggregation symbol, Void context) { FunctionImplementation impl = functions.getQualified(symbol.functionIdent()); AggregationContext aggregationContext = new AggregationContext((AggregationFunction) impl); for (Symbol aggInput : symbol.inputs()) { aggregationContext.addInput(process(aggInput, context)); } aggregationContexts.add(aggregationContext); // can't generate an input from an aggregation. // since they cannot/shouldn't be nested this should be okay. return null; } } private static class RefVisitor<T extends Input<?>> extends BaseImplementationSymbolVisitor<Void> { private final ReferenceResolver<T> referenceResolver; RefVisitor(Functions functions, ReferenceResolver<T> referenceResolver) { super(functions); this.referenceResolver = referenceResolver; } @Override public Input<?> visitReference(Reference ref, Void context) { T implementation = referenceResolver.getImplementation(ref); if (implementation == null) { throw new IllegalArgumentException("Column implementation not found for: " + ref); } return implementation; } } }