Package com.orientechnologies.orient.core.sql

Source Code of com.orientechnologies.orient.core.sql.OCommandExecutorSQLInsert

/*
  *
  *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
  *  *
  *  *  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.
  *  *
  *  * For more information: http://www.orientechnologies.com
  *
  */
package com.orientechnologies.orient.core.sql;

import com.orientechnologies.orient.core.command.OCommandDistributedReplicateRequest;
import com.orientechnologies.orient.core.command.OCommandRequest;
import com.orientechnologies.orient.core.command.OCommandRequestText;
import com.orientechnologies.orient.core.command.OCommandResultListener;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper;
import com.orientechnologies.orient.core.sql.filter.OSQLFilterItemField;
import com.orientechnologies.orient.core.sql.query.OSQLAsynchQuery;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

/**
* SQL INSERT command.
*
* @author Luca Garulli
* @author Johann Sorel (Geomatys)
*/
public class OCommandExecutorSQLInsert extends OCommandExecutorSQLSetAware implements OCommandDistributedReplicateRequest,
    OCommandResultListener {
  public static final String             KEYWORD_INSERT   = "INSERT";
  protected static final String          KEYWORD_RETURN   = "RETURN";
  private static final String            KEYWORD_VALUES   = "VALUES";
  private String                         className        = null;
  private String                         clusterName      = null;
  private String                         indexName        = null;
  private List<Map<String, Object>>      newRecords;
  private OSQLAsynchQuery<OIdentifiable> subQuery         = null;
  private AtomicLong                     saved            = new AtomicLong(0);
  private Object                         returnExpression = null;
  private List<ODocument>                queryResult      = null;

  @SuppressWarnings("unchecked")
  public OCommandExecutorSQLInsert parse(final OCommandRequest iRequest) {
    final ODatabaseRecord database = getDatabase();

    init((OCommandRequestText) iRequest);

    className = null;
    newRecords = null;
    content = null;

    parserRequiredKeyword("INSERT");
    parserRequiredKeyword("INTO");

    String subjectName = parserRequiredWord(true, "Invalid subject name. Expected cluster, class or index");
    if (subjectName.startsWith(OCommandExecutorSQLAbstract.CLUSTER_PREFIX))
      // CLUSTER
      clusterName = subjectName.substring(OCommandExecutorSQLAbstract.CLUSTER_PREFIX.length());

    else if (subjectName.startsWith(OCommandExecutorSQLAbstract.INDEX_PREFIX))
      // INDEX
      indexName = subjectName.substring(OCommandExecutorSQLAbstract.INDEX_PREFIX.length());

    else {
      // CLASS
      if (subjectName.startsWith(OCommandExecutorSQLAbstract.CLASS_PREFIX))
        subjectName = subjectName.substring(OCommandExecutorSQLAbstract.CLASS_PREFIX.length());

      final OClass cls = database.getMetadata().getSchema().getClass(subjectName);
      if (cls == null)
        throwParsingException("Class " + subjectName + " not found in database");

      className = cls.getName();
    }

    parserSkipWhiteSpaces();
    if (parserIsEnded())
      throwSyntaxErrorException("Set of fields is missed. Example: (name, surname) or SET name = 'Bill'");

    final String temp = parseOptionalWord(true);
    if (temp.equals("CLUSTER")) {
      clusterName = parserRequiredWord(false);

      parserSkipWhiteSpaces();
      if (parserIsEnded())
        throwSyntaxErrorException("Set of fields is missed. Example: (name, surname) or SET name = 'Bill'");
    } else
      parserGoBack();

    newRecords = new ArrayList<Map<String, Object>>();
    Boolean sourceClauseProcessed = false;
    if (parserGetCurrentChar() == '(') {
      parseValues();
      parserNextWord(true, " \r\n");
      sourceClauseProcessed = true;
    } else {
      parserNextWord(true, " ,\r\n");

      if (parserGetLastWord().equals(KEYWORD_CONTENT)) {
        newRecords = null;
        parseContent();
        sourceClauseProcessed = true;
      } else if (parserGetLastWord().equals(KEYWORD_SET)) {
        final LinkedHashMap<String, Object> fields = new LinkedHashMap<String, Object>();
        newRecords.add(fields);
        parseSetFields(fields);
        sourceClauseProcessed = true;
      }
    }
    if (sourceClauseProcessed)
      parserNextWord(true, " \r\n");
    // it has to be processed before KEYWORD_FROM in order to not be taken as part of SELECT
    if (parserGetLastWord().equals(KEYWORD_RETURN)) {
      parseReturn(!sourceClauseProcessed);
      parserNextWord(true, " \r\n");
    }

    if (!sourceClauseProcessed) {
      if (parserGetLastWord().equals(KEYWORD_FROM)) {
        newRecords = null;
        subQuery = new OSQLAsynchQuery<OIdentifiable>(parserText.substring(parserGetCurrentPosition()), this);
      }
    }

    return this;
  }

  /**
   * Execute the INSERT and return the ODocument object created.
   */
  public Object execute(final Map<Object, Object> iArgs) {
    if (newRecords == null && content == null && subQuery == null)
      throw new OCommandExecutionException("Cannot execute the command because it has not been parsed yet");

    final OCommandParameters commandParameters = new OCommandParameters(iArgs);
    if (indexName != null) {
      if (newRecords == null)
        throw new OCommandExecutionException("No key/value found");

      final OIndex<?> index = getDatabase().getMetadata().getIndexManager().getIndex(indexName);
      if (index == null)
        throw new OCommandExecutionException("Target index '" + indexName + "' not found");

      // BIND VALUES
      Map<String, Object> result = null;

      for (Map<String, Object> candidate : newRecords) {
        index.put(getIndexKeyValue(commandParameters, candidate), getIndexValue(commandParameters, candidate));
        result = candidate;
      }

      // RETURN LAST ENTRY
      return prepareReturnItem(new ODocument(result));
    } else {

      // CREATE NEW DOCUMENTS
      final List<ODocument> docs = new ArrayList<ODocument>();
      if (newRecords != null) {
        for (Map<String, Object> candidate : newRecords) {
          final ODocument doc = className != null ? new ODocument(className) : new ODocument();
          OSQLHelper.bindParameters(doc, candidate, commandParameters, context);

          saveRecord(doc);
          docs.add(doc);
        }

        if (docs.size() == 1)
          return prepareReturnItem(docs.get(0));
        else
          return prepareReturnResult(docs);
      } else if (content != null) {
        final ODocument doc = className != null ? new ODocument(className) : new ODocument();
        doc.merge(content, true, false);
        saveRecord(doc);
        return prepareReturnItem(doc);
      } else if (subQuery != null) {
        subQuery.execute();
        if (queryResult != null)
          return prepareReturnResult(queryResult);

        return saved.longValue();
      }
    }
    return null;
  }

  @Override
  public OCommandDistributedReplicateRequest.DISTRIBUTED_EXECUTION_MODE getDistributedExecutionMode() {
    return indexName != null ? DISTRIBUTED_EXECUTION_MODE.REPLICATE : DISTRIBUTED_EXECUTION_MODE.LOCAL;
  }

  @Override
  public Set<String> getInvolvedClusters() {
    if (className != null) {
      final OClass clazz = getDatabase().getMetadata().getSchema().getClass(className);
      return Collections.singleton(getDatabase().getClusterNameById(clazz.getClusterSelection().getCluster(clazz)));
    } else if (clusterName != null)
      return getInvolvedClustersOfClusters(Collections.singleton(clusterName));

    return Collections.EMPTY_SET;
  }

  @Override
  public String getSyntax() {
    return "INSERT INTO [class:]<class>|cluster:<cluster>|index:<index> [(<field>[,]*) VALUES (<expression>[,]*)[,]*]|[SET <field> = <expression>|<sub-command>[,]*]|CONTENT {<JSON>} [RETURN <expression>] [FROM select-query]";
  }

  @Override
  public boolean result(final Object iRecord) {
    final ORecord rec = ((OIdentifiable) iRecord).getRecord().copy();

    // RESET THE IDENTITY TO AVOID UPDATE
    rec.getIdentity().reset();

    if (rec instanceof ODocument && className != null)
      ((ODocument) rec).setClassName(className);

    rec.setDirty();
    synchronized (this) {
      saveRecord(rec);
      if (queryResult != null)
        queryResult.add(((ODocument) rec));
    }

    return true;
  }

  @Override
  public void end() {

  }

  protected Object prepareReturnResult(List<ODocument> res) {
    if (returnExpression == null)
      return res;// No transformation
    final ArrayList<Object> ret = new ArrayList<Object>();
    for (ODocument resItem : (List<ODocument>) res)
      ret.add(prepareReturnItem(resItem));
    return ret;
  }

  protected Object prepareReturnItem(ODocument item) {
    if (returnExpression == null)
      return item;// No transformation

    this.getContext().setVariable("current", item);
    final Object res = OSQLHelper.getValue(returnExpression, item, this.getContext());
    if (res instanceof OIdentifiable)
      return res;
    else {// wrapping doc
      final ODocument wrappingDoc = new ODocument("result", res);
      wrappingDoc.field("rid", item.getIdentity());// passing record id.In many cases usable on client side
      wrappingDoc.field("version", item.getVersion());// passing record version
      return wrappingDoc;
    }
  }

  protected void saveRecord(final ORecord rec) {
    if (clusterName != null)
      rec.save(clusterName);
    else
      rec.save();
    saved.incrementAndGet();
  }

  protected void parseValues() {
    final int beginFields = parserGetCurrentPosition();

    final int endFields = parserText.indexOf(')', beginFields + 1);
    if (endFields == -1)
      throwSyntaxErrorException("Missed closed brace");

    final ArrayList<String> fieldNames = new ArrayList<String>();
    parserSetCurrentPosition(OStringSerializerHelper.getParameters(parserText, beginFields, endFields, fieldNames));
    if (fieldNames.size() == 0)
      throwSyntaxErrorException("Set of fields is empty. Example: (name, surname)");

    // REMOVE QUOTATION MARKS IF ANY
    for (int i = 0; i < fieldNames.size(); ++i)
      fieldNames.set(i, OStringSerializerHelper.removeQuotationMarks(fieldNames.get(i)));

    parserRequiredKeyword(KEYWORD_VALUES);
    parserSkipWhiteSpaces();
    if (parserIsEnded() || parserText.charAt(parserGetCurrentPosition()) != '(') {
      throwParsingException("Set of values is missed. Example: ('Bill', 'Stuart', 300)");
    }

    int blockStart = parserGetCurrentPosition();
    int blockEnd = parserGetCurrentPosition();

    final List<String> records = OStringSerializerHelper.smartSplit(parserText, new char[] { ',' }, blockStart, -1, true, true,
        false, false);
    for (String record : records) {

      final List<String> values = new ArrayList<String>();
      blockEnd += OStringSerializerHelper.getParameters(record, 0, -1, values);

      if (blockEnd == -1)
        throw new OCommandSQLParsingException("Missed closed brace. Use " + getSyntax(), parserText, blockStart);

      if (values.isEmpty())
        throw new OCommandSQLParsingException("Set of values is empty. Example: ('Bill', 'Stuart', 300). Use " + getSyntax(),
            parserText, blockStart);

      if (values.size() != fieldNames.size())
        throw new OCommandSQLParsingException("Fields not match with values", parserText, blockStart);

      // TRANSFORM FIELD VALUES
      final Map<String, Object> fields = new LinkedHashMap<String, Object>();
      for (int i = 0; i < values.size(); ++i)
        fields.put(fieldNames.get(i), OSQLHelper.parseValue(this, OStringSerializerHelper.decode(values.get(i).trim()), context));

      newRecords.add(fields);
      blockStart = blockEnd;
    }

  }

  /**
   * Parses the returning keyword if found.
   */
  protected void parseReturn(Boolean subQueryExpected) throws OCommandSQLParsingException {
    parserNextWord(false, " ");
    String returning = parserGetLastWord().trim();
    if (returning.startsWith("$") || returning.startsWith("@")) {
      if (subQueryExpected)
        queryResult = new ArrayList<ODocument>();
      returnExpression = (returning.length() > 0) ? OSQLHelper.parseValue(this, returning, this.getContext()) : null;
    } else
      throwSyntaxErrorException("record attribute (@attributes) or functions with $current variable expected");

  }

  private Object getIndexKeyValue(OCommandParameters commandParameters, Map<String, Object> candidate) {
    final Object parsedKey = candidate.get(KEYWORD_KEY);
    if (parsedKey instanceof OSQLFilterItemField) {
      final OSQLFilterItemField f = (OSQLFilterItemField) parsedKey;
      if (f.getRoot().equals("?"))
        // POSITIONAL PARAMETER
        return commandParameters.getNext();
      else if (f.getRoot().startsWith(":"))
        // NAMED PARAMETER
        return commandParameters.getByName(f.getRoot().substring(1));
    }
    return parsedKey;
  }

  private OIdentifiable getIndexValue(OCommandParameters commandParameters, Map<String, Object> candidate) {
    final Object parsedRid = candidate.get(KEYWORD_RID);
    if (parsedRid instanceof OSQLFilterItemField) {
      final OSQLFilterItemField f = (OSQLFilterItemField) parsedRid;
      if (f.getRoot().equals("?"))
        // POSITIONAL PARAMETER
        return (OIdentifiable) commandParameters.getNext();
      else if (f.getRoot().startsWith(":"))
        // NAMED PARAMETER
        return (OIdentifiable) commandParameters.getByName(f.getRoot().substring(1));
    }
    return (OIdentifiable) parsedRid;
  }

}
TOP

Related Classes of com.orientechnologies.orient.core.sql.OCommandExecutorSQLInsert

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.