/* * 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.consumer; import io.crate.analyze.InsertFromSubQueryAnalyzedStatement; import io.crate.analyze.QuerySpec; import io.crate.analyze.relations.AnalyzedRelation; import io.crate.analyze.relations.AnalyzedRelationVisitor; import io.crate.analyze.relations.QueriedDocTable; import io.crate.analyze.relations.QueriedRelation; import io.crate.analyze.symbol.Symbol; import io.crate.exceptions.UnsupportedFeatureException; import io.crate.metadata.DocReferences; import io.crate.planner.Merge; import io.crate.planner.Plan; import io.crate.planner.Planner; import io.crate.planner.projection.ColumnIndexWriterProjection; import io.crate.planner.projection.MergeCountProjection; import org.elasticsearch.common.settings.Settings; import java.util.List; public final class InsertFromSubQueryPlanner { private static final ToSourceLookupConverter SOURCE_LOOKUP_CONVERTER = new ToSourceLookupConverter(); private InsertFromSubQueryPlanner() { } public static Plan plan(InsertFromSubQueryAnalyzedStatement statement, ConsumerContext context) { ColumnIndexWriterProjection indexWriterProjection = new ColumnIndexWriterProjection( statement.tableInfo().ident(), null, statement.tableInfo().primaryKey(), statement.columns(), statement.onDuplicateKeyAssignments(), statement.primaryKeySymbols(), statement.tableInfo().partitionedBy(), statement.partitionedBySymbols(), statement.tableInfo().clusteredBy(), statement.clusteredBySymbol(), Settings.EMPTY, statement.tableInfo().isPartitioned() ); Planner.Context plannerContext = context.plannerContext(); QueriedRelation subRelation = statement.subQueryRelation(); QuerySpec subQuerySpec = subRelation.querySpec(); // We'd have to enable paging & add a mergePhase to the sub-plan which applies the ordering/limit before // the indexWriterProjection can be added if (subQuerySpec.limit().isPresent() || subQuerySpec.offset().isPresent() || subQuerySpec.orderBy().isPresent()) { throw new UnsupportedFeatureException("Using limit, offset or order by is not " + "supported on insert using a sub-query"); } SOURCE_LOOKUP_CONVERTER.process(subRelation, null); context.setFetchMode(FetchMode.NEVER); Plan plannedSubQuery = plannerContext.planSubRelation(subRelation, context); if (plannedSubQuery == null) { return null; } plannedSubQuery.addProjection(indexWriterProjection, null, null, null); Plan plan = Merge.ensureOnHandler(plannedSubQuery, plannerContext); if (plan == plannedSubQuery) { return plan; } plan.addProjection(MergeCountProjection.INSTANCE, null, null, null); return plan; } private static class ToSourceLookupConverter extends AnalyzedRelationVisitor<Void, Void> { @Override public Void visitQueriedDocTable(QueriedDocTable table, Void context) { if (table.querySpec().hasAggregates() || table.querySpec().groupBy().isPresent()) { return null; } List<Symbol> outputs = table.querySpec().outputs(); assert !table.querySpec().orderBy().isPresent() : "insert from subquery with order by is not supported"; for (int i = 0; i < outputs.size(); i++) { outputs.set(i, DocReferences.toSourceLookup(outputs.get(i))); } return null; } @Override protected Void visitAnalyzedRelation(AnalyzedRelation relation, Void context) { return null; } } }