/*
* 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.planner;
import io.crate.analyze.MultiSourceSelect;
import io.crate.analyze.QueriedTable;
import io.crate.analyze.QuerySpec;
import io.crate.analyze.SelectAnalyzedStatement;
import io.crate.analyze.relations.AnalyzedRelation;
import io.crate.analyze.relations.AnalyzedRelationVisitor;
import io.crate.analyze.relations.QueriedDocTable;
import io.crate.analyze.symbol.SelectSymbol;
import io.crate.exceptions.VersionInvalidException;
import io.crate.planner.consumer.ConsumingPlanner;
import io.crate.planner.consumer.ESGetStatementPlanner;
import java.util.Map;
class SelectStatementPlanner {
private final Visitor visitor;
SelectStatementPlanner(ConsumingPlanner consumingPlanner) {
visitor = new Visitor(consumingPlanner);
}
public Plan plan(SelectAnalyzedStatement statement, Planner.Context context) {
return visitor.process(statement.relation(), context);
}
private static class Visitor extends AnalyzedRelationVisitor<Planner.Context, Plan> {
private final ConsumingPlanner consumingPlanner;
public Visitor(ConsumingPlanner consumingPlanner) {
this.consumingPlanner = consumingPlanner;
}
private Plan invokeConsumingPlanner(AnalyzedRelation relation, Planner.Context context) {
Plan plan = consumingPlanner.plan(relation, context);
if (plan == null) {
throw new UnsupportedOperationException("Cannot create plan for: " + relation);
}
return Merge.ensureOnHandler(plan, context);
}
@Override
protected Plan visitAnalyzedRelation(AnalyzedRelation relation, Planner.Context context) {
return invokeConsumingPlanner(relation, context);
}
@Override
public Plan visitQueriedTable(QueriedTable table, Planner.Context context) {
context.applySoftLimit(table.querySpec());
return super.visitQueriedTable(table, context);
}
@Override
public Plan visitQueriedDocTable(QueriedDocTable table, Planner.Context context) {
QuerySpec querySpec = table.querySpec();
context.applySoftLimit(querySpec);
if (querySpec.hasAggregates() || querySpec.groupBy().isPresent()) {
return invokeConsumingPlanner(table, context);
}
if (querySpec.where().docKeys().isPresent() && !table.tableRelation().tableInfo().isAlias()) {
SubqueryPlanner subqueryPlanner = new SubqueryPlanner(context);
Map<Plan, SelectSymbol> subQueries = subqueryPlanner.planSubQueries(table.querySpec());
return MultiPhasePlan.createIfNeeded(ESGetStatementPlanner.convert(table, context), subQueries);
}
if (querySpec.where().hasVersions()) {
throw new VersionInvalidException();
}
Limits limits = context.getLimits(querySpec);
if (querySpec.where().noMatch() || (querySpec.limit().isPresent() && limits.finalLimit() == 0)) {
return new NoopPlan(context.jobId());
}
return invokeConsumingPlanner(table, context);
}
@Override
public Plan visitMultiSourceSelect(MultiSourceSelect mss, Planner.Context context) {
QuerySpec querySpec = mss.querySpec();
context.applySoftLimit(querySpec);
if (!querySpec.hasAggregates() && querySpec.where().noMatch()) {
return new NoopPlan(context.jobId());
}
return invokeConsumingPlanner(mss, context);
}
}
}