/* * 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.statement; import com.google.common.collect.ImmutableList; import io.crate.analyze.DeleteAnalyzedStatement; import io.crate.analyze.WhereClause; import io.crate.analyze.relations.DocTableRelation; import io.crate.analyze.symbol.InputColumn; import io.crate.analyze.where.DocKeys; import io.crate.metadata.Reference; import io.crate.metadata.Routing; import io.crate.metadata.doc.DocSysColumns; import io.crate.metadata.doc.DocTableInfo; import io.crate.metadata.table.TableInfo; import io.crate.operation.projectors.TopN; import io.crate.planner.Merge; import io.crate.planner.NoopPlan; import io.crate.planner.Plan; import io.crate.planner.Planner; import io.crate.planner.distribution.DistributionInfo; import io.crate.planner.node.ddl.ESDeletePartition; import io.crate.planner.node.dml.Delete; import io.crate.planner.node.dml.ESDelete; import io.crate.planner.node.dql.Collect; import io.crate.planner.node.dql.RoutedCollectPhase; import io.crate.planner.projection.DeleteProjection; import io.crate.planner.projection.MergeCountProjection; import io.crate.types.DataTypes; import org.elasticsearch.cluster.routing.Preference; import java.util.*; public final class DeleteStatementPlanner { public static Plan planDelete(DeleteAnalyzedStatement analyzedStatement, Planner.Context context) { DocTableRelation tableRelation = analyzedStatement.analyzedRelation(); List<WhereClause> whereClauses = new ArrayList<>(analyzedStatement.whereClauses().size()); List<DocKeys.DocKey> docKeys = new ArrayList<>(analyzedStatement.whereClauses().size()); Map<Integer, Integer> itemToBulkIdx = new HashMap<>(); int bulkIdx = -1; int itemIdx = 0; for (WhereClause whereClause : analyzedStatement.whereClauses()) { bulkIdx++; if (whereClause.noMatch()) { continue; } if (whereClause.docKeys().isPresent() && whereClause.docKeys().get().size() == 1) { DocKeys.DocKey docKey = whereClause.docKeys().get().getOnlyKey(); if (docKey.id() != null) { docKeys.add(docKey); itemToBulkIdx.put(itemIdx, bulkIdx); itemIdx++; } } else if (!whereClause.noMatch()) { whereClauses.add(whereClause); } } if (!docKeys.isEmpty()) { return new ESDelete(context.jobId(), context.nextExecutionPhaseId(), tableRelation.tableInfo(), docKeys, itemToBulkIdx, analyzedStatement.whereClauses().size()); } else if (!whereClauses.isEmpty()) { return deleteByQuery(tableRelation.tableInfo(), whereClauses, context); } return new NoopPlan(context.jobId()); } private static Plan deleteByQuery(DocTableInfo tableInfo, List<WhereClause> whereClauses, Planner.Context context) { List<Plan> planNodes = new ArrayList<>(); List<String> indicesToDelete = new ArrayList<>(); for (WhereClause whereClause : whereClauses) { String[] indices = Planner.indices(tableInfo, whereClause); if (indices.length > 0) { if (!whereClause.hasQuery() && tableInfo.isPartitioned()) { indicesToDelete.addAll(Arrays.asList(indices)); } else { planNodes.add(collectWithDeleteProjection(tableInfo, whereClause, context)); } } } if (!indicesToDelete.isEmpty()) { assert planNodes.isEmpty() : "If a partition can be deleted that must be true for all bulk operations"; return new ESDeletePartition(context.jobId(), indicesToDelete.toArray(new String[0])); } if (planNodes.isEmpty()) { return new NoopPlan(context.jobId()); } return new Delete(planNodes, context.jobId()); } private static Plan collectWithDeleteProjection(TableInfo tableInfo, WhereClause whereClause, Planner.Context plannerContext) { // for delete, we always need to collect the `_uid` Reference idReference = tableInfo.getReference(DocSysColumns.ID); DeleteProjection deleteProjection = new DeleteProjection( new InputColumn(0, DataTypes.STRING)); Routing routing = plannerContext.allocateRouting(tableInfo, whereClause, Preference.PRIMARY.type()); RoutedCollectPhase collectPhase = new RoutedCollectPhase( plannerContext.jobId(), plannerContext.nextExecutionPhaseId(), "collect", routing, tableInfo.rowGranularity(), ImmutableList.of(idReference), ImmutableList.of(deleteProjection), whereClause, DistributionInfo.DEFAULT_BROADCAST ); Collect collect = new Collect(collectPhase, TopN.NO_LIMIT, 0, 1, 1, null); return Merge.ensureOnHandler(collect, plannerContext, Collections.singletonList(MergeCountProjection.INSTANCE)); } }