All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arcadedb.query.sql.executor.UpdateExecutionPlanner Maven / Gradle / Ivy

There is a newer version: 24.11.1
Show newest version
/*
 * Copyright © 2021-present Arcade Data Ltd ([email protected])
 *
 * Licensed 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.
 *
 * SPDX-FileCopyrightText: 2021-present Arcade Data Ltd ([email protected])
 * SPDX-License-Identifier: Apache-2.0
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.query.sql.parser.Bucket;
import com.arcadedb.query.sql.parser.FromClause;
import com.arcadedb.query.sql.parser.Identifier;
import com.arcadedb.query.sql.parser.Limit;
import com.arcadedb.query.sql.parser.Projection;
import com.arcadedb.query.sql.parser.SelectStatement;
import com.arcadedb.query.sql.parser.Timeout;
import com.arcadedb.query.sql.parser.UpdateEdgeStatement;
import com.arcadedb.query.sql.parser.UpdateOperations;
import com.arcadedb.query.sql.parser.UpdateStatement;
import com.arcadedb.query.sql.parser.WhereClause;

import java.util.*;
import java.util.stream.*;

/**
 * Created by luigidellaquila on 08/08/16.
 */
public class UpdateExecutionPlanner {
  private final   FromClause             target;
  public final    WhereClause            whereClause;
  protected final boolean                upsert;
  protected final List operations;
  protected final boolean                returnBefore;
  protected final boolean                returnAfter;
  protected final boolean                returnCount;
  protected       boolean                updateEdge = false;
  protected final Projection             returnProjection;
  public final    Limit                  limit;
  public final    Timeout                timeout;

  public UpdateExecutionPlanner(final UpdateStatement oUpdateStatement) {
    if (oUpdateStatement instanceof UpdateEdgeStatement) {
      updateEdge = true;
    }
    this.target = oUpdateStatement.getTarget().copy();
    this.whereClause = oUpdateStatement.getWhereClause() == null ? null : oUpdateStatement.getWhereClause().copy();
    this.operations =
        oUpdateStatement.getOperations() == null ? null : oUpdateStatement.getOperations().stream().map(x -> x.copy()).collect(Collectors.toList());
    this.upsert = oUpdateStatement.isUpsert();

    this.returnBefore = oUpdateStatement.isReturnBefore();
    this.returnAfter = oUpdateStatement.isReturnAfter();
    this.returnCount = !(returnAfter || returnBefore);
    this.returnProjection = oUpdateStatement.getReturnProjection() == null ? null : oUpdateStatement.getReturnProjection().copy();
    this.limit = oUpdateStatement.getLimit() == null ? null : oUpdateStatement.getLimit().copy();
    this.timeout = oUpdateStatement.getTimeout() == null ? null : oUpdateStatement.getTimeout().copy();
  }

  public UpdateExecutionPlan createExecutionPlan(final CommandContext context, final boolean enableProfiling) {
    final UpdateExecutionPlan result = new UpdateExecutionPlan(context);

    handleTarget(result, context, this.target, this.whereClause, this.timeout, enableProfiling);
    if (updateEdge) {
      result.chain(new CheckRecordTypeStep(context, "E", enableProfiling));
    }
    handleUpsert(result, context, this.target, this.whereClause, this.upsert, enableProfiling);
    handleTimeout(result, context, this.timeout, enableProfiling);
    convertToModifiableResult(result, context, enableProfiling);
    handleLimit(result, context, this.limit, enableProfiling);
    handleReturnBefore(result, context, this.returnBefore, enableProfiling);
    handleOperations(result, context, this.operations, enableProfiling);
    handleSave(result, target.getItem().getBucket(), context, enableProfiling);
    handleResultForReturnBefore(result, context, this.returnBefore, returnProjection, enableProfiling);
    handleResultForReturnAfter(result, context, this.returnAfter, returnProjection, enableProfiling);
    handleResultForReturnCount(result, context, this.returnCount, enableProfiling);
    return result;
  }

  /**
   * add a step that transforms a normal OResult in a specific object that under setProperty() updates the actual PIdentifiable
   *
   * @param plan    the execution plan
   * @param context the execution context
   */
  private void convertToModifiableResult(final UpdateExecutionPlan plan, final CommandContext context, final boolean profilingEnabled) {
    plan.chain(new ConvertToUpdatableResultStep(context, profilingEnabled));
  }

  private void handleResultForReturnCount(final UpdateExecutionPlan result, final CommandContext context, final boolean returnCount,
      final boolean profilingEnabled) {
    if (returnCount) {
      result.chain(new CountStep(context, profilingEnabled));
    }
  }

  private void handleResultForReturnAfter(final UpdateExecutionPlan result, final CommandContext context, final boolean returnAfter,
      final Projection returnProjection, final boolean profilingEnabled) {
    if (returnAfter) {
      //re-convert to normal step
      result.chain(new ConvertToResultInternalStep(context, profilingEnabled));
      if (returnProjection != null) {
        result.chain(new ProjectionCalculationStep(returnProjection, context, profilingEnabled));
      }
    }
  }

  private void handleResultForReturnBefore(final UpdateExecutionPlan result, final CommandContext context, final boolean returnBefore,
      final Projection returnProjection, final boolean profilingEnabled) {
    if (returnBefore) {
      result.chain(new UnwrapPreviousValueStep(context, profilingEnabled));
      if (returnProjection != null) {
        result.chain(new ProjectionCalculationStep(returnProjection, context, profilingEnabled));
      }
    }
  }

  private void handleSave(final UpdateExecutionPlan result, final Bucket bucket, final CommandContext context, final boolean profilingEnabled) {
    if (bucket != null) {
      final String bucketName =
          bucket.getBucketName() != null ? bucket.getBucketName() : context.getDatabase().getSchema().getBucketById(bucket.getBucketNumber()).getName();
      result.chain(new SaveElementStep(context, new Identifier(bucketName), profilingEnabled));
    } else
      result.chain(new SaveElementStep(context, profilingEnabled));
  }

  private void handleTimeout(final UpdateExecutionPlan result, final CommandContext context, final Timeout timeout, final boolean profilingEnabled) {
    if (timeout != null && timeout.getVal().longValue() > 0) {
      result.chain(new TimeoutStep(timeout, context, profilingEnabled));
    }
  }

  private void handleReturnBefore(final UpdateExecutionPlan result, final CommandContext context, final boolean returnBefore, final boolean profilingEnabled) {
    if (returnBefore) {
      result.chain(new CopyRecordContentBeforeUpdateStep(context, profilingEnabled));
    }
  }

  private void handleLimit(final UpdateExecutionPlan plan, final CommandContext context, final Limit limit, final boolean profilingEnabled) {
    if (limit != null) {
      plan.chain(new LimitExecutionStep(limit, context, profilingEnabled));
    }
  }

  private void handleUpsert(final UpdateExecutionPlan plan, final CommandContext context, final FromClause target, final WhereClause where,
      final boolean upsert, final boolean profilingEnabled) {
    if (upsert) {
      plan.chain(new UpsertStep(target, where, context, profilingEnabled));
    }
  }

  private void handleOperations(final UpdateExecutionPlan plan, final CommandContext context, final List ops,
      final boolean profilingEnabled) {
    if (ops != null) {
      for (final UpdateOperations op : ops) {
        switch (op.getType()) {
        case UpdateOperations.TYPE_SET:
          plan.chain(new UpdateSetStep(op.getUpdateItems(), context, profilingEnabled));
          //TODO: ARCADEDB MANAGES EDGES IN DIFFERENT WAY. DO WE NEED THIS?
          //if(updateEdge){
          //plan.chain(new UpdateEdgePointersStep( context, profilingEnabled));
          //}
          break;
        case UpdateOperations.TYPE_REMOVE:
          plan.chain(new UpdateRemoveStep(op.getUpdateRemoveItems(), context, profilingEnabled));
          break;
        case UpdateOperations.TYPE_MERGE:
          plan.chain(new UpdateMergeStep(op.getJson(), context, profilingEnabled));
          break;
        case UpdateOperations.TYPE_CONTENT:
          plan.chain(new UpdateContentStep(op.getJson(), context, profilingEnabled));
          break;
        case UpdateOperations.TYPE_PUT:
        case UpdateOperations.TYPE_INCREMENT:
        case UpdateOperations.TYPE_ADD:
          throw new CommandExecutionException("Cannot execute with UPDATE PUT/ADD/INCREMENT new executor: " + op);
        }
      }
    }
  }

  private void handleTarget(final UpdateExecutionPlan result, final CommandContext context, final FromClause target, final WhereClause whereClause,
      final Timeout timeout, final boolean profilingEnabled) {
    final SelectStatement sourceStatement = new SelectStatement(-1);
    sourceStatement.setTarget(target);
    sourceStatement.setWhereClause(whereClause);
    if (timeout != null) {
      sourceStatement.setTimeout(this.timeout.copy());
    }
    final SelectExecutionPlanner planner = new SelectExecutionPlanner(sourceStatement);
    result.chain(new SubQueryStep(planner.createExecutionPlan(context, profilingEnabled), context, context, profilingEnabled));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy