/* * 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.executor.transport; import com.google.common.util.concurrent.FutureCallback; import io.crate.analyze.symbol.Literal; import io.crate.analyze.symbol.SelectSymbol; import io.crate.analyze.symbol.Symbol; import io.crate.analyze.where.DocKeys; import io.crate.collections.Lists2; import io.crate.metadata.ReplaceMode; import io.crate.metadata.ReplacingSymbolVisitor; import io.crate.planner.Merge; import io.crate.planner.MultiPhasePlan; import io.crate.planner.Plan; import io.crate.planner.PlanVisitor; import io.crate.planner.node.dql.Collect; import io.crate.planner.node.dql.ESGet; import io.crate.planner.node.dql.QueryThenFetch; import io.crate.planner.node.dql.join.NestedLoop; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.function.Function; /** * Replaces all symbols in the querySpec that match the given selectSymbol once the futureCallback * triggers with a Literal containing the value from the callback. */ class SubSelectSymbolReplacer implements FutureCallback<Object> { private final Plan plan; private final SelectSymbol selectSymbolToReplace; private static final PlanSymbolVisitor PLAN_SYMBOL_VISITOR = new PlanSymbolVisitor(); SubSelectSymbolReplacer(Plan plan, SelectSymbol selectSymbolToReplace) { this.plan = plan; this.selectSymbolToReplace = selectSymbolToReplace; } @Override public void onSuccess(@Nullable Object result) { // sub-selects on the same level may run concurrently, but symbol mutation is not thread-safe synchronized (plan) { PLAN_SYMBOL_VISITOR.process(plan, new SymbolReplacer(selectSymbolToReplace, result)); } } @Override public void onFailure(@Nonnull Throwable t) { } private static class PlanSymbolVisitor extends PlanVisitor<SymbolReplacer, Void> { @Override protected Void visitPlan(Plan plan, SymbolReplacer context) { throw new UnsupportedOperationException("Subselect not supported in " + plan); } @Override public Void visitMultiPhasePlan(MultiPhasePlan multiPhasePlan, SymbolReplacer replacer) { process(multiPhasePlan.rootPlan(), replacer); return null; } @Override public Void visitQueryThenFetch(QueryThenFetch plan, SymbolReplacer replacer) { process(plan.subPlan(), replacer); // plan.fetchPhase() -> has only references - no selectSymbols return null; } @Override public Void visitCollect(Collect plan, SymbolReplacer replacer) { plan.collectPhase().replaceSymbols(replacer); return null; } @Override public Void visitMerge(Merge merge, SymbolReplacer replacer) { merge.mergePhase().replaceSymbols(replacer); process(merge.subPlan(), replacer); return null; } @Override public Void visitGetPlan(ESGet plan, SymbolReplacer replacer) { Lists2.replaceItems(plan.outputs(), replacer); Lists2.replaceItems(plan.sortSymbols(), replacer); for (DocKeys.DocKey current : plan.docKeys()) { Lists2.replaceItems(current.values(), replacer); } return null; } @Override public Void visitNestedLoop(NestedLoop plan, SymbolReplacer replacer) { process(plan.left(), replacer); process(plan.right(), replacer); plan.nestedLoopPhase().replaceSymbols(replacer); return null; } } private static class SymbolReplacer extends ReplacingSymbolVisitor<Void> implements Function<Symbol, Symbol> { private final SelectSymbol selectSymbolToReplace; private final Object value; private SymbolReplacer(SelectSymbol selectSymbolToReplace, Object value) { super(ReplaceMode.MUTATE); this.selectSymbolToReplace = selectSymbolToReplace; this.value = value; } @Override public Symbol visitSelectSymbol(SelectSymbol selectSymbol, Void context) { if (selectSymbol == selectSymbolToReplace) { return Literal.of(selectSymbolToReplace.valueType(), value); } return selectSymbol; } @Nullable @Override public Symbol apply(@Nullable Symbol input) { return process(input, null); } } }