<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/CheckSafeDeleteStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * <p>
 * Checks if a record can be safely deleted (throws PCommandExecutionException in case).
 * A record cannot be safely deleted if it's a vertex or an edge (it requires additional operations).</p>
 * <p>
 * The result set returned by syncPull() throws an PCommandExecutionException as soon as it finds a record
 * that cannot be safely deleted (eg. a vertex or an edge)</p>
 * <p>This step is used used in DELETE statement to make sure that you are not deleting vertices or edges without passing for an
 * explicit DELETE VERTEX/EDGE</p>
 *
 * @author <NAME> (luigi.dellaquila-(at)-gmail.com)
 */
public class CheckSafeDeleteStep extends AbstractExecutionStep {
  private long cost = 0;

  public CheckSafeDeleteStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        Result result = upstream.next();
        long begin = profilingEnabled ? System.nanoTime() : 0;
        try {
//          if (result.isElement()) {
//
//            Record record = result.getElement().get();
//            if (record instanceof Document) {
          //TODO
//              Document doc = (Document) record;
//              DocumentType typez = doc.getType();
//              if (typez != null) {
//                if (typez.getName().equalsIgnoreCase("V") || typez.isSubClassOf("V")) {
//                  throw new PCommandExecutionException("Cannot safely delete a vertex, please use DELETE VERTEX or UNSAFE");
//                }
//                if (typez.getName().equalsIgnoreCase("E") || typez.isSubClassOf("E")) {
//                  throw new PCommandExecutionException("Cannot safely delete an edge, please use DELETE EDGE or UNSAFE");
//                }
//              }
//            }
//          }
          return result;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }

      @Override
      public void close() {

      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ CHECK SAFE DELETE");
    if (profilingEnabled) {
      result.append(" (").append(getCostFormatted()).append(")");
    }
    return result.toString();
  }

  @Override
  public long getCost() {
    return cost;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/parser/PatternTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.parser;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/** Created by luigidellaquila on 11/10/16. */
public class PatternTest extends ParserTestAbstract {

  @Test
  public void testSimplePattern() {
    String query = "MATCH {as:a, type:Person} return a";
    SqlParser parser = getParserFor(query);
    try {
      MatchStatement stm = (MatchStatement) parser.parse();
      stm.buildPatterns();
      Pattern pattern = stm.pattern;
      Assertions.assertEquals(0, pattern.getNumOfEdges());
      Assertions.assertEquals(1, pattern.getAliasToNode().size());
      Assertions.assertNotNull(pattern.getAliasToNode().get("a"));
      Assertions.assertEquals(1, pattern.getDisjointPatterns().size());
    } catch (ParseException e) {
      Assertions.fail();
    }
  }

  @Test
  public void testCartesianProduct() {
    String query = "MATCH {as:a, type:Person}, {as:b, type:Person} return a, b";
    SqlParser parser = getParserFor(query);
    try {
      MatchStatement stm = (MatchStatement) parser.parse();
      stm.buildPatterns();
      Pattern pattern = stm.pattern;
      Assertions.assertEquals(0, pattern.getNumOfEdges());
      Assertions.assertEquals(2, pattern.getAliasToNode().size());
      Assertions.assertNotNull(pattern.getAliasToNode().get("a"));
      List<Pattern> subPatterns = pattern.getDisjointPatterns();
      Assertions.assertEquals(2, subPatterns.size());
      Assertions.assertEquals(0, subPatterns.get(0).getNumOfEdges());
      Assertions.assertEquals(1, subPatterns.get(0).getAliasToNode().size());
      Assertions.assertEquals(0, subPatterns.get(1).getNumOfEdges());
      Assertions.assertEquals(1, subPatterns.get(1).getAliasToNode().size());

      Set<String> aliases = new HashSet<>();
      aliases.add("a");
      aliases.add("b");
      aliases.remove(subPatterns.get(0).getAliasToNode().keySet().iterator().next());
      aliases.remove(subPatterns.get(1).getAliasToNode().keySet().iterator().next());
      Assertions.assertEquals(0, aliases.size());

    } catch (ParseException e) {
      Assertions.fail();
    }
  }

  @Test
  public void testComplexCartesianProduct() {
    String query =
        "MATCH {as:a, type:Person}-->{as:b}, {as:c, type:Person}-->{as:d}-->{as:e}, {as:d, type:Foo}-->{as:f} return a, b";
    SqlParser parser = getParserFor(query);
    try {
      MatchStatement stm = (MatchStatement) parser.parse();
      stm.buildPatterns();
      Pattern pattern = stm.pattern;
      Assertions.assertEquals(4, pattern.getNumOfEdges());
      Assertions.assertEquals(6, pattern.getAliasToNode().size());
      Assertions.assertNotNull(pattern.getAliasToNode().get("a"));
      List<Pattern> subPatterns = pattern.getDisjointPatterns();
      Assertions.assertEquals(2, subPatterns.size());

      Set<String> aliases = new HashSet<>();
      aliases.add("a");
      aliases.add("b");
      aliases.add("c");
      aliases.add("d");
      aliases.add("e");
      aliases.add("f");
      aliases.removeAll(subPatterns.get(0).getAliasToNode().keySet());
      aliases.removeAll(subPatterns.get(1).getAliasToNode().keySet());
      Assertions.assertEquals(0, aliases.size());

    } catch (ParseException e) {
      Assertions.fail();
    }
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/SelectStatementExecutionTestIT.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.database.MutableDocument;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * Created by tglman on 09/06/17.
 */
public class SelectStatementExecutionTestIT extends TestHelper {

    public SelectStatementExecutionTestIT() {
        autoStartTx = true;
    }

    @Test
    public void stressTest() {
        String className = "stressTestNew";
        database.getSchema().createDocumentType(className);
        for (int i = 0; i < 1000000; i++) {
            MutableDocument doc = database.newDocument(className);
            doc.set("name", "name" + i);
            doc.set("surname", "surname" + i);
            doc.save();
        }

        for (int run = 0; run < 5; run++) {
            long begin = System.nanoTime();
            ResultSet result = database.query("sql", "select name from " + className + " where name <> 'name1' ");
            for (int i = 0; i < 999999; i++) {
                //        Assertions.assertTrue(result.hasNext());
                Result item = result.next();
                //        Assertions.assertNotNull(item);
                Object name = item.getProperty("name");
                Assertions.assertFalse("name1".equals(name));
            }
            Assertions.assertFalse(result.hasNext());
            result.close();
            long end = System.nanoTime();
        }
    }

}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/parser/ExecutionPlanCacheTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.parser;

import com.arcadedb.database.DatabaseFactory;
import com.arcadedb.database.DatabaseInternal;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Property;
import com.arcadedb.schema.Schema;
import com.arcadedb.schema.Type;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class ExecutionPlanCacheTest {

  @Test
  public void testCacheInvalidation1() throws InterruptedException {
    String testName = "testCacheInvalidation1";

    final DatabaseInternal db = (DatabaseInternal) new DatabaseFactory("ExecutionPlanCacheTest").create();
    try {
      db.begin();
      db.getSchema().createDocumentType("OUser");
      db.newDocument("OUser").set("name", "jay").save();
      db.commit();

      ExecutionPlanCache cache = ExecutionPlanCache.instance(db);
      String stm = "SELECT FROM OUser";

      /*
       * the cache has a mechanism that guarantees that if you are doing execution planning
       * and the cache is invalidated in the meantime, the newly generated execution plan
       * is not cached. This mechanism relies on a System.currentTimeMillis(), so it can happen
       * that the execution planning is done right after the cache invalidation, but still in THE SAME
       * millisecond, this Thread.sleep() guarantees that the new execution plan is generated
       * at least one ms after last invalidation, so it is cached.
       */
      Thread.sleep(2);

      // schema changes
      db.query("sql", stm).close();
      cache = ExecutionPlanCache.instance(db);
      Assertions.assertTrue(cache.contains(stm));

      DocumentType clazz = db.getSchema().createDocumentType(testName);
      Assertions.assertFalse(cache.contains(stm));

      Thread.sleep(2);

      // schema changes 2
      db.query("sql", stm).close();
      cache = ExecutionPlanCache.instance(db);
      Assertions.assertTrue(cache.contains(stm));

      Property prop = clazz.createProperty("name", Type.STRING);
      Assertions.assertFalse(cache.contains(stm));

      Thread.sleep(2);

      // index changes
      db.query("sql", stm).close();
      cache = ExecutionPlanCache.instance(db);
      Assertions.assertTrue(cache.contains(stm));

      db.getSchema().createTypeIndex(Schema.INDEX_TYPE.LSM_TREE, false, testName, "name");
      Assertions.assertFalse(cache.contains(stm));

    } finally {
      db.drop();
      Assertions.assertTrue(DatabaseFactory.getActiveDatabaseInstances().isEmpty(), "Found active databases: " + DatabaseFactory.getActiveDatabaseInstances());
    }
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/engine/MutablePage.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.engine;

import com.arcadedb.database.Binary;
import com.arcadedb.database.DatabaseFactory;
import com.arcadedb.database.TrackableBinary;

/**
 * Mutable page that accepts updates. It keeps track of the modified bytes.
 */
public class MutablePage extends BasePage implements TrackableContent {
  private int     modifiedRangeFrom = Integer.MAX_VALUE;
  private int     modifiedRangeTo   = -1;
  private WALFile walFile;

  public MutablePage(final PageManager manager, final PageId pageId, final int size) {
    this(manager, pageId, size, new byte[size], 0, 0);
    updateModifiedRange(0, size - 1);
  }

  public MutablePage(final PageManager manager, final PageId pageId, final int size, final byte[] array, final int version, final int contentSize) {
    super(manager, pageId, size, array, version, contentSize);
  }

  public TrackableBinary getTrackable() {
    content.getByteBuffer().position(PAGE_HEADER_SIZE);
    return new TrackableBinary(this, content.getByteBuffer().slice());
  }

  public void incrementVersion() {
    updateModifiedRange(0, Binary.LONG_SERIALIZED_SIZE);
    version++;
  }

  public int writeNumber(int index, final long content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.LONG_SERIALIZED_SIZE + 1); // WITH VARSIZE NUMBER THE WORST CASE SCENARIO IS 1 BYTE MORE
    return this.content.putNumber(index, content);
  }

  public void writeLong(int index, final long content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.LONG_SERIALIZED_SIZE);
    this.content.putLong(index, content);
  }

  public void writeInt(int index, final int content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.INT_SERIALIZED_SIZE);
    this.content.putInt(index, content);
  }

  public void writeUnsignedInt(int index, final long content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.INT_SERIALIZED_SIZE);
    this.content.putInt(index, (int) content);
  }

  public void writeShort(int index, final short content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.SHORT_SERIALIZED_SIZE);
    this.content.putShort(index, content);
  }

  public void writeUnsignedShort(int index, final int content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.SHORT_SERIALIZED_SIZE);
    this.content.putShort(index, (short) content);
  }

  public void writeByte(int index, final byte content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, Binary.BYTE_SERIALIZED_SIZE);
    this.content.putByte(index, content);
  }

  public int writeBytes(int index, final byte[] content) {
    index += PAGE_HEADER_SIZE;
    final int varSizeBytesUsed = this.content.getVarSize(content.length);
    checkBoundariesOnWrite(index, varSizeBytesUsed + content.length);
    return this.content.putBytes(index, content);
  }

  public void writeByteArray(int index, final byte[] content) {
    index += PAGE_HEADER_SIZE;
    checkBoundariesOnWrite(index, content.length);
    this.content.putByteArray(index, content);
  }

  public int writeString(final int index, final String content) {
    return writeBytes(index, content.getBytes(DatabaseFactory.getDefaultCharset()));
  }

  public int getAvailableContentSize() {
    return getPhysicalSize() - getContentSize();
  }

  @Override
  public int[] getModifiedRange() {
    return new int[] { modifiedRangeFrom, modifiedRangeTo };
  }

  @Override
  public void updateModifiedRange(final int start, final int end) {
    if (start < 0 || end >= getPhysicalSize())
      throw new IllegalArgumentException("Update range (" + start + "-" + end + ") out of bound (0-" + getPhysicalSize() + ")");

    if (start < modifiedRangeFrom)
      modifiedRangeFrom = start;
    if (end > modifiedRangeTo)
      modifiedRangeTo = end;
  }

  public WALFile getWALFile() {
    return walFile;
  }

  public void setWALFile(final WALFile WALFile) {
    this.walFile = WALFile;
  }

  public void move(int startPosition, int destPosition, final int length) {
    startPosition += PAGE_HEADER_SIZE;
    destPosition += PAGE_HEADER_SIZE;
    updateModifiedRange(startPosition, destPosition + length);
    content.move(startPosition, destPosition, length);
  }

  private void checkBoundariesOnWrite(final int start, final int length) {
    if (start < 0)
      throw new IllegalArgumentException("Invalid position " + start);

    if (start + length > getPhysicalSize())
      throw new IllegalArgumentException("Cannot write outside the page space (" + (start + length) + ">" + getPhysicalSize() + ")");

    updateModifiedRange(start, start + length - 1);
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/CountFromClassStepTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.query.sql.parser.Identifier;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class CountFromClassStepTest {

  private static final String ALIAS = "size";

  @Test
  public void shouldCountRecordsOfClass() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      String className = TestHelper.createRandomType(db).getName();
      for (int i = 0; i < 20; i++) {
        MutableDocument document = db.newDocument(className);
        document.save();
      }

      Identifier classIdentifier = new Identifier(className);

      BasicCommandContext context = new BasicCommandContext();
      context.setDatabase(db);
      CountFromClassStep step = new CountFromClassStep(classIdentifier, ALIAS, context, false);

      ResultSet result = step.syncPull(context, 20);
      Assertions.assertEquals(20, (long) result.next().getProperty(ALIAS));
      Assertions.assertFalse(result.hasNext());
    });
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/TruncateClassStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.index.Index;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Schema;
import com.arcadedb.schema.Type;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.*;

/**
 * @author <NAME> (l.dellaquila-(at)-orientdb.com)
 */
public class TruncateClassStatementExecutionTest extends TestHelper {

  @SuppressWarnings("unchecked")
  @Test
  public void testTruncateClass() {
    database.begin();

    Schema schema = database.getSchema();
    DocumentType testClass = getOrcreateDocumentType(schema);

    final Index index = getOrCreateIndex(testClass);

    database.command("sql", "truncate type test_class");

    database.newDocument(testClass.getName()).set("name", "x").set("data", Arrays.asList(1, 2)).save();
    database.newDocument(testClass.getName()).set("name", "y").set("data", Arrays.asList(3, 0)).save();

    database.command("sql", "truncate type test_class");

    database.newDocument(testClass.getName()).set("name", "x").set("data", Arrays.asList(5, 6, 7)).save();
    database.newDocument(testClass.getName()).set("name", "y").set("data", Arrays.asList(8, 9, -1)).save();

    ResultSet result = database.query("sql", "SElect from test_class");
    //    Assertions.assertEquals(result.size(), 2);

    Set<Integer> set = new HashSet<Integer>();
    while (result.hasNext()) {
      set.addAll(result.next().getProperty("data"));
    }
    result.close();
    Assertions.assertTrue(set.containsAll(Arrays.asList(5, 6, 7, 8, 9, -1)));

    schema.dropType("test_class");
    database.commit();
  }


  @Test
  public void testTruncateVertexClassSubclasses() {
    database.begin();
    database.command("sql", "create document type TestTruncateVertexClassSuperclass");
    database.command("sql", "create document type TestTruncateVertexClassSubclass extends TestTruncateVertexClassSuperclass");

    database.command("sql", "insert into TestTruncateVertexClassSuperclass set name = 'foo'");
    database.command("sql", "insert into TestTruncateVertexClassSubclass set name = 'bar'");

    ResultSet result = database.query("sql", "SElect from TestTruncateVertexClassSuperclass");
    for (int i = 0; i < 2; i++) {
      Assertions.assertTrue(result.hasNext());
      result.next();
    }
    Assertions.assertFalse(result.hasNext());
    result.close();

    database.command("sql", "truncate type TestTruncateVertexClassSuperclass ");
    result = database.query("sql", "SElect from TestTruncateVertexClassSubclass");
    Assertions.assertTrue(result.hasNext());
    result.next();
    Assertions.assertFalse(result.hasNext());
    result.close();

    database.command("sql", "truncate type TestTruncateVertexClassSuperclass polymorphic");
    result = database.query("sql", "SElect from TestTruncateVertexClassSubclass");
    Assertions.assertFalse(result.hasNext());
    result.close();
    database.commit();
  }

  @Test
  public void testTruncateVertexClassSubclassesWithIndex() {
    database.begin();
    database.command("sql", "create document type TestTruncateVertexClassSuperclassWithIndex");
    database.command("sql", "create property TestTruncateVertexClassSuperclassWithIndex.name STRING");
    database.command("sql", "create index TestTruncateVertexClassSuperclassWithIndex_index on TestTruncateVertexClassSuperclassWithIndex (name) NOTUNIQUE");

    database.command("sql", "create document type TestTruncateVertexClassSubclassWithIndex extends TestTruncateVertexClassSuperclassWithIndex");

    database.command("sql", "insert into TestTruncateVertexClassSuperclassWithIndex set name = 'foo'");
    database.command("sql", "insert into TestTruncateVertexClassSubclassWithIndex set name = 'bar'");

    database.command("sql", "truncate type TestTruncateVertexClassSubclassWithIndex");
    database.command("sql", "truncate type TestTruncateVertexClassSuperclassWithIndex polymorphic");
    database.commit();
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testTruncateClassWithCommandCache() {
    database.begin();

    Schema schema = database.getSchema();
    DocumentType testClass = getOrcreateDocumentType(schema);

    database.command("sql", "truncate type test_class");

    database.newDocument(testClass.getName()).set("name", "x").set("data", Arrays.asList(1, 2)).save();
    database.newDocument(testClass.getName()).set("name", "y").set("data", Arrays.asList(3, 0)).save();

    ResultSet result = database.query("sql", "SElect from test_class");
    Assertions.assertEquals(toList(result).size(), 2);

    result.close();
    database.command("sql", "truncate type test_class");

    result = database.query("sql", "SElect from test_class");
    Assertions.assertEquals(toList(result).size(), 0);
    result.close();

    schema.dropType("test_class");

    database.commit();
  }

  private List<Result> toList(ResultSet input) {
    List<Result> result = new ArrayList<>();
    while (input.hasNext()) {
      result.add(input.next());
    }
    return result;
  }

  private Index getOrCreateIndex(DocumentType testClass) {
    if (database.getSchema().existsIndex("test_class_by_data"))
      return database.getSchema().getIndexByName("test_class_by_data");

    testClass.createProperty("data", Type.LIST);
    return testClass.createTypeIndex(Schema.INDEX_TYPE.LSM_TREE, true, "data");
  }

  private DocumentType getOrcreateDocumentType(Schema schema) {
    DocumentType testClass;
    if (schema.existsType("test_class")) {
      testClass = schema.getType("test_class");
    } else {
      testClass = schema.createDocumentType("test_class");
    }
    return testClass;
  }
}


<|start_filename|>integration/src/main/java/com/arcadedb/integration/importer/ConsoleLogger.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.integration.importer;

/**
 * Console logger used by importers and exporters.
 *
 * @author <NAME>
 */
public class ConsoleLogger {
  private final int verboseLevel;

  public ConsoleLogger(final int verboseLevel) {
    this.verboseLevel = verboseLevel;
  }

  public void logLine(final int level, final String text, final Object... args) {
    if (level > verboseLevel)
      return;

    if (args.length == 0)
      System.out.println(text);
    else
      System.out.println(String.format(text, args));
  }

  public void log(final int level, final String text, final Object... args) {
    if (level > verboseLevel)
      return;

    if (args.length == 0)
      System.out.print(text);
    else
      System.out.print(String.format(text, args));
  }

  public void errorLine(final String text, final Object... args) {
    if (args.length == 0)
      System.out.println(text);
    else
      System.out.println(String.format(text, args));
  }

  public int getVerboseLevel() {
    return verboseLevel;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/CreatePropertyStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Property;
import com.arcadedb.schema.Type;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author <NAME> (l.dellaquila-(at)-orientdb.com)
 */
public class CreatePropertyStatementExecutionTest extends TestHelper {
  private static final String PROP_NAME     = "name";
  private static final String PROP_DIVISION = "division";
  private static final String PROP_OFFICERS = "officers";
  private static final String PROP_ID       = "id";

  @Test
  public void testBasicCreateProperty() throws Exception {
    database.command("sql", "create document type testBasicCreateProperty").close();
    database.command("sql", "CREATE property testBasicCreateProperty.name STRING").close();

    DocumentType companyClass = database.getSchema().getType("testBasicCreateProperty");
    Property nameProperty = companyClass.getProperty(PROP_NAME);

    Assertions.assertEquals(nameProperty.getName(), PROP_NAME);
    Assertions.assertEquals(nameProperty.getType(), Type.STRING);
  }

  @Test
  public void testCreateMandatoryPropertyWithEmbeddedType() throws Exception {
    database.command("sql", "create document type testCreateMandatoryPropertyWithEmbeddedType").close();
    database.command("sql", "CREATE Property testCreateMandatoryPropertyWithEmbeddedType.officers LIST").close();

    DocumentType companyClass = database.getSchema().getType("testCreateMandatoryPropertyWithEmbeddedType");
    Property nameProperty = companyClass.getProperty(PROP_OFFICERS);

    Assertions.assertEquals(nameProperty.getName(), PROP_OFFICERS);
    Assertions.assertEquals(nameProperty.getType(), Type.LIST);
  }

  @Test
  public void testCreateUnsafePropertyWithEmbeddedType() throws Exception {
    database.command("sql", "create document type testCreateUnsafePropertyWithEmbeddedType").close();
    database.command("sql", "CREATE Property testCreateUnsafePropertyWithEmbeddedType.officers LIST UNSAFE").close();

    DocumentType companyClass = database.getSchema().getType("testCreateUnsafePropertyWithEmbeddedType");
    Property nameProperty = companyClass.getProperty(PROP_OFFICERS);

    Assertions.assertEquals(nameProperty.getName(), PROP_OFFICERS);
    Assertions.assertEquals(nameProperty.getType(), Type.LIST);
  }

  @Test
  public void testExtraSpaces() throws Exception {
    database.command("sql", "create document type testExtraSpaces").close();
    database.command("sql", "CREATE PROPERTY testExtraSpaces.id INTEGER  ").close();

    DocumentType companyClass = database.getSchema().getType("testExtraSpaces");
    Property idProperty = companyClass.getProperty(PROP_ID);

    Assertions.assertEquals(idProperty.getName(), PROP_ID);
    Assertions.assertEquals(idProperty.getType(), Type.INTEGER);
  }

  public void testInvalidAttributeName() throws Exception {
    try {
      database.command("sql", "create document type CommandExecutionException").close();
      database.command("sql", "CREATE PROPERTY CommandExecutionException.id INTEGER (MANDATORY, INVALID, NOTNULL)  UNSAFE").close();
      Assertions.fail("Expected CommandExecutionException");
    } catch (CommandExecutionException e) {
      // OK
    }
  }

  @Test
  public void testMandatoryAsLinkedName() throws Exception {
    database.command("sql", "create document type testMandatoryAsLinkedName").close();
    database.command("sql", "create document type testMandatoryAsLinkedName_2").close();
    database.command("sql", "CREATE PROPERTY testMandatoryAsLinkedName.id LIST UNSAFE").close();

    DocumentType companyClass = database.getSchema().getType("testMandatoryAsLinkedName");
    DocumentType mandatoryClass = database.getSchema().getType("testMandatoryAsLinkedName_2");
    Property idProperty = companyClass.getProperty(PROP_ID);

    Assertions.assertEquals(idProperty.getName(), PROP_ID);
    Assertions.assertEquals(idProperty.getType(), Type.LIST);
  }

  @Test
  public void testIfNotExists() throws Exception {
    database.command("sql", "create document type testIfNotExists").close();
    database.command("sql", "CREATE property testIfNotExists.name if not exists STRING").close();

    DocumentType clazz = database.getSchema().getType("testIfNotExists");
    Property nameProperty = clazz.getProperty(PROP_NAME);

    Assertions.assertEquals(nameProperty.getName(), PROP_NAME);
    Assertions.assertEquals(nameProperty.getType(), Type.STRING);

    database.command("sql", "CREATE property testIfNotExists.name if not exists STRING").close();

    clazz = database.getSchema().getType("testIfNotExists");
    nameProperty = clazz.getProperty(PROP_NAME);

    Assertions.assertEquals(nameProperty.getName(), PROP_NAME);
    Assertions.assertEquals(nameProperty.getType(), Type.STRING);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ReturnStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.SimpleExecStatement;

/**
 * Created by luigidellaquila on 11/10/16.
 */
public class ReturnStep extends AbstractExecutionStep {
  private final SimpleExecStatement statement;
  boolean executed = false;

  public ReturnStep(SimpleExecStatement statement, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.statement = statement;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (executed) {
      return new InternalResultSet();
    }
    executed = true;
    return statement.executeSimple(ctx);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/UpdateContentStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.InputParameter;
import com.arcadedb.query.sql.parser.Json;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 09/08/16.
 */
public class UpdateContentStep extends AbstractExecutionStep {
  private Json           json;
  private InputParameter inputParameter;

  public UpdateContentStep(Json json, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.json = json;
  }

  public UpdateContentStep(InputParameter inputParameter, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.inputParameter = inputParameter;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        Result result = upstream.next();
        if (result instanceof ResultInternal) {
          if (!(result.getElement().get() instanceof Document)) {
            ((ResultInternal) result).setElement((Document) result.getElement().get().getRecord());
          }
          if (!(result.getElement().get() instanceof Document)) {
            return result;
          }
          handleContent(result.getElement().get(), ctx);
        }
        return result;
      }

      @Override
      public void close() {
        upstream.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private boolean handleContent(Document record, CommandContext ctx) {
    // REPLACE ALL THE CONTENT
    final MutableDocument doc = record.modify();

    if (json != null) {
      doc.fromMap(json.toMap(record, ctx));
    } else if (inputParameter != null) {
      Object val = inputParameter.getValue(ctx.getInputParameters());
      if (val instanceof Document) {
        doc.fromMap(((Document) val).getRecord().toJSON().toMap());
      } else if (val instanceof Map) {
        doc.fromMap((Map<String, Object>) val);
      } else {
        throw new CommandExecutionException("Invalid value for UPDATE CONTENT: " + val);
      }
    }

    return true;
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ UPDATE CONTENT\n");
    result.append(spaces);
    result.append("  ");
    if (json != null) {
      result.append(json);
    } else {
      result.append(inputParameter);
    }
    return result.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/DeleteExecutionPlan.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

/**
 * @author <NAME> (luigi.dellaquila-(at)-gmail.com)
 */
public class DeleteExecutionPlan extends UpdateExecutionPlan {

  public DeleteExecutionPlan(CommandContext ctx) {
    super(ctx);
  }

  @Override public Result toResult() {
    ResultInternal res = (ResultInternal) super.toResult();
    res.setProperty("type", "DeleteExecutionPlan");
    return res;
  }

  @Override
  public boolean canBeCached() {
    return false;
  }
}


<|start_filename|>server/src/test/java/com/arcadedb/server/ha/ReplicationServerQuorumMajority1ServerOutIT.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.server.ha;

import com.arcadedb.GlobalConfiguration;
import com.arcadedb.log.LogManager;
import com.arcadedb.server.ArcadeDBServer;
import com.arcadedb.server.TestCallback;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

public class ReplicationServerQuorumMajority1ServerOutIT extends ReplicationServerIT {
  private final AtomicInteger messages = new AtomicInteger();

  public ReplicationServerQuorumMajority1ServerOutIT() {
    GlobalConfiguration.HA_QUORUM.setValue("Majority");
  }

  @Override
  protected void onBeforeStarting(final ArcadeDBServer server) {
    if (server.getServerName().equals("ArcadeDB_2"))
      server.registerTestEventListener(new TestCallback() {
        @Override
        public void onEvent(final TYPE type, final Object object, final ArcadeDBServer server) {
          if (type == TYPE.REPLICA_MSG_RECEIVED) {
            if (messages.incrementAndGet() > 100) {
              LogManager.instance().log(this, Level.INFO, "TEST: Stopping Replica 2...");
              getServer(2).stop();
            }
          }
        }
      });
  }

  protected int[] getServerToCheck() {
    final int[] result = new int[getServerCount() - 1];
    for (int i = 0; i < result.length; ++i)
      result[i] = i;
    return result;
  }

  @Override
  protected int getTxs() {
    return 300;
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ConvertToUpdatableResultStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.Record;
import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * <p>
 * takes a normal result set and transforms it in another result set made of OUpdatableRecord instances.
 * Records that are not identifiable are discarded.
 * </p>
 * <p>This is the opposite of ConvertToResultInternalStep</p>
 *
 * @author <NAME> (<EMAIL>-(at)-<EMAIL>)
 */
public class ConvertToUpdatableResultStep extends AbstractExecutionStep {

  private long cost = 0;

  ResultSet prevResult = null;

  public ConvertToUpdatableResultStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (prev.isEmpty()) {
      throw new IllegalStateException("filter step requires a previous step");
    }
    ExecutionStepInternal prevStep = prev.get();

    return new ResultSet() {
      public boolean finished = false;

      Result nextItem = null;
      int fetched = 0;

      private void fetchNextItem() {
        nextItem = null;
        if (finished) {
          return;
        }
        if (prevResult == null) {
          prevResult = prevStep.syncPull(ctx, nRecords);
          if (!prevResult.hasNext()) {
            finished = true;
            return;
          }
        }
        while (!finished) {
          while (!prevResult.hasNext()) {
            prevResult = prevStep.syncPull(ctx, nRecords);
            if (!prevResult.hasNext()) {
              finished = true;
              return;
            }
          }
          nextItem = prevResult.next();
          long begin = profilingEnabled ? System.nanoTime() : 0;
          try {
            if (nextItem instanceof UpdatableResult) {
              break;
            }
            if (nextItem.isElement()) {
              Record element = nextItem.getElement().get();
              if (element != null) {
                nextItem = new UpdatableResult(((Document) element.getRecord()).modify());
              }
              break;
            }

            nextItem = null;
          } finally {
            cost = (System.nanoTime() - begin);
          }
        }
      }

      @Override
      public boolean hasNext() {
        if (fetched >= nRecords || finished) {
          return false;
        }
        if (nextItem == null) {
          fetchNextItem();
        }

        return nextItem != null;

      }

      @Override
      public Result next() {
        if (fetched >= nRecords || finished) {
          throw new IllegalStateException();
        }
        if (nextItem == null) {
          fetchNextItem();
        }
        if (nextItem == null) {
          throw new IllegalStateException();
        }
        Result result = nextItem;
        nextItem = null;
        fetched++;
        ctx.setVariable("$current", result);
        return result;
      }

      @Override
      public void close() {
        ConvertToUpdatableResultStep.this.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };

  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String result = ExecutionStepInternal.getIndent(depth, indent) + "+ CONVERT TO UPDATABLE ITEM";
    if (profilingEnabled) {
      result += " (" + getCostFormatted() + ")";
    }
    return result;
  }

  @Override
  public long getCost() {
    return cost;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/index/lsm/LSMTreeIndexUnderlyingCompactedSeriesCursor.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.index.lsm;

import com.arcadedb.database.RID;
import com.arcadedb.engine.BasePage;
import com.arcadedb.engine.PageId;
import com.arcadedb.index.IndexException;

import java.io.IOException;

public class LSMTreeIndexUnderlyingCompactedSeriesCursor extends LSMTreeIndexUnderlyingAbstractCursor {
  private final int                              lastPageNumber;
  private       LSMTreeIndexUnderlyingPageCursor pageCursor;

  public LSMTreeIndexUnderlyingCompactedSeriesCursor(final LSMTreeIndexCompacted index, final int firstPageNumber, final int lastPageNumber,
      final byte[] keyTypes, final boolean ascendingOrder, final int posInPage) {
    super(index, keyTypes, keyTypes.length, ascendingOrder);
    this.lastPageNumber = lastPageNumber;

    loadNextNonEmptyPage(firstPageNumber, posInPage);
  }

  @Override
  public boolean hasNext() {
    if (pageCursor == null)
      return false;

    if (pageCursor.hasNext())
      return true;

    final int nextPage = pageCursor.pageId.getPageNumber() + (ascendingOrder ? 1 : -1);

    loadNextNonEmptyPage(nextPage, -1);

    return pageCursor.hasNext();
  }

  private void loadNextNonEmptyPage(final int startingPageNumber, int posInPage) {
    // LOAD NEXT PAGE IF NEEDED
    for (int currentPageNumber = startingPageNumber; ascendingOrder ?
        currentPageNumber <= lastPageNumber :
        currentPageNumber >= lastPageNumber; currentPageNumber += ascendingOrder ? 1 : -1) {
      try {
        final BasePage page = index.getDatabase().getTransaction().getPage(new PageId(index.getFileId(), currentPageNumber), index.getPageSize());
        final int count = index.getCount(page);

        pageCursor = new LSMTreeIndexUnderlyingPageCursor(index, page, posInPage == -1 ? ascendingOrder ? -1 : count : posInPage,
            index.getHeaderSize(currentPageNumber), keyTypes, count, ascendingOrder);

        if (pageCursor.hasNext())
          break;

        // RESET POSITION IN PAGE AFTER THE 1ST PAGE
        posInPage = -1;

      } catch (IOException e) {
        throw new IndexException("Error on iterating cursor on compacted index", e);
      }
    }
  }

  @Override
  public void next() {
    pageCursor.next();
  }

  @Override
  public Object[] getKeys() {
    return pageCursor.getKeys();
  }

  @Override
  public RID[] getValue() {
    return pageCursor.getValue();
  }

  @Override
  public PageId getCurrentPageId() {
    return pageCursor.pageId;
  }

  @Override
  public int getCurrentPositionInPage() {
    return pageCursor.currentEntryIndex;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/CreateDocumentTypeStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Schema;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author <NAME> (l.dellaquila-(at)-orientdb.com)
 */
public class CreateDocumentTypeStatementExecutionTest {
  @Test
  public void testPlain() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      String className = "testPlain";
      ResultSet result = db.command("sql", "create document type " + className);
      Schema schema = db.getSchema();
      DocumentType clazz = schema.getType(className);
      Assertions.assertNotNull(clazz);
      result.close();
    });
  }

  @Test
  public void testClusters() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      String className = "testClusters";
      ResultSet result = db.command("sql", "create document type " + className + " buckets 32");
      Schema schema = db.getSchema();
      DocumentType clazz = schema.getType(className);
      Assertions.assertNotNull(clazz);
      Assertions.assertEquals(32, clazz.getBuckets(false).size());
      result.close();
    });
  }

  @Test
  public void testIfNotExists() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      String className = "testIfNotExists";
      ResultSet result = db.command("sql", "create document type " + className + " if not exists");
      Schema schema = db.getSchema();
      DocumentType clazz = schema.getType(className);
      Assertions.assertNotNull(clazz);
      result.close();

      result = db.command("sql", "create document type " + className + " if not exists");
      clazz = schema.getType(className);
      Assertions.assertNotNull(clazz);
      result.close();
    });
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/AccumulatingTimeoutStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.Timeout;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by luigidellaquila on 08/08/16.
 */
public class AccumulatingTimeoutStep extends AbstractExecutionStep {
  private final Timeout timeout;
  private final long    timeoutMillis;

  private AtomicLong totalTime = new AtomicLong(0);

  public AccumulatingTimeoutStep(Timeout timeout, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.timeout = timeout;
    this.timeoutMillis = this.timeout.getVal().longValue();
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws CommandExecutionException {

    final ResultSet internal = getPrev().get().syncPull(ctx, nRecords);

    if (getPrev().get().isTimedOut())
      fail();

    return new ResultSet() {

      @Override
      public boolean hasNext() {
        if (timedOut || totalTime.get() / 1_000_000 > timeoutMillis) {
          fail();
        }
        long begin = System.nanoTime();

        try {
          return internal.hasNext();
        } finally {
          totalTime.addAndGet(System.nanoTime() - begin);
        }
      }

      @Override
      public Result next() {
        if (totalTime.get() / 1_000_000 > timeoutMillis) {
          fail();
        }
        long begin = System.nanoTime();
        try {
          return internal.next();
        } finally {
          totalTime.addAndGet(System.nanoTime() - begin);
        }
      }

      @Override
      public void close() {
        internal.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return internal.getExecutionPlan();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return internal.getQueryStats();
      }
    };
  }

  private void fail() {
    this.timedOut = true;
    sendTimeout();
    if (!Timeout.RETURN.equals(this.timeout.getFailureStrategy())) {
      throw new TimeoutException("Timeout expired");
    }
  }

  @Override
  public boolean canBeCached() {
    return true;
  }

  @Override
  public ExecutionStep copy(CommandContext ctx) {
    return new AccumulatingTimeoutStep(timeout.copy(), ctx, profilingEnabled);
  }

  @Override
  public void reset() {
    this.totalTime = new AtomicLong(0);
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    return ExecutionStepInternal.getIndent(depth, indent) + "+ TIMEOUT (" + timeout.getVal().toString() + "ms)";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/async/CreateBothVerticesAndEdgeAsyncTask.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database.async;

import com.arcadedb.database.DatabaseInternal;
import com.arcadedb.graph.MutableVertex;

/**
 * Asynchronous Task that creates both vertices and the edge that connects them.
 */
public class CreateBothVerticesAndEdgeAsyncTask extends CreateEdgeAsyncTask {
  private final String   sourceVertexType;
  private final String[] sourceVertexAttributeNames;
  private final Object[] sourceVertexAttributeValues;

  private final String   destinationVertexType;
  private final String[] destinationVertexAttributeNames;
  private final Object[] destinationVertexAttributeValues;

  public CreateBothVerticesAndEdgeAsyncTask(final String sourceVertexType, final String[] sourceVertexAttributeNames,
      final Object[] sourceVertexAttributeValues, final String destinationVertexType, final String[] destinationVertexAttributeNames,
      final Object[] destinationVertexAttributeValues, final String edgeType, final Object[] edgeAttributes, final boolean bidirectional, final boolean light,
      final NewEdgeCallback callback) {
    super(null, null, edgeType, edgeAttributes, bidirectional, light, callback);

    this.sourceVertexType = sourceVertexType;
    this.sourceVertexAttributeNames = sourceVertexAttributeNames;
    this.sourceVertexAttributeValues = sourceVertexAttributeValues;

    this.destinationVertexType = destinationVertexType;
    this.destinationVertexAttributeNames = destinationVertexAttributeNames;
    this.destinationVertexAttributeValues = destinationVertexAttributeValues;
  }

  public void execute(final DatabaseInternal database) {
    final MutableVertex sourceVertex = database.newVertex(sourceVertexType);
    for (int i = 0; i < sourceVertexAttributeNames.length; ++i)
      sourceVertex.set(sourceVertexAttributeNames[i], sourceVertexAttributeValues[i]);
    sourceVertex.save();

    final MutableVertex destinationVertex = database.newVertex(destinationVertexType);
    for (int i = 0; i < destinationVertexAttributeNames.length; ++i)
      destinationVertex.set(destinationVertexAttributeNames[i], destinationVertexAttributeValues[i]);
    destinationVertex.save();

    createEdge(database, sourceVertex, destinationVertex, true, true);
  }

  @Override
  public String toString() {
    return "CreateBothVerticesAndEdgeAsyncTask(" + sourceVertexType + "->" + destinationVertexType + ")";
  }
}


<|start_filename|>server/src/test/java/com/arcadedb/server/security/ServerProfilingIT.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.server.security;

import com.arcadedb.GlobalConfiguration;
import com.arcadedb.database.DatabaseContext;
import com.arcadedb.database.DatabaseInternal;
import com.arcadedb.database.Document;
import com.arcadedb.database.RID;
import com.arcadedb.database.Record;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.graph.MutableVertex;
import com.arcadedb.graph.Vertex;
import com.arcadedb.index.lsm.LSMTreeIndexAbstract;
import com.arcadedb.query.sql.executor.ResultSet;
import com.arcadedb.schema.Schema;
import com.arcadedb.security.SecurityDatabaseUser;
import com.arcadedb.server.ArcadeDBServer;
import com.arcadedb.utility.CallableNoReturn;
import com.arcadedb.utility.FileUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.*;

public class ServerProfilingIT {
  private static       ArcadeDBServer SERVER;
  private static       ServerSecurity SECURITY;
  private final static String         DATABASE_NAME = "ServerProfilingIT";

  @Test
  void userDefaultAccessCannotAccessDatabase() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>")));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      final RID validRID = createSomeRecords(database, true);

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());
      expectedSecurityException(() -> database.iterateType("Document1", true));
      expectedSecurityException(() -> database.lookupByRID(validRID, true));

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void notRootAdminAccess() {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "admin" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchema(database);

      final RID validRID = createSomeRecords(database, true);

      database.newVertex("Vertex1").save();
      database.newDocument("Document1").save();
      database.iterateType("Document1", true);
      database.lookupByRID(validRID, true);

      dropSchema(database);
    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void testMultipleGroupsAnyType() {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "creator", "reader", "updater", "deleter" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      setCurrentUser("root", database);
      createSchema(database);

      checkElonUser(setCurrentUser("elon", database));

      final RID validRID = createSomeRecords(database, true);

      database.newVertex("Vertex1").save();
      database.newDocument("Document1").save();
      database.iterateType("Document1", true);
      database.lookupByRID(validRID, true);

      setCurrentUser("root", database);
      dropSchema(database);
    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void testMultipleGroupsSpecificType() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>")).put("databases",
        new JSONObject().put(DATABASE_NAME,
            new JSONArray(new String[] { "creatorOfDocuments", "readerOfDocuments", "updaterOfDocuments", "deleterOfDocuments" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      setCurrentUser("root", database);
      createSchema(database);

      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      database.newDocument("Document1").save();
      database.iterateType("Document1", true);
      database.transaction(() -> {
        database.iterateType("Document1", true).next().asDocument().modify().set("modified", true).save();
      });
      database.transaction(() -> {
        database.iterateType("Document1", true).next().asDocument().delete();
      });

      setCurrentUser("root", database);
      dropSchema(database);
    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void createOnlyAccess() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "creator" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      final RID validRID = createSomeRecords(database, false);

      database.newVertex("Vertex1").save();
      database.newDocument("Document1").save();
      expectedSecurityException(() -> database.iterateType("Document1", true));
      expectedSecurityException(() -> database.lookupByRID(validRID, true));

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void readOnlyAccess() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "reader" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      final RID validRID = createSomeRecords(database, true);

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());
      database.iterateType("Document1", true);
      database.lookupByRID(validRID, true);

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void updateOnlyAccess() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "updater" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      final RID validRID = createSomeRecords(database, true);

      Vertex v = validRID.getRecord(true).asVertex();

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());
      expectedSecurityException(() -> database.iterateType("Document1", true));
      expectedSecurityException(() -> database.lookupByRID(validRID, true));

      database.transaction(() -> {
        v.modify().set("justModified", true).save();
      });

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void deleteOnlyAccess() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "deleter" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      createSomeRecords(database, true);
      Document doc = database.iterateType("Document1", true).next().asDocument();

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());
      expectedSecurityException(() -> database.iterateType("Document1", true));
      expectedSecurityException(() -> database.lookupByRID(doc.getIdentity(), true));

      database.transaction(() -> {
        database.deleteRecord(doc);
      });

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void testResultSetLimit() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "readerOfDocumentsCapped" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      createSomeRecords(database, true);
      for (int i = 0; i < 14; i++) {
        createSomeRecords(database, true);
      }

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());

      int count = 0;
      for (final Iterator<Record> iter = database.iterateType("Document1", true); iter.hasNext(); ) {
        iter.next();
        ++count;
      }

      Assertions.assertEquals(10, count);

      count = 0;
      for (final ResultSet iter = database.query("sql", "select from Document1"); iter.hasNext(); ) {
        iter.next();
        ++count;
      }

      Assertions.assertEquals(10, count);

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void testReadTimeout() throws Throwable {
    SECURITY.createUser(new JSONObject().put("name", "elon").put("password", SECURITY.encodePassword("<PASSWORD>"))
        .put("databases", new JSONObject().put(DATABASE_NAME, new JSONArray(new String[] { "readerOfDocumentsShortTimeout" }))));

    try {
      DatabaseInternal database = (DatabaseInternal) SERVER.getDatabase(DATABASE_NAME);

      checkElonUser(setCurrentUser("elon", database));

      createSchemaNotAllowed(database);

      // SWITCH TO ROOT TO CREATE SOME TYPES FOR FURTHER TESTS
      setCurrentUser("root", database);

      createSchema(database);

      database.transaction(() -> {
        for (int i = 0; i < 10000; i++) {
          database.newDocument("Document1").save();
        }
      });

      // SWITCH BACK TO ELON
      checkElonUser(setCurrentUser("elon", database));

      expectedSecurityException(() -> database.newVertex("Vertex1").save());
      expectedSecurityException(() -> database.newDocument("Document1").save());

      try {
        for (final Iterator<Record> iter = database.iterateType("Document1", true); iter.hasNext(); ) {
          iter.next();
        }
        Assertions.fail();
      } catch (TimeoutException e) {
        // EXPECTED
      }

      try {
        for (final ResultSet iter = database.query("sql", "select from Document1"); iter.hasNext(); ) {
          iter.next();
        }
        Assertions.fail();
      } catch (TimeoutException e) {
        // EXPECTED
      }

      // SWITCH TO ROOT TO DROP THE SCHEMA
      setCurrentUser("root", database);
      dropSchema(database);

    } finally {
      SECURITY.dropUser("elon");
    }
  }

  @Test
  void testGroupsReload() throws Throwable {
    final File file = new File("./target/config/server-groups.json");
    Assertions.assertTrue(file.exists());

    final JSONObject json = new JSONObject(FileUtils.readFileAsString(file, "UTF8"));

    final byte[] original = json.toString(2).getBytes();

    json.getJSONObject("databases").getJSONObject("*").getJSONObject("groups").put("reloaded", true);

    try {
      FileUtils.writeContentToStream(file, json.toString(2).getBytes());

      Thread.sleep(6_000);

      Assertions.assertTrue(SECURITY.getDatabaseGroupsConfiguration("*").getBoolean("reloaded"));
    } finally {
      // RESTORE THE ORIGINAL FILE AND WAIT FOR TO RELOAD
      FileUtils.writeContentToStream(file, original);
      Thread.sleep(6_000);
      createSecurity();
    }
  }

  private void createSchemaNotAllowed(DatabaseInternal database) throws Throwable {
    expectedSecurityException(() -> database.getSchema().createBucket("Bucket1"));
    expectedSecurityException(() -> database.getSchema().createVertexType("Vertex1"));
    expectedSecurityException(() -> database.getSchema().createEdgeType("Edge1"));
    expectedSecurityException(() -> database.getSchema().createDocumentType("Document1"));

    expectedSecurityException(() -> database.getSchema()
        .createBucketIndex(Schema.INDEX_TYPE.LSM_TREE, true, "Document1", "Bucket1", new String[] { "id" }, 10000, LSMTreeIndexAbstract.NULL_STRATEGY.ERROR,
            null));
    expectedSecurityException(() -> database.getSchema().createTypeIndex(Schema.INDEX_TYPE.LSM_TREE, true, "Document1", "id"));

    expectedSecurityException(() -> database.getSchema().dropType("Document1"));
    expectedSecurityException(() -> database.getSchema().dropIndex("Idx1"));
    expectedSecurityException(() -> database.getSchema().dropBucket("Bucket1"));
  }

  private void expectedSecurityException(CallableNoReturn callback) throws Throwable {
    try {
      callback.call();
      Assertions.fail();
    } catch (SecurityException e) {
      // EXPECTED
    }
  }

  private void checkElonUser(ServerSecurityUser elon) {
    Assertions.assertNotNull(elon);
    ServerSecurityUser authElon = SECURITY.authenticate("elon", "musk", null);
    Assertions.assertNotNull(authElon);
    Assertions.assertEquals(elon.getName(), authElon.getName());

    SecurityUserFileRepository repository = new SecurityUserFileRepository("./target/config");
    Assertions.assertEquals(2, repository.getUsers().size());
    Assertions.assertEquals("elon", repository.getUsers().get(1).getString("name"));
  }

  private ServerSecurityUser setCurrentUser(final String userName, DatabaseInternal database) {
    final ServerSecurityUser user = SECURITY.getUser(userName);
    SecurityDatabaseUser dbUser = user.getDatabaseUser(database);
    DatabaseContext.INSTANCE.init(database).setCurrentUser(dbUser);
    Assertions.assertEquals(dbUser, DatabaseContext.INSTANCE.getContext(database.getDatabasePath()).getCurrentUser());
    return user;
  }

  private RID createSomeRecords(DatabaseInternal database, boolean createEdge) {
    final AtomicReference<RID> validRID = new AtomicReference<>();
    database.transaction(() -> {
      MutableVertex v1 = database.newVertex("Vertex1").save();
      MutableVertex v2 = database.newVertex("Vertex1").save();
      if (createEdge)
        v1.newEdge("Edge1", v2, true);
      database.newDocument("Document1").save();

      validRID.set(v1.getIdentity());
    });
    return validRID.get();
  }

  private void createSchema(DatabaseInternal database) {
    database.getSchema().createBucket("Bucket1");
    database.getSchema().createVertexType("Vertex1");
    database.getSchema().createEdgeType("Edge1");
    database.getSchema().createDocumentType("Document1");
  }

  private void dropSchema(DatabaseInternal database) {
    database.getSchema().dropBucket("Bucket1");
    database.getSchema().dropType("Vertex1");
    database.getSchema().dropType("Edge1");
    database.getSchema().dropType("Document1");
  }

  @BeforeAll
  public static void beforeAll() {
    FileUtils.deleteRecursively(new File("./target/config"));
    FileUtils.deleteRecursively(new File("./target/databases"));
    GlobalConfiguration.SERVER_ROOT_PASSWORD.setValue("<PASSWORD>");
    GlobalConfiguration.SERVER_DATABASE_DIRECTORY.setValue("./target/databases");
    GlobalConfiguration.SERVER_ROOT_PATH.setValue("./target");

    SERVER = new ArcadeDBServer();
    SERVER.start();
    createSecurity();

    SERVER.getOrCreateDatabase(DATABASE_NAME);
  }

  private static void createSecurity() {
    SECURITY = SERVER.getSecurity();
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("reader",//
        new JSONObject().put("types", new JSONObject().put("*", new JSONObject().put("access", new JSONArray(new String[] { "readRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("creator",
        new JSONObject().put("types", new JSONObject().put("*", new JSONObject().put("access", new JSONArray(new String[] { "createRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("updater",
        new JSONObject().put("types", new JSONObject().put("*", new JSONObject().put("access", new JSONArray(new String[] { "updateRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("deleter",
        new JSONObject().put("types", new JSONObject().put("*", new JSONObject().put("access", new JSONArray(new String[] { "deleteRecord" })))));

    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("readerOfDocuments",//
        new JSONObject().put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "readRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("creatorOfDocuments",
        new JSONObject().put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "createRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("updaterOfDocuments",
        new JSONObject().put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "updateRecord" })))));
    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("deleterOfDocuments",
        new JSONObject().put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "deleteRecord" })))));

    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("readerOfDocumentsCapped",//
        new JSONObject().put("resultSetLimit", 10)//
            .put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "readRecord" })))));

    SECURITY.getDatabaseGroupsConfiguration(DATABASE_NAME).put("readerOfDocumentsShortTimeout",//
        new JSONObject().put("readTimeout", 1)
            .put("types", new JSONObject().put("Document1", new JSONObject().put("access", new JSONArray(new String[] { "readRecord" })))));
    SECURITY.saveGroups();
  }

  @AfterAll
  public static void afterAll() {
    SERVER.stop();
    GlobalConfiguration.SERVER_ROOT_PASSWORD.setValue(null);

    FileUtils.deleteRecursively(new File("./target/config"));
    FileUtils.deleteRecursively(new File("./target/databases"));
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/CartesianProductStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.LocalResultSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 11/10/16.
 */
public class CartesianProductStep extends AbstractExecutionStep {

  private final List<InternalExecutionPlan> subPlans = new ArrayList<>();

  private       boolean                 inited            = false;
  private final List<Boolean>           completedPrefetch = new ArrayList<>();
  private final List<InternalResultSet> preFetches        = new ArrayList<>();//consider using resultset.reset() instead of buffering

  private final List<ResultSet> resultSets   = new ArrayList<>();
  private       List<Result>    currentTuple = new ArrayList<>();

  ResultInternal nextRecord;

  private long cost = 0;

  public CartesianProductStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    getPrev().ifPresent(x -> x.syncPull(ctx, nRecords));
    init();
    //    return new OInternalResultSet();
    return new ResultSet() {
      int currentCount = 0;

      @Override
      public boolean hasNext() {
        if (currentCount >= nRecords) {
          return false;
        }
        return nextRecord != null;
      }

      @Override
      public Result next() {
        if (currentCount >= nRecords || nextRecord == null) {
          throw new IllegalStateException();
        }
        ResultInternal result = nextRecord;
        fetchNextRecord();
        currentCount++;
        return result;
      }

      @Override
      public void close() {
        // EMPTY METHOD
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
    //    throw new UnsupportedOperationException("cartesian product is not yet implemented in MATCH statement");
    //TODO
  }

  private void init() {
    if (subPlans.isEmpty()) {
      return;
    }
    if (inited) {
      return;
    }

    for (InternalExecutionPlan plan : subPlans) {
      resultSets.add(new LocalResultSet(plan));
      this.preFetches.add(new InternalResultSet());
    }
    fetchFirstRecord();
    inited = true;
  }

  private void fetchFirstRecord() {
    for (ResultSet rs : resultSets) {
      if (!rs.hasNext()) {
        nextRecord = null;
        return;
      }
      Result item = rs.next();
      currentTuple.add(item);
      completedPrefetch.add(false);
    }
    buildNextRecord();
  }

  private void fetchNextRecord() {
    fetchNextRecord(resultSets.size() - 1);
  }

  private void fetchNextRecord(int level) {
    ResultSet currentRs = resultSets.get(level);
    if (!currentRs.hasNext()) {
      if (level <= 0) {
        nextRecord = null;
        currentTuple = null;
        return;
      }
      currentRs = preFetches.get(level);
      currentRs.reset();
      resultSets.set(level, currentRs);
      currentTuple.set(level, currentRs.next());
      fetchNextRecord(level - 1);
    } else {
      currentTuple.set(level, currentRs.next());
    }
    buildNextRecord();
  }

  private void buildNextRecord() {
    long begin = profilingEnabled ? System.nanoTime() : 0;
    try {
      if (currentTuple == null) {
        nextRecord = null;
        return;
      }
      nextRecord = new ResultInternal();

      for (int i = 0; i < this.currentTuple.size(); i++) {
        Result res = this.currentTuple.get(i);
        for (String s : res.getPropertyNames()) {
          nextRecord.setProperty(s, res.getProperty(s));
        }
        if (!completedPrefetch.get(i)) {
          preFetches.get(i).add(res);
          if (!resultSets.get(i).hasNext()) {
            completedPrefetch.set(i, true);
          }
        }
      }
    } finally {
      if (profilingEnabled) {
        cost += (System.nanoTime() - begin);
      }
    }
  }

  public void addSubPlan(InternalExecutionPlan subPlan) {
    this.subPlans.add(subPlan);
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String result = "";
    String ind = ExecutionStepInternal.getIndent(depth, indent);

    int[] blockSizes = new int[subPlans.size()];

    for (int i = 0; i < subPlans.size(); i++) {
      InternalExecutionPlan currentPlan = subPlans.get(subPlans.size() - 1 - i);
      String partial = currentPlan.prettyPrint(0, indent);

      String[] partials = partial.split("\n");
      blockSizes[subPlans.size() - 1 - i] = partials.length + 2;
      result = "+-------------------------\n" + result;
      for (int j = 0; j < partials.length; j++) {
        String p = partials[partials.length - 1 - j];
        if (result.length() > 0) {
          result = appendPipe(p) + "\n" + result;
        } else {
          result = appendPipe(p);
        }
      }
      result = "+-------------------------\n" + result;
    }
    result = addArrows(result, blockSizes);
    result += foot(blockSizes);
    result = ind + result;
    result = result.replaceAll("\n", "\n" + ind);
    result = head(depth, indent) + "\n" + result;
    return result;
  }

  private String addArrows(String input, int[] blockSizes) {
    String result = "";
    String[] rows = input.split("\n");
    int rowNum = 0;
    for (int block = 0; block < blockSizes.length; block++) {
      int blockSize = blockSizes[block];
      for (int subRow = 0; subRow < blockSize; subRow++) {
        for (int col = 0; col < blockSizes.length * 3; col++) {
          if (isHorizontalRow(col, subRow, block, blockSize)) {
            result += "-";
          } else if (isPlus(col, subRow, block, blockSize)) {
            result += "+";
          } else if (isVerticalRow(col, subRow, block, blockSize)) {
            result += "|";
          } else {
            result += " ";
          }
        }
        result += rows[rowNum] + "\n";
        rowNum++;
      }
    }

    return result;
  }

  private boolean isHorizontalRow(int col, int subRow, int block, int blockSize) {
    if (col < block * 3 + 2) {
      return false;
    }
    return subRow == blockSize / 2;
  }

  private boolean isPlus(int col, int subRow, int block, int blockSize) {
    if (col == block * 3 + 1) {
      return subRow == blockSize / 2;
    }
    return false;
  }

  private boolean isVerticalRow(int col, int subRow, int block, int blockSize) {
    if (col == block * 3 + 1) {
      return subRow > blockSize / 2;
    } else
      return col < block * 3 + 1 && col % 3 == 1;

  }

  private String head(int depth, int indent) {
    String ind = ExecutionStepInternal.getIndent(depth, indent);
    String result = ind + "+ CARTESIAN PRODUCT";
    if (profilingEnabled) {
      result += " (" + getCostFormatted() + ")";
    }
    return result;
  }

  private String foot(int[] blockSizes) {
    String result = "";
    for (int i = 0; i < blockSizes.length; i++) {
      result += " V ";//TODO
    }
    return result;
  }

  private String appendPipe(String p) {
    return "| " + p;
  }

  @Override
  public long getCost() {
    return cost;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/parser/DropPropertyStatementTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.parser;

import org.junit.jupiter.api.Test;

public class DropPropertyStatementTest extends ParserTestAbstract {

  @Test
  public void testPlain() {
    checkRightSyntax("DROP PROPERTY Foo.bar");
    checkRightSyntax("drop property Foo.bar");
    checkRightSyntax("drop property `Foo bar`.`bar baz`");
    checkRightSyntax("drop property Foo.bar force");
    checkRightSyntax("drop property Foo.bar FORCE");

    checkWrongSyntax("drop property foo");
    checkWrongSyntax("drop property foo.bar.baz");
    checkWrongSyntax("drop property foo.bar foo");
  }

  @Test
  public void testIfExists() {
    checkRightSyntax("DROP PROPERTY Foo.bar if exists");
    checkRightSyntax("DROP PROPERTY Foo.bar IF EXISTS");
    checkRightSyntax("DROP PROPERTY Foo.bar if exists force");

    checkWrongSyntax("DROP PROPERTY Foo.bar if");
    checkWrongSyntax("DROP PROPERTY Foo.bar if force");
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/Record.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database;

import org.json.JSONObject;

/**
 * Generic interface representing a record as an entry in the database.
 *
 * @author <NAME>
 */
public interface Record extends Identifiable {
  RID getIdentity();

  byte getRecordType();

  Database getDatabase();

  void reload();

  void delete();

  JSONObject toJSON();
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/InsertExecutionPlan.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

/**
 * Created by luigidellaquila on 08/08/16.
 */

import com.arcadedb.exception.CommandExecutionException;

import java.util.ArrayList;
import java.util.List;

/**
 * @author <NAME> (<EMAIL>)
 */
public class InsertExecutionPlan extends SelectExecutionPlan {

  final List<Result> result = new ArrayList<>();
  int          next   = 0;

  public InsertExecutionPlan(CommandContext ctx) {
    super(ctx);
  }

  @Override
  public ResultSet fetchNext(int n) {
    if (next >= result.size()) {
      return new InternalResultSet();//empty
    }

    IteratorResultSet nextBlock = new IteratorResultSet(result.subList(next, Math.min(next + n, result.size())).iterator());
    next += n;
    return nextBlock;
  }

  @Override
  public void reset(CommandContext ctx) {
    result.clear();
    next = 0;
    super.reset(ctx);
    executeInternal();
  }

  public void executeInternal() throws CommandExecutionException {
    while (true) {
      ResultSet nextBlock = super.fetchNext(100);
      if (!nextBlock.hasNext()) {
        return;
      }
      while (nextBlock.hasNext()) {
        result.add(nextBlock.next());
      }
    }
  }

  @Override
  public Result toResult() {
    ResultInternal res = (ResultInternal) super.toResult();
    res.setProperty("type", "InsertExecutionPlan");
    return res;
  }

  @Override
  public boolean canBeCached() {
    return false;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/misc/SQLFunctionIfNull.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.function.SQLFunctionAbstract;

/**
 * Returns the passed {@literal field/value} (or optional parameter {@literal return_value_if_not_null}) if
 * {@literal field/value} is <b>not</b> null; otherwise it returns {@literal return_value_if_null}.
 *
 * <br>
 * Syntax: <blockquote>
 *
 * <pre>
 * ifnull(&lt;field|value&gt;, &lt;return_value_if_null&gt; [,&lt;return_value_if_not_null&gt;])
 * </pre>
 *
 * </blockquote>
 *
 * <br>
 * Examples: <blockquote>
 *
 * <pre>
 * SELECT <b>ifnull('a', 'b')</b> FROM ...
 *  -&gt; 'a'
 *
 * SELECT <b>ifnull('a', 'b', 'c')</b> FROM ...
 *  -&gt; 'c'
 *
 * SELECT <b>ifnull(null, 'b')</b> FROM ...
 *  -&gt; 'b'
 *
 * SELECT <b>ifnull(null, 'b', 'c')</b> FROM ...
 *  -&gt; 'b'
 * </pre>
 *
 * </blockquote>
 *
 * @author <NAME>
 */

public class SQLFunctionIfNull extends SQLFunctionAbstract {

  public static final String NAME = "ifnull";

  public SQLFunctionIfNull() {
    super(NAME);
  }

  @Override
  public Object execute( Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, final Object[] iParams, final CommandContext iContext) {
    /*
     * iFuncParams [0] field/value to check for null [1] return value if [0] is null [2] optional return value if [0] is not null
     */
    if (iParams[0] != null) {
      if (iParams.length == 3) {
        return iParams[2];
      }
      return iParams[0];
    }
    return iParams[1];
  }

  @Override
  public String getSyntax() {
    return "Syntax error: ifnull(<field|value>, <return_value_if_null> [,<return_value_if_not_null>])";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/log/Logger.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.log;

import java.util.logging.Level;

/**
 * Logger interface that avoids using varargs to remove garbage on the GC
 */
public interface Logger {
  void log(Object iRequester, Level iLevel, String iMessage, Throwable iException, String context, Object arg1, Object arg2, Object arg3, Object arg4,
      Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, Object arg15,
      Object arg16, Object arg17);

  void log(Object iRequester, Level iLevel, String iMessage, Throwable iException, String context, Object... args);

  void flush();
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/DeleteStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * Deletes records coming from upstream steps
 *
 * @author <NAME> (<EMAIL>)
 */
public class DeleteStep extends AbstractExecutionStep {
  private long cost = 0;

  public DeleteStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        Result result = upstream.next();
        long begin = profilingEnabled ? System.nanoTime() : 0;
        try {
          if (result.isElement()) {
            ctx.getDatabase().deleteRecord(result.getElement().get());
          }
          return result;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }

      @Override
      public void close() {
        upstream.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ DELETE");
    if (profilingEnabled) {
      result.append(" (").append(getCostFormatted()).append(")");
    }
    return result.toString();
  }

  @Override
  public long getCost() {
    return cost;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/method/misc/SQLMethodSize.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.method.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.MultiValue;

/**
 * @author <NAME> (Geomatys)
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLMethodSize extends AbstractSQLMethod {

  public static final String NAME = "size";

  public SQLMethodSize() {
    super(NAME);
  }

  @Override
  public Object execute( Object iThis, final Identifiable iCurrentRecord, final CommandContext iContext, final Object ioResult,
      final Object[] iParams) {

    final Number size;
    if (ioResult != null) {
      if (ioResult instanceof Identifiable) {
        size = 1;
      } else {
        size = MultiValue.getSize(ioResult);
      }
    } else {
      size = 0;
    }

    return size;
  }
}


<|start_filename|>network/src/main/java/com/arcadedb/network/binary/ChannelDataOutput.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.network.binary;

import com.arcadedb.database.RID;

import java.io.IOException;
import java.io.OutputStream;

public interface ChannelDataOutput {

  ChannelDataOutput writeByte(final byte iContent) throws IOException;

  ChannelDataOutput writeBoolean(final boolean iContent) throws IOException;

  ChannelDataOutput writeInt(final int iContent) throws IOException;

  ChannelDataOutput writeLong(final long iContent) throws IOException;

  ChannelDataOutput writeShort(final short iContent) throws IOException;

  ChannelDataOutput writeString(final String iContent) throws IOException;

  ChannelDataOutput writeVarLengthBytes(final byte[] iContent) throws IOException;

  ChannelDataOutput writeVarLengthBytes(final byte[] iContent, final int iLength) throws IOException;

  void writeRID(final RID iRID) throws IOException;

  void writeVersion(final int version) throws IOException;

  OutputStream getDataOutput();

}


<|start_filename|>engine/src/main/java/com/arcadedb/engine/ImmutablePage.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.engine;

/**
 * Low level immutable (read-only) page implementation of 65536 bytes (2 exp 16 = 65Kb). The first 8 bytes (the header) are reserved
 * to store the page version (MVCC). The maximum content is 65528.
 */
public class ImmutablePage extends BasePage {
  public ImmutablePage(final PageManager manager, final PageId pageId, final int size) {
    this(manager, pageId, size, new byte[size], 0, 0);
  }

  public ImmutablePage(final PageManager manager, final PageId pageId, final int size, final byte[] content, final int version, final int contentSize) {
    super(manager, pageId, size, content, version, contentSize);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/coll/SQLFunctionIntersect.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.coll;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.MultiValue;

import java.util.*;

/**
 * This operator can work as aggregate or inline. If only one argument is passed than aggregates, otherwise executes, and returns,
 * the INTERSECTION of the collections received as parameters.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLFunctionIntersect extends SQLFunctionMultiValueAbstract<Object> {
  public static final String NAME = "intersect";

  public SQLFunctionIntersect() {
    super(NAME, 1, -1);
  }

  public Object execute( Object iThis, final Identifiable iCurrentRecord, Object iCurrentResult, final Object[] iParams,
      CommandContext iContext) {
    Object value = iParams[0];

    if (value == null)
      return Collections.emptySet();

    if (iParams.length == 1) {
      // AGGREGATION MODE (STATEFUL)
      if (context == null) {
        // ADD ALL THE ITEMS OF THE FIRST COLLECTION
        if (value instanceof Collection) {
          context = ((Collection) value).iterator();
        } else if (value instanceof Iterator) {
          context = value;
        } else if (value instanceof Iterable) {
          context = ((Iterable) value).iterator();
        } else {
          context = Arrays.asList(value).iterator();
        }
      } else {
        Iterator contextIterator = null;
        if (context instanceof Iterator) {
          contextIterator = (Iterator) context;
        } else if (MultiValue.isMultiValue(context)) {
          contextIterator = MultiValue.getMultiValueIterator(context);
        }
        context = intersectWith(contextIterator, value);
      }
      return null;
    }

    // IN-LINE MODE (STATELESS)
    Iterator iterator = MultiValue.getMultiValueIterator(value, false);

    for (int i = 1; i < iParams.length; ++i) {
      value = iParams[i];

      if (value != null) {
        value = intersectWith(iterator, value);
        iterator = MultiValue.getMultiValueIterator(value, false);
      } else {
        return Collections.emptyIterator();
      }
    }

    List result = new ArrayList();
    while (iterator.hasNext()) {
      result.add(iterator.next());
    }
    return result;
  }

  @Override
  public Object getResult() {
    return MultiValue.toSet(context);
  }

  static Collection intersectWith(final Iterator current, Object value) {
    final HashSet tempSet = new HashSet();

    if (!(value instanceof Set))
      value = MultiValue.toSet(value);

    for (Iterator it = current; it.hasNext(); ) {
      final Object curr = it.next();

      if (value instanceof Collection) {
        if (((Collection) value).contains(curr))
          tempSet.add(curr);
      }
    }

    return tempSet;
  }

  public String getSyntax() {
    return "intersect(<field>*)";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/async/DatabaseAsyncCompletion.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database.async;

import com.arcadedb.database.DatabaseInternal;

public class DatabaseAsyncCompletion extends DatabaseAsyncAbstractCallbackTask {
  public DatabaseAsyncCompletion() {
  }

  @Override
  public void execute(final DatabaseAsyncExecutorImpl.AsyncThread async, final DatabaseInternal database) {
    try {
      if (database.isTransactionActive())
        database.commit();
      async.onOk();
    } catch (Exception e) {
      async.onError(e);
    }
  }

  @Override
  public boolean requiresActiveTx() {
    return false;
  }

  @Override
  public String toString() {
    return "Completion";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/index/IndexCursorEntry.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.index;

import com.arcadedb.database.Identifiable;

import java.util.Arrays;
import java.util.Objects;

public class IndexCursorEntry {
  public final Object[]     keys;
  public final Identifiable record;
  public final int          score;

  public IndexCursorEntry(final Object[] keys, final Identifiable record, final int score) {
    this.keys = keys;
    this.record = record;
    this.score = score;
  }

  @Override
  public boolean equals(final Object o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;
    final IndexCursorEntry that = (IndexCursorEntry) o;
    return score == that.score && Objects.equals(record, that.record) && Arrays.equals(keys, that.keys);
  }

  @Override
  public int hashCode() {
    int result = Objects.hash(record, score);
    result = 31 * result + Arrays.hashCode(keys);
    return result;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/SubQueryStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

/**
 * Created by luigidellaquila on 22/07/16.
 */
public class SubQueryStep extends AbstractExecutionStep {
  private final InternalExecutionPlan subExecuitonPlan;

  /**
   * executes a sub-query
   *
   * @param subExecutionPlan the execution plan of the sub-query
   * @param ctx              the context of the current execution plan
   * @param subCtx           the context of the subquery execution plan
   */
  public SubQueryStep(InternalExecutionPlan subExecutionPlan, CommandContext ctx, CommandContext subCtx,
      boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.subExecuitonPlan = subExecutionPlan;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    getPrev().ifPresent(x -> x.syncPull(ctx, nRecords));
    return subExecuitonPlan.fetchNext(nRecords);
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    StringBuilder builder = new StringBuilder();
    String ind = ExecutionStepInternal.getIndent(depth, indent);
    builder.append(ind);
    builder.append("+ FETCH FROM SUBQUERY \n");
    builder.append(subExecuitonPlan.prettyPrint(depth + 1, indent));
    return builder.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/index/TempIndexCursor.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.index;

import com.arcadedb.database.Identifiable;
import com.arcadedb.serializer.BinaryComparator;

import java.util.Collection;
import java.util.Iterator;

public class TempIndexCursor implements IndexCursor {
  private final Iterator<IndexCursorEntry> iterator;
  private final long                       size;
  private       IndexCursorEntry           current;

  public TempIndexCursor(final Collection<IndexCursorEntry> list) {
    this.iterator = list.iterator();
    this.size = list.size();
  }

  @Override
  public Object[] getKeys() {
    return current.keys;
  }

  @Override
  public Identifiable getRecord() {
    return current.record;
  }

  @Override
  public boolean hasNext() {
    return iterator.hasNext();
  }

  @Override
  public Identifiable next() {
    current = iterator.next();
    return current.record;
  }

  @Override
  public int getScore() {
    return current.score;
  }

  @Override
  public void close() {
  }

  @Override
  public String dumpStats() {
    return "no-stats";
  }

  @Override
  public BinaryComparator getComparator() {
    return null;
  }

  @Override
  public byte[] getKeyTypes() {
    return new byte[0];
  }

  @Override
  public long size() {
    return size;
  }

  @Override
  public Iterator<Identifiable> iterator() {
    return this;
  }
}


<|start_filename|>integration/src/main/java/com/arcadedb/integration/importer/AnalyzedProperty.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.integration.importer;

import com.arcadedb.schema.Type;

import java.util.HashSet;
import java.util.Set;

public class AnalyzedProperty {
  private final String      name;
  private final long        maxValueSampling;
  private       Type        type;
  private final int         index;
  private       String      lastContent;
  private final Set<String> contents            = new HashSet<>();
  private       boolean     candidateForInteger = true;
  private       boolean     candidateForDecimal = true;
  private       boolean     collectingSamples   = true;

  public AnalyzedProperty(final String name, final Type type, final long maxValueSampling, final int index) {
    this.name = name;
    this.type = type;
    this.maxValueSampling = maxValueSampling;
    this.index = index;
  }

  public String getName() {
    return name;
  }

  public Type getType() {
    return type;
  }

  public void endParsing() {
    if (lastContent != null)
      if (candidateForInteger)
        type = Type.LONG;
      else if (candidateForDecimal)
        type = Type.DOUBLE;
  }

  public int getIndex() {
    return index;
  }

  public void setLastContent(String lastContent) {
    if (!collectingSamples)
      return;

    if (lastContent != null) {
      if (lastContent.length() > 100) {
        collectingSamples = false;
        contents.clear();
        return;
      }

      this.lastContent = lastContent;
      if (contents.size() > maxValueSampling) {
        collectingSamples = false;
        contents.clear();
        return;
      }

      contents.add(lastContent);

      if (!lastContent.isEmpty()) {
        if (candidateForInteger) {
          try {
            Long.parseLong(lastContent);
          } catch (NumberFormatException e) {
            candidateForInteger = false;
          }
        }

        if (candidateForDecimal) {
          try {
            Double.parseDouble(lastContent);
          } catch (NumberFormatException e) {
            candidateForDecimal = false;
          }
        }
      }
    }
  }

  public Set<String> getContents() {
    return contents;
  }

  public boolean isCollectingSamples() {
    return collectingSamples;
  }

  @Override
  public String toString() {
    return name;
  }
}


<|start_filename|>gremlin/src/main/java/org/apache/tinkerpop/gremlin/arcadedb/structure/ArcadeVariableFeatures.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package org.apache.tinkerpop.gremlin.arcadedb.structure;

import org.apache.tinkerpop.gremlin.structure.Graph;

public class ArcadeVariableFeatures implements Graph.Features.VariableFeatures {

  @Override
  public boolean supportsVariables() {
    return false;
  }

  @Override
  public boolean supportsBooleanArrayValues() {
    return false;
  }

  @Override
  public boolean supportsBooleanValues() {
    return false;
  }

  @Override
  public boolean supportsByteArrayValues() {
    return false;
  }

  @Override
  public boolean supportsByteValues() {
    return false;
  }

  @Override
  public boolean supportsDoubleArrayValues() {
    return false;
  }

  @Override
  public boolean supportsDoubleValues() {
    return false;
  }

  @Override
  public boolean supportsFloatArrayValues() {
    return false;
  }

  @Override
  public boolean supportsFloatValues() {
    return false;
  }

  @Override
  public boolean supportsIntegerArrayValues() {
    return false;
  }

  @Override
  public boolean supportsIntegerValues() {
    return false;
  }

  @Override
  public boolean supportsLongArrayValues() {
    return false;
  }

  @Override
  public boolean supportsLongValues() {
    return false;
  }

  @Override
  public boolean supportsMapValues() {
    return false;
  }

  @Override
  public boolean supportsMixedListValues() {
    return false;
  }

  @Override
  public boolean supportsSerializableValues() {
    return false;
  }

  @Override
  public boolean supportsStringArrayValues() {
    return false;
  }

  @Override
  public boolean supportsStringValues() {
    return false;
  }

  @Override
  public boolean supportsUniformListValues() {
    return false;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/OptionalMatchStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

/**
 * Created by luigidellaquila on 17/10/16.
 */
public class OptionalMatchStep extends MatchStep {
  public OptionalMatchStep(CommandContext context, EdgeTraversal edge, boolean profilingEnabled) {
    super(context, edge, profilingEnabled);
  }

  @Override
  protected MatchEdgeTraverser createTraverser(Result lastUpstreamRecord) {
    return new OptionalMatchEdgeTraverser(lastUpstreamRecord, edge);
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ OPTIONAL MATCH ");
    if (edge.out) {
      result.append(" ---->\n");
    } else {
      result.append("     <----\n");
    }
    result.append(spaces);
    result.append("  ");
    result.append("{").append(edge.edge.out.alias).append("}");
    result.append(edge.edge.item.getMethod());
    result.append("{").append(edge.edge.in.alias).append("}");
    return result.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/TraverseExecutionPlanner.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Database;
import com.arcadedb.database.Identifiable;
import com.arcadedb.database.RID;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.index.RangeIndex;
import com.arcadedb.query.sql.parser.*;
import com.arcadedb.schema.DocumentType;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author <NAME> (<EMAIL>.<EMAIL>-(at)-gmail.com)
 */
public class TraverseExecutionPlanner {
  private final List<TraverseProjectionItem> projections;
  private final FromClause                   target;
  private final WhereClause                  whileClause;
  private final TraverseStatement.Strategy   strategy;
  private final PInteger                     maxDepth;
  private final Skip                         skip;
  private final Limit                        limit;

  public TraverseExecutionPlanner(TraverseStatement statement) {
    //copying the content, so that it can be manipulated and optimized
    this.projections = statement.getProjections() == null ? null : statement.getProjections().stream().map(x -> x.copy()).collect(Collectors.toList());

    this.target = statement.getTarget();
    this.whileClause = statement.getWhileClause() == null ? null : statement.getWhileClause().copy();

    this.strategy = statement.getStrategy() == null ? TraverseStatement.Strategy.DEPTH_FIRST : statement.getStrategy();
    this.maxDepth = statement.getMaxDepth() == null ? null : statement.getMaxDepth().copy();

    this.skip = statement.getSkip();
    this.limit = statement.getLimit();
  }

  public InternalExecutionPlan createExecutionPlan(CommandContext ctx, boolean enableProfiling) {
    SelectExecutionPlan result = new SelectExecutionPlan(ctx);

    handleFetchFromTarger(result, ctx, enableProfiling);

    handleTraversal(result, ctx, enableProfiling);

    if (skip != null) {
      result.chain(new SkipExecutionStep(skip, ctx, enableProfiling));
    }
    if (limit != null) {
      result.chain(new LimitExecutionStep(limit, ctx, enableProfiling));
    }

    return result;
  }

  private void handleTraversal(SelectExecutionPlan result, CommandContext ctx, boolean profilingEnabled) {
    switch (strategy) {
    case BREADTH_FIRST:
      result.chain(new BreadthFirstTraverseStep(this.projections, this.whileClause, maxDepth, ctx, profilingEnabled));
      break;
    case DEPTH_FIRST:
      result.chain(new DepthFirstTraverseStep(this.projections, this.whileClause, maxDepth, ctx, profilingEnabled));
      break;
    }
    //TODO
  }

  private void handleFetchFromTarger(SelectExecutionPlan result, CommandContext ctx, boolean profilingEnabled) {

    FromItem target = this.target == null ? null : this.target.getItem();
    if (target == null) {
      handleNoTarget(result, ctx, profilingEnabled);
    } else if (target.getIdentifier() != null) {
      handleClassAsTarget(result, this.target, ctx, profilingEnabled);
    } else if (target.getBucket() != null) {
      handleClustersAsTarget(result, Collections.singletonList(target.getBucket()), ctx, profilingEnabled);
    } else if (target.getBucketList() != null) {
      handleClustersAsTarget(result, target.getBucketList().toListOfClusters(), ctx, profilingEnabled);
    } else if (target.getStatement() != null) {
      handleSubqueryAsTarget(result, target.getStatement(), ctx, profilingEnabled);
    } else if (target.getFunctionCall() != null) {
      //        handleFunctionCallAsTarget(result, target.getFunctionCall(), ctx);//TODO
      throw new CommandExecutionException("function call as target is not supported yet");
    } else if (target.getInputParam() != null) {
      handleInputParamAsTarget(result, target.getInputParam(), ctx, profilingEnabled);
    } else if (target.getIndex() != null) {
      handleIndexAsTarget(result, target.getIndex(), ctx, profilingEnabled);
    } else if (target.getSchema() != null) {
      handleMetadataAsTarget(result, target.getSchema(), ctx, profilingEnabled);
    } else if (target.getRids() != null && target.getRids().size() > 0) {
      handleRidsAsTarget(result, target.getRids(), ctx, profilingEnabled);
    } else {
      throw new UnsupportedOperationException();
    }

  }

  private void handleInputParamAsTarget(SelectExecutionPlan result, InputParameter inputParam, CommandContext ctx, boolean profilingEnabled) {
    Object paramValue = inputParam.getValue(ctx.getInputParameters());
    if (paramValue == null) {
      result.chain(new EmptyStep(ctx, profilingEnabled));//nothing to return
    } else if (paramValue instanceof DocumentType) {
      FromClause from = new FromClause(-1);
      FromItem item = new FromItem(-1);
      from.setItem(item);
      item.setIdentifier(new Identifier(((DocumentType) paramValue).getName()));
      handleClassAsTarget(result, from, ctx, profilingEnabled);
    } else if (paramValue instanceof String) {
      //strings are treated as classes
      FromClause from = new FromClause(-1);
      FromItem item = new FromItem(-1);
      from.setItem(item);
      item.setIdentifier(new Identifier((String) paramValue));
      handleClassAsTarget(result, from, ctx, profilingEnabled);
    } else if (paramValue instanceof Identifiable) {
      RID orid = ((Identifiable) paramValue).getIdentity();

      Rid rid = new Rid(-1);
      PInteger bucket = new PInteger(-1);
      bucket.setValue(orid.getBucketId());
      PInteger position = new PInteger(-1);
      position.setValue(orid.getPosition());
      rid.setLegacy(true);
      rid.setBucket(bucket);
      rid.setPosition(position);

      handleRidsAsTarget(result, Collections.singletonList(rid), ctx, profilingEnabled);
    } else if (paramValue instanceof Iterable) {
      //try list of RIDs
      List<Rid> rids = new ArrayList<>();
      for (Object x : (Iterable) paramValue) {
        if (!(x instanceof Identifiable)) {
          throw new CommandExecutionException("Cannot use colleciton as target: " + paramValue);
        }
        RID orid = ((Identifiable) x).getIdentity();

        Rid rid = new Rid(-1);
        PInteger bucket = new PInteger(-1);
        bucket.setValue(orid.getBucketId());
        PInteger position = new PInteger(-1);
        position.setValue(orid.getPosition());
        rid.setBucket(bucket);
        rid.setPosition(position);

        rids.add(rid);
      }
      handleRidsAsTarget(result, rids, ctx, profilingEnabled);
    } else {
      throw new CommandExecutionException("Invalid target: " + paramValue);
    }
  }

  private void handleNoTarget(SelectExecutionPlan result, CommandContext ctx, boolean profilingEnabled) {
    result.chain(new EmptyDataGeneratorStep(1, ctx, profilingEnabled));
  }

  private void handleIndexAsTarget(SelectExecutionPlan result, IndexIdentifier indexIdentifier, CommandContext ctx, boolean profilingEnabled) {
    String indexName = indexIdentifier.getIndexName();
    RangeIndex index = (RangeIndex) ctx.getDatabase().getSchema().getIndexByName(indexName);
    if (index == null) {
      throw new CommandExecutionException("Index not found: " + indexName);
    }

    switch (indexIdentifier.getType()) {
    case INDEX:
      if (!index.supportsOrderedIterations()) {
        throw new CommandExecutionException("Index " + indexName + " does not allow iteration without a condition");
      }
      result.chain(new FetchFromIndexStep(index, null, null, ctx, profilingEnabled));
      result.chain(new GetValueFromIndexEntryStep(ctx, null, profilingEnabled));
      break;
    case VALUES:
    case VALUESASC:
      if (!index.supportsOrderedIterations()) {
        throw new CommandExecutionException("Index " + indexName + " does not allow iteration on values");
      }
      result.chain(new FetchFromIndexValuesStep(index, true, ctx, profilingEnabled));
      result.chain(new GetValueFromIndexEntryStep(ctx, null, profilingEnabled));
      break;
    case VALUESDESC:
      if (!index.supportsOrderedIterations()) {
        throw new CommandExecutionException("Index " + indexName + " does not allow iteration on values");
      }
      result.chain(new FetchFromIndexValuesStep(index, false, ctx, profilingEnabled));
      result.chain(new GetValueFromIndexEntryStep(ctx, null, profilingEnabled));
      break;
    }
  }

  private void handleMetadataAsTarget(final SelectExecutionPlan plan, SchemaIdentifier metadata, CommandContext ctx, boolean profilingEnabled) {
    throw new UnsupportedOperationException();
//    final Database db = ctx.getDatabase();
//    String schemaRecordIdAsString = null;
//    if (metadata.getName().equalsIgnoreCase(OCommandExecutorSQLAbstract.METADATA_SCHEMA)) {
//      schemaRecordIdAsString = db.getStorage().getConfiguration().getSchemaRecordId();
//    } else if (metadata.getName().equalsIgnoreCase(OCommandExecutorSQLAbstract.METADATA_INDEXMGR)) {
//      schemaRecordIdAsString = db.getStorage().getConfiguration().getIndexMgrRecordId();
//    } else {
//      throw new UnsupportedOperationException("Invalid metadata: " + metadata.getName());
//    }
//    ORecordId schemaRid = new ORecordId(schemaRecordIdAsString);
//    plan.chain(new FetchFromRidsStep(Collections.singleton(schemaRid), ctx, profilingEnabled));

  }

  private void handleRidsAsTarget(SelectExecutionPlan plan, List<Rid> rids, CommandContext ctx, boolean profilingEnabled) {
    List<RID> actualRids = new ArrayList<>();
    for (Rid rid : rids) {
      actualRids.add(rid.toRecordId((Result) null, ctx));
    }
    plan.chain(new FetchFromRidsStep(actualRids, ctx, profilingEnabled));
  }

  private void handleClassAsTarget(SelectExecutionPlan plan, FromClause queryTarget, CommandContext ctx, boolean profilingEnabled) {
    Identifier identifier = queryTarget.getItem().getIdentifier();

    Boolean orderByRidAsc = null;//null: no order. true: asc, false:desc
    FetchFromClassExecutionStep fetcher = new FetchFromClassExecutionStep(identifier.getStringValue(), null, ctx, orderByRidAsc, profilingEnabled);
    plan.chain(fetcher);
  }

  private void handleClustersAsTarget(SelectExecutionPlan plan, List<Bucket> clusters, CommandContext ctx, boolean profilingEnabled) {
    final Database db = ctx.getDatabase();
    Boolean orderByRidAsc = null;//null: no order. true: asc, false:desc
    if (clusters.size() == 1) {
      final Bucket bucket = clusters.get(0);
      java.lang.Integer bucketId = bucket.getBucketNumber();
      if (bucketId == null) {
        bucketId = db.getSchema().getBucketByName(bucket.getBucketName()).getId();
      }

      final FetchFromClusterExecutionStep step = new FetchFromClusterExecutionStep(bucketId, ctx, profilingEnabled);
      // TODO: THIS SEEMS A BUG
      if (Boolean.TRUE.equals(orderByRidAsc)) {
        step.setOrder(FetchFromClusterExecutionStep.ORDER_ASC);
      } else if (Boolean.FALSE.equals(orderByRidAsc)) {
        step.setOrder(FetchFromClusterExecutionStep.ORDER_DESC);
      }
      plan.chain(step);
    } else {
      int[] bucketIds = new int[clusters.size()];
      for (int i = 0; i < clusters.size(); i++) {
        final Bucket bucket = clusters.get(i);
        java.lang.Integer bucketId = bucket.getBucketNumber();
        if (bucketId == null) {
          bucketId = db.getSchema().getBucketByName(bucket.getBucketName()).getId();
        }
        bucketIds[i] = bucketId;
      }
      final FetchFromClustersExecutionStep step = new FetchFromClustersExecutionStep(bucketIds, ctx, orderByRidAsc, profilingEnabled);
      plan.chain(step);
    }
  }

  private void handleSubqueryAsTarget(SelectExecutionPlan plan, Statement subQuery, CommandContext ctx, boolean profilingEnabled) {
    BasicCommandContext subCtx = new BasicCommandContext();
    subCtx.setDatabase(ctx.getDatabase());
    subCtx.setParent(ctx);
    InternalExecutionPlan subExecutionPlan = subQuery.createExecutionPlan(subCtx, profilingEnabled);
    plan.chain(new SubQueryStep(subExecutionPlan, ctx, subCtx, profilingEnabled));
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/database/ImmutableDocument.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database;

import com.arcadedb.exception.DatabaseOperationException;
import com.arcadedb.schema.DocumentType;
import org.json.JSONObject;

import java.util.Map;
import java.util.Set;

/**
 * Immutable document implementation. To modify the record, you need to get the mutable representation by calling {@link #modify()}. This implementation keeps the
 * information in a byte[] to reduce the amount of objects to be managed by the Garbage Collector. For recurrent access to the record property you could evaluate
 * to return the mutable version of it that is backed by an internal map where the record properties are cached in RAM.
 *
 * @author <NAME>
 */
public class ImmutableDocument extends BaseDocument {

  protected ImmutableDocument(final Database graph, final DocumentType type, final RID rid, final Binary buffer) {
    super(graph, type, rid, buffer);
  }

  @Override
  public synchronized boolean has(final String propertyName) {
    checkForLazyLoading();
    final Map<String, Object> map = database.getSerializer()
        .deserializeProperties(database, buffer, new EmbeddedModifierProperty(this, propertyName), propertyName);
    return map.containsKey(propertyName);
  }

  @Override
  public synchronized Object get(final String propertyName) {
    checkForLazyLoading();
    final Map<String, Object> map = database.getSerializer()
        .deserializeProperties(database, buffer, new EmbeddedModifierProperty(this, propertyName), propertyName);
    return map.get(propertyName);
  }

  @Override
  public synchronized MutableDocument modify() {
    final Record recordInCache = database.getTransaction().getRecordFromCache(rid);
    if (recordInCache instanceof MutableDocument)
      return (MutableDocument) recordInCache;

    checkForLazyLoading();
    buffer.rewind();
    return new MutableDocument(database, type, rid, buffer.copy());
  }

  @Override
  public synchronized JSONObject toJSON() {
    checkForLazyLoading();
    final Map<String, Object> map = database.getSerializer().deserializeProperties(database, buffer, new EmbeddedModifierObject(this));

    final JSONObject result = new JSONSerializer(database).map2json(map);
    result.put("@type", type.getName());
    if (getIdentity() != null)
      result.put("@rid", getIdentity().toString());
    return result;
  }

  @Override
  public synchronized Map<String, Object> toMap() {
    checkForLazyLoading();
    return database.getSerializer().deserializeProperties(database, buffer, new EmbeddedModifierObject(this));
  }

  @Override
  public synchronized String toString() {
    final StringBuilder output = new StringBuilder(256);
    if (rid != null)
      output.append(rid);
    output.append('[');
    if (buffer == null)
      output.append('?');
    else {
      final int currPosition = buffer.position();

      buffer.position(propertiesStartingPosition);
      final Map<String, Object> map = this.database.getSerializer().deserializeProperties(database, buffer, new EmbeddedModifierObject(this));

      buffer.position(currPosition);

      int i = 0;
      for (Map.Entry<String, Object> entry : map.entrySet()) {
        if (i > 0)
          output.append(',');

        output.append(entry.getKey());
        output.append('=');
        output.append(entry.getValue());
        i++;
      }
    }
    output.append(']');
    return output.toString();
  }

  @Override
  public synchronized Set<String> getPropertyNames() {
    checkForLazyLoading();
    return database.getSerializer().getPropertyNames(database, buffer);
  }

  protected boolean checkForLazyLoading() {
    if (buffer == null) {
      if (rid == null)
        throw new DatabaseOperationException("Document cannot be loaded because RID is null");

      buffer = database.getSchema().getBucketById(rid.getBucketId()).getRecord(rid);
      buffer.position(propertiesStartingPosition);
      return true;
    }

    buffer.position(propertiesStartingPosition);
    return false;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/bucketselectionstrategy/ThreadBucketSelectionStrategy.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database.bucketselectionstrategy;

import com.arcadedb.database.Document;
import com.arcadedb.schema.DocumentType;

/**
 * Threaded implementation that returns the bucket partitioned with the thread id. In this way there is no conflict between documents created by concurrent threads.
 *
 * @author <NAME>
 */
public class ThreadBucketSelectionStrategy implements BucketSelectionStrategy {
  protected int total;

  @Override
  public void setType(final DocumentType type) {
    this.total = type.getBuckets(false).size();
  }

  @Override
  public int getBucketIdByRecord(final Document record, final boolean async) {
    return (int) (Thread.currentThread().getId() % total);
  }

  @Override
  public int getBucketIdByKeys(final Object[] keys, final boolean async) {
    // UNSUPPORTED
    return -1;
  }

  @Override
  public String getName() {
    return "thread";
  }

  @Override
  public String toString() {
    return getName();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/FunctionAggregationContext.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.query.sql.parser.Expression;

import java.util.ArrayList;
import java.util.List;

/**
 * Delegates to an aggregate function for aggregation calculation
 *
 * @author <NAME> (<EMAIL>)
 */
public class FunctionAggregationContext implements AggregationContext {
  private final SQLFunction      aggregateFunction;
  private       List<Expression> params;

  public FunctionAggregationContext(SQLFunction function, List<Expression> params) {
    this.aggregateFunction = function;
    this.params = params;
    if (this.params == null) {
      this.params = new ArrayList<>();
    }
  }

  @Override
  public Object getFinalValue() {
    return aggregateFunction.getResult();
  }

  @Override
  public void apply(Result next, CommandContext ctx) {
    List<Object> paramValues = new ArrayList<>();
    for (Expression expr : params) {
      paramValues.add(expr.execute(next, ctx));
    }
    aggregateFunction.execute(next, null, null, paramValues.toArray(), ctx);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ReturnMatchPathsStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 12/10/16.
 */
public class ReturnMatchPathsStep extends AbstractExecutionStep {

  public ReturnMatchPathsStep(CommandContext context, boolean profilingEnabled) {
    super(context, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        return upstream.next();
      }

      @Override
      public void close() {

      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    return spaces + "+ RETURN $paths";
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/misc/SQLFunctionUUID.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.function.SQLFunctionAbstract;

import java.util.UUID;

/**
 * Generates a UUID as a 128-bits value using the Leach-Salz variant. For more information look at:
 * http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLFunctionUUID extends SQLFunctionAbstract {
  public static final String NAME = "uuid";

  /**
   * Get the date at construction to have the same date for all the iteration.
   */
  public SQLFunctionUUID() {
    super(NAME);
  }

  public Object execute( Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, final Object[] iParams,
      CommandContext iContext) {
    return UUID.randomUUID().toString();
  }

  public boolean aggregateResults(final Object[] configuredParameters) {
    return false;
  }

  public String getSyntax() {
    return "uuid()";
  }

  @Override
  public Object getResult() {
    return null;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/AbstractExecutionStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import java.text.DecimalFormat;
import java.util.Optional;

/**
 * @author <NAME> (<EMAIL>.dellaquila-(at)-gmail.com)
 */
public abstract class AbstractExecutionStep implements ExecutionStepInternal {

  protected final CommandContext                  ctx;
  protected       Optional<ExecutionStepInternal> prev     = Optional.empty();
  protected       Optional<ExecutionStepInternal> next     = Optional.empty();
  protected       boolean                         timedOut = false;

  protected boolean profilingEnabled;

  public AbstractExecutionStep(CommandContext ctx, boolean profilingEnabled) {
    this.ctx = ctx;
    this.profilingEnabled = profilingEnabled;
  }

  @Override
  public void setPrevious(ExecutionStepInternal step) {
    this.prev = Optional.ofNullable(step);
  }

  @Override
  public void setNext(ExecutionStepInternal step) {
    this.next = Optional.ofNullable(step);
  }

  public CommandContext getContext() {
    return ctx;
  }

  public Optional<ExecutionStepInternal> getPrev() {
    return prev;
  }

  public Optional<ExecutionStepInternal> getNext() {
    return next;
  }

  @Override
  public void sendTimeout() {
    this.timedOut = true;
    prev.ifPresent(ExecutionStepInternal::sendTimeout);
  }

  public boolean isTimedOut() {
    return timedOut;
  }

  @Override
  public void close() {
    prev.ifPresent(ExecutionStepInternal::close);
  }

  public boolean isProfilingEnabled() {
    return profilingEnabled;
  }

  public void setProfilingEnabled(boolean profilingEnabled) {
    this.profilingEnabled = profilingEnabled;
  }

  protected String getCostFormatted() {
    return new DecimalFormat().format(getCost() / 1000) + "μs";
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/engine/BufferBloomFilter.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.engine;

import com.arcadedb.database.Binary;

public class BufferBloomFilter {
  private final Binary buffer;
  private final int    hashSeed;
  private final int    capacity;

  public BufferBloomFilter(final Binary buffer, final int slots, final int hashSeed) {
    if (slots % 8 > 0)
      throw new IllegalArgumentException("Slots must be a multiplier of 8");
    this.buffer = buffer;
    this.hashSeed = hashSeed;
    this.capacity = slots;
  }

  public void add(final int value) {
    final byte[] b = new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
    final int hash = MurmurHash.hash32(b, 4, hashSeed);
    final int h = hash != Integer.MIN_VALUE ? Math.abs(hash) : Integer.MAX_VALUE;

    final int bit2change = h > capacity ? h % capacity : h;
    final int byte2change = bit2change / 8;
    final int bitInByte2change = bit2change % 8;

    final byte v = buffer.getByte(byte2change);
    buffer.putByte(byte2change, (byte) (v | (1 << bitInByte2change)));
  }

  public boolean mightContain(final int value) {
    final byte[] b = new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
    final int hash = MurmurHash.hash32(b, 4, hashSeed);
    final int h = hash != Integer.MIN_VALUE ? Math.abs(hash) : Integer.MAX_VALUE;

    final int bit2change = h > capacity ? h % capacity : h;
    final int byte2change = bit2change / 8;
    final int bitInByte2change = bit2change % 8;

    final byte v = buffer.getByte(byte2change);
    return ((v >> bitInByte2change) & 1) == 1;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/ConstantsTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class ConstantsTest {

  @Test
  void getVersionMajor() {
    Assertions.assertNotNull(Constants.getVersionMajor());
  }

  @Test
  void getVersionMinor() {
    Assertions.assertNotNull(Constants.getVersionMinor());
  }

  @Test
  void getVersionHotfix() {
    Assertions.assertNotNull(Constants.getVersionHotfix());
  }

  @Test
  void getVersion() {
    Assertions.assertNotNull(Constants.getVersion());
  }

  @Test
  void getRawVersion() {
    Assertions.assertNotNull(Constants.getRawVersion());
  }

  @Test
  void getBranch() {
    Assertions.assertNotNull(Constants.getBranch());
  }

  @Test
  void getBuildNumber() {
    Assertions.assertNotNull(Constants.getBuildNumber());
  }

  @Test
  void getTimestamp() {
    Assertions.assertNotNull(Constants.getTimestamp());
  }

  @Test
  void isSnapshot() {
    Assertions.assertNotNull(Constants.isSnapshot());
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/TraverseStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collection;

/**
 * @author <NAME> (l.dellaquila-(at)-orientdb.com)
 */
public class TraverseStatementExecutionTest extends TestHelper {
  @Test
  public void testPlainTraverse() {
    database.transaction(() -> {
      String classPrefix = "testPlainTraverse_";
      database.getSchema().createVertexType(classPrefix + "V");
      database.getSchema().createEdgeType(classPrefix + "E");
      database.command("sql", "create vertex " + classPrefix + "V set name = 'a'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'b'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'c'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'd'").close();

      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'a') to (select from " + classPrefix + "V where name = 'b')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'b') to (select from " + classPrefix + "V where name = 'c')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'c') to (select from " + classPrefix + "V where name = 'd')")
          .close();

      ResultSet result = database.query("sql", "traverse out() from (select from " + classPrefix + "V where name = 'a')");

      for (int i = 0; i < 4; i++) {
        Assertions.assertTrue(result.hasNext());
        Result item = result.next();
        Assertions.assertEquals(i, item.getMetadata("$depth"));
      }
      Assertions.assertFalse(result.hasNext());
      result.close();
    });
  }

  @Test
  public void testWithDepth() {
    database.transaction(() -> {
      String classPrefix = "testWithDepth_";
      database.getSchema().createVertexType(classPrefix + "V");
      database.getSchema().createEdgeType(classPrefix + "E");
      database.command("sql", "create vertex " + classPrefix + "V set name = 'a'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'b'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'c'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'd'").close();

      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'a') to (select from " + classPrefix + "V where name = 'b')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'b') to (select from " + classPrefix + "V where name = 'c')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'c') to (select from " + classPrefix + "V where name = 'd')")
          .close();

      ResultSet result = database.query("sql", "traverse out() from (select from " + classPrefix + "V where name = 'a') WHILE $depth < 2");

      for (int i = 0; i < 2; i++) {
        Assertions.assertTrue(result.hasNext());
        Result item = result.next();
        Assertions.assertEquals(i, item.getMetadata("$depth"));
      }
      Assertions.assertFalse(result.hasNext());
      result.close();
    });
  }

  @Test
  public void testMaxDepth() {
    database.transaction(() -> {
      String classPrefix = "testMaxDepth";
      database.getSchema().createVertexType(classPrefix + "V");
      database.getSchema().createEdgeType(classPrefix + "E");
      database.command("sql", "create vertex " + classPrefix + "V set name = 'a'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'b'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'c'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'd'").close();

      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'a') to (select from " + classPrefix + "V where name = 'b')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'b') to (select from " + classPrefix + "V where name = 'c')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'c') to (select from " + classPrefix + "V where name = 'd')")
          .close();

      ResultSet result = database.query("sql", "traverse out() from (select from " + classPrefix + "V where name = 'a') MAXDEPTH 1");

      for (int i = 0; i < 2; i++) {
        Assertions.assertTrue(result.hasNext());
        Result item = result.next();
        Assertions.assertEquals(i, item.getMetadata("$depth"));
      }
      Assertions.assertFalse(result.hasNext());
      result.close();

      result = database.query("sql", "traverse out() from (select from " + classPrefix + "V where name = 'a') MAXDEPTH 2");

      for (int i = 0; i < 3; i++) {
        Assertions.assertTrue(result.hasNext());
        Result item = result.next();
        Assertions.assertEquals(i, item.getMetadata("$depth"));
      }
      Assertions.assertFalse(result.hasNext());
      result.close();
    });
  }

  @Test
  public void testBreadthFirst() {
    database.transaction(() -> {
      String classPrefix = "testBreadthFirst_";
      database.getSchema().createVertexType(classPrefix + "V");
      database.getSchema().createEdgeType(classPrefix + "E");
      database.command("sql", "create vertex " + classPrefix + "V set name = 'a'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'b'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'c'").close();
      database.command("sql", "create vertex " + classPrefix + "V set name = 'd'").close();

      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'a') to (select from " + classPrefix + "V where name = 'b')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'b') to (select from " + classPrefix + "V where name = 'c')")
          .close();
      database.command("sql",
          "create edge " + classPrefix + "E from (select from " + classPrefix + "V where name = 'c') to (select from " + classPrefix + "V where name = 'd')")
          .close();

      ResultSet result = database.query("sql", "traverse out() from (select from " + classPrefix + "V where name = 'a') STRATEGY BREADTH_FIRST");

      for (int i = 0; i < 4; i++) {
        Assertions.assertTrue(result.hasNext());
        Result item = result.next();
        Assertions.assertEquals(i, item.getMetadata("$depth"));
      }
      Assertions.assertFalse(result.hasNext());
      result.close();
    });
  }

  @Test
  public void testTraverseInBatchTx() {
    database.transaction(() -> {
      String script = "";
      script += "";

      script += "drop type testTraverseInBatchTx_V if exists unsafe;";
      script += "create vertex type testTraverseInBatchTx_V;";
      script += "create property testTraverseInBatchTx_V.name STRING;";
      script += "drop type testTraverseInBatchTx_E if exists unsafe;";
      script += "create edge type testTraverseInBatchTx_E;";

      script += "begin;";
      script += "insert into testTraverseInBatchTx_V(name) values ('a'), ('b'), ('c');";
      script += "create edge testTraverseInBatchTx_E from (select from testTraverseInBatchTx_V where name = 'a') to (select from testTraverseInBatchTx_V where name = 'b');";
      script += "create edge testTraverseInBatchTx_E from (select from testTraverseInBatchTx_V where name = 'b') to (select from testTraverseInBatchTx_V where name = 'c');";
      script += "let top = (select * from (traverse in('testTraverseInBatchTx_E') from (select from testTraverseInBatchTx_V where name='c')) where in('testTraverseInBatchTx_E').size() == 0);";
      script += "commit;";
      script += "return $top;";

      ResultSet result = database.execute("sql", script);
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Object val = item.getProperty("value");
      Assertions.assertTrue(val instanceof Collection);
      Assertions.assertEquals(1, ((Collection) val).size());
      result.close();
    });
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/MatchStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.FieldMatchPathItem;
import com.arcadedb.query.sql.parser.MultiMatchPathItem;

import java.util.Map;
import java.util.Optional;

/**
 * @author <NAME>
 */
public class MatchStep extends AbstractExecutionStep {
  protected final EdgeTraversal      edge;
  private         ResultSet          upstream;
  private         Result             lastUpstreamRecord;
  private         MatchEdgeTraverser traverser;
  private         Result             nextResult;

  public MatchStep(CommandContext context, EdgeTraversal edge, boolean profilingEnabled) {
    super(context, profilingEnabled);
    this.edge = edge;
  }

  @Override
  public void reset() {
    this.upstream = null;
    this.lastUpstreamRecord = null;
    this.traverser = null;
    this.nextResult = null;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    return new ResultSet() {
      int localCount = 0;

      @Override
      public boolean hasNext() {
        if (localCount >= nRecords) {
          return false;
        }
        if (nextResult == null) {
          fetchNext(ctx, nRecords);
        }
        return nextResult != null;
      }

      @Override
      public Result next() {
        if (localCount >= nRecords) {
          throw new IllegalStateException();
        }
        if (nextResult == null) {
          fetchNext(ctx, nRecords);
        }
        if (nextResult == null) {
          throw new IllegalStateException();
        }
        Result result = nextResult;
        fetchNext(ctx, nRecords);
        localCount++;
        ctx.setVariable("$matched", result);
        return result;
      }

      @Override
      public void close() {

      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private void fetchNext(CommandContext ctx, int nRecords) {
    nextResult = null;
    while (true) {
      if (traverser != null && traverser.hasNext(ctx)) {
        nextResult = traverser.next(ctx);
        break;
      }

      if (upstream == null || !upstream.hasNext()) {
        upstream = getPrev().get().syncPull(ctx, nRecords);
      }
      if (!upstream.hasNext()) {
        return;
      }

      lastUpstreamRecord = upstream.next();

      traverser = createTraverser(lastUpstreamRecord);

      boolean found = false;
      while (traverser.hasNext(ctx)) {
        nextResult = traverser.next(ctx);
        if (nextResult != null) {
          found = true;
          break;
        }
      }
      if (found) {
        break;
      }
    }
  }

  protected MatchEdgeTraverser createTraverser(Result lastUpstreamRecord) {
    if (edge.edge.item instanceof MultiMatchPathItem) {
      return new MatchMultiEdgeTraverser(lastUpstreamRecord, edge);
    } else if (edge.edge.item instanceof FieldMatchPathItem) {
      return new MatchFieldTraverser(lastUpstreamRecord, edge);
    } else if (edge.out) {
      return new MatchEdgeTraverser(lastUpstreamRecord, edge);
    } else {
      return new MatchReverseEdgeTraverser(lastUpstreamRecord, edge);
    }
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ MATCH ");
    if (edge.out) {
      result.append("     ---->\n");
    } else {
      result.append("     <----\n");
    }
    result.append(spaces);
    result.append("  ");
    result.append("{").append(edge.edge.out.alias).append("}");
    result.append(edge.edge.item.getMethod());
    result.append("{").append(edge.edge.in.alias).append("}");
    return result.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/CopyRecordContentBeforeUpdateStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.Record;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * <p>Reads an upstream result set and returns a new result set that contains copies of the original OResult instances
 * </p>
 * <p>This is mainly used from statements that need to copy of the original data before modifying it,
 * eg. UPDATE ... RETURN BEFORE</p>
 *
 * @author <NAME> (luigi.dellaquila-(at)-gmail.com)
 */
public class CopyRecordContentBeforeUpdateStep extends AbstractExecutionStep {
  private long cost = 0;

  public CopyRecordContentBeforeUpdateStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet lastFetched = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return lastFetched.hasNext();
      }

      @Override
      public Result next() {
        Result result = lastFetched.next();
        long begin = profilingEnabled ? System.nanoTime() : 0;
        try {

          if (result instanceof UpdatableResult) {
            ResultInternal prevValue = new ResultInternal();
            Record rec = result.getElement().get().getRecord();
            prevValue.setProperty("@rid", rec.getIdentity());
            if (rec instanceof Document) {
              prevValue.setProperty("@class", ((Document) rec).getTypeName());
            }
            for (String propName : result.getPropertyNames()) {
              prevValue.setProperty(propName, result.getProperty(propName));
            }
            ((UpdatableResult) result).previousValue = prevValue;
          } else {
            throw new CommandExecutionException("Cannot fetch previous value: " + result);
          }
          return result;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }

      @Override
      public void close() {
        lastFetched.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ COPY RECORD CONTENT BEFORE UPDATE");
    if (profilingEnabled) {
      result.append(" (").append(getCostFormatted()).append(")");
    }
    return result.toString();
  }

  @Override
  public long getCost() {
    return cost;
  }

}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/parser/LetStatementTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.parser;

import org.junit.jupiter.api.Test;

public class LetStatementTest extends ParserTestAbstract {

  @Test
  public void testPlain() {
    checkRightSyntax("let foo = select from foo where name = 'bar'");
    checkRightSyntax("LET foo = select from foo where name = 'bar'");
    checkRightSyntax("LET foo = select from foo where name = 'bar';");
    checkRightSyntax("LET $foo = select from foo where name = 'bar'");
    checkRightSyntax("LET $foo = (select from foo where name = 'bar')");
    checkRightSyntax("LET $foo = bar");
    checkRightSyntax("LET $foo = 1 + 2 + 3");
    checkRightSyntax("LET $foo = [1, 3, 5]");

    checkRightSyntax("LET a = create vertex Test content {\"id\": \"12345678\"}");

    checkWrongSyntax("LET ");
    checkWrongSyntax("LET foo");
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/SQLFunctionAbstract.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function;

import com.arcadedb.query.sql.executor.SQLFunction;

/**
 * Abstract class to extend to build Custom SQL Functions.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public abstract class SQLFunctionAbstract implements SQLFunction {
  protected String name;

  public SQLFunctionAbstract(final String iName) {
    this.name = iName;
  }

  @Override
  public String getName() {
    return name;
  }

  @Override
  public String toString() {
    return getSyntax();
  }

  @Override
  public void config(final Object[] iConfiguredParameters) {
  }

  @Override
  public boolean aggregateResults() {
    return false;
  }

  @Override
  public Object getResult() {
    return null;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/BinaryStructure.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database;

import java.nio.ByteBuffer;

public interface BinaryStructure {
  void append(Binary toCopy);

  int position();

  void position(int index);

  void putByte(int index, byte value);

  void putByte(byte value);

  int putNumber(int index, long value);

  int putNumber(long value);

  int putUnsignedNumber(int index, long value);

  int putUnsignedNumber(long value);

  void putShort(int index, short value);

  void putShort(short value);

  void putInt(int index, int value);

  void putInt(int value);

  void putLong(int index, long value);

  void putLong(long value);

  int putString(int index, String value);

  int putString(String value);

  int putBytes(int index, byte[] value);

  int putBytes(byte[] value);

  int putBytes(byte[] value, int size);

  void putByteArray(int index, byte[] value);

  void putByteArray(int index, byte[] value, int length);

  void putByteArray(byte[] value);

  void putBuffer(ByteBuffer value);

  void putByteArray(byte[] value, int length);

  byte getByte(int index);

  byte getByte();

  long[] getNumberAndSize(int index);

  long getNumber();

  long getUnsignedNumber();

  long[] getUnsignedNumberAndSize();

  short getShort(int index);

  short getShort();

  short getUnsignedShort();

  int getInt();

  int getInt(int index);

  long getLong();

  long getLong(int index);

  String getString();

  String getString(int index);

  void getByteArray(byte[] buffer);

  void getByteArray(int index, byte[] buffer);

  void getByteArray(int index, byte[] buffer, int offset, int length);

  byte[] getBytes();

  byte[] getBytes(int index);

  byte[] toByteArray();

  byte[] remainingToByteArray();

  ByteBuffer getByteBuffer();

  int size();
}


<|start_filename|>engine/src/main/java/com/arcadedb/utility/RecordTableFormatter.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.utility;

import com.arcadedb.database.Document;
import com.arcadedb.query.sql.executor.Result;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class RecordTableFormatter extends TableFormatter {

  public static class TableRecordRow implements TableRow {
    private final Result result;

    public TableRecordRow(final Result result) {
      this.result = result;
    }

    @Override
    public Object getField(final String field) {
      if (field.equalsIgnoreCase("@rid")) {
        if (result.getIdentity().isPresent())
          return result.getIdentity().get();
      } else if (field.equalsIgnoreCase("@type")) {
        if (result.getRecord().isPresent())
          return ((Document) result.getRecord().get()).getTypeName();
      }
      return result.getProperty(field);
    }

    @Override
    public Set<String> getFields() {
      return result.getPropertyNames();
    }
  }

  public RecordTableFormatter(final TableOutput iConsole) {
    super(iConsole);
  }

  public void writeRecords(final List<Result> records, final int limit) {
    final List<TableRow> rows = new ArrayList<>();
    for (Result record : records)
      rows.add(new TableRecordRow(record));

    super.writeRows(rows, limit);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/TrackableBinary.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database;

import com.arcadedb.engine.TrackableContent;

import java.nio.ByteBuffer;

public class TrackableBinary extends Binary implements TrackableContent {
  private final TrackableContent derivedFrom;

  public TrackableBinary(final TrackableContent derivedFrom, final ByteBuffer slice) {
    super(slice);
    this.derivedFrom = derivedFrom;
  }

  public int[] getModifiedRange() {
    return derivedFrom.getModifiedRange();
  }

  public void updateModifiedRange(final int start, final int end) {
    derivedFrom.updateModifiedRange(buffer.arrayOffset() + start, buffer.arrayOffset() + end);
  }

  @Override
  protected void checkForAllocation(final int offset, final int bytesToWrite) {
    super.checkForAllocation(offset, bytesToWrite);
    updateModifiedRange(offset, offset + bytesToWrite - 1);
  }

  public void move(final int startPosition, final int destPosition, final int length) {
    super.move(startPosition, destPosition, length);
    updateModifiedRange(startPosition, destPosition + length);
  }

  public Binary slice() {
    return new TrackableBinary(this, buffer.slice());
  }

  public Binary slice(final int position) {
    buffer.position(position);
    return new TrackableBinary(this, buffer.slice());
  }

  public Binary slice(final int position, final int length) {
    buffer.position(position);
    final ByteBuffer result = buffer.slice();
    result.position(length);
    result.flip();
    return new TrackableBinary(this, result);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/FilterByClustersStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Database;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Created by luigidellaquila on 01/03/17.
 */
public class FilterByClustersStep extends AbstractExecutionStep {
  private Set<String>  clusters;
  private Set<Integer> bucketIds;

  ResultSet prevResult = null;

  public FilterByClustersStep(Set<String> filterClusters, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.clusters = filterClusters;
    Database db = ctx.getDatabase();
    init(db);

  }

  private void init(Database db) {
    if (this.bucketIds == null) {
      this.bucketIds = clusters.stream().filter(x -> x != null).map(x -> db.getSchema().getBucketByName(x).getId()).collect(Collectors.toSet());
    }
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    init(ctx.getDatabase());
    if (prev.isEmpty()) {
      throw new IllegalStateException("filter step requires a previous step");
    }
    ExecutionStepInternal prevStep = prev.get();

    return new ResultSet() {
      public boolean finished = false;

      Result nextItem = null;
      int fetched = 0;

      private void fetchNextItem() {
        nextItem = null;
        if (finished) {
          return;
        }
        if (prevResult == null) {
          prevResult = prevStep.syncPull(ctx, nRecords);
          if (!prevResult.hasNext()) {
            finished = true;
            return;
          }
        }
        while (!finished) {
          while (!prevResult.hasNext()) {
            prevResult = prevStep.syncPull(ctx, nRecords);
            if (!prevResult.hasNext()) {
              finished = true;
              return;
            }
          }
          nextItem = prevResult.next();
          if (nextItem.isElement()) {
            int bucketId = nextItem.getIdentity().get().getBucketId();
            if (bucketId < 0) {
              // this record comes from a TX, it still doesn't have a bucket assigned
              break;
            }
            if (bucketIds.contains(bucketId)) {
              break;
            }
          }
          nextItem = null;
        }
      }

      @Override
      public boolean hasNext() {

        if (fetched >= nRecords || finished) {
          return false;
        }
        if (nextItem == null) {
          fetchNextItem();
        }

        return nextItem != null;

      }

      @Override
      public Result next() {
        if (fetched >= nRecords || finished) {
          throw new IllegalStateException();
        }
        if (nextItem == null) {
          fetchNextItem();
        }
        if (nextItem == null) {
          throw new IllegalStateException();
        }
        Result result = nextItem;
        nextItem = null;
        fetched++;
        return result;
      }

      @Override
      public void close() {
        FilterByClustersStep.this.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };

  }

  @Override
  public String prettyPrint(int depth, int indent) {
    return ExecutionStepInternal.getIndent(depth, indent) + "+ FILTER ITEMS BY CLUSTERS \n" + ExecutionStepInternal.getIndent(depth, indent) + "  "
        + clusters.stream().collect(Collectors.joining(", "));
  }

  @Override
  public Result serialize() {
    ResultInternal result = ExecutionStepInternal.basicSerialize(this);
    if (clusters != null) {
      result.setProperty("clusters", clusters);
    }

    return result;
  }

  @Override
  public void deserialize(Result fromResult) {
    try {
      ExecutionStepInternal.basicDeserialize(fromResult, this);
      clusters = fromResult.getProperty("clusters");
    } catch (Exception e) {
      throw new CommandExecutionException(e);
    }
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/DistributedExecutionStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 08/05/17.
 */
public class DistributedExecutionStep extends AbstractExecutionStep {

  private final SelectExecutionPlan subExecuitonPlan;
  private final String              nodeName;

  private boolean inited;

  private ResultSet remoteResultSet;

  public DistributedExecutionStep(SelectExecutionPlan subExecutionPlan, String nodeName, CommandContext ctx,
      boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.subExecuitonPlan = subExecutionPlan;
    this.nodeName = nodeName;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    init(ctx);
    getPrev().ifPresent(x -> x.syncPull(ctx, nRecords));
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        throw new UnsupportedOperationException("Implement distributed execution step!");
      }

      @Override
      public Result next() {
        throw new UnsupportedOperationException("Implement distributed execution step!");
      }

      @Override
      public void close() {
        DistributedExecutionStep.this.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  public void init(CommandContext ctx) {
    if (!inited) {
      inited = true;
      this.remoteResultSet = sendSerializedExecutionPlan(nodeName, subExecuitonPlan, ctx);
    }
  }

  private ResultSet sendSerializedExecutionPlan(String nodeName, ExecutionPlan serializedExecutionPlan, CommandContext ctx) {
//    Database db = ctx.getDatabase();
    throw new UnsupportedOperationException();
//    return db.queryOnNode(nodeName, serializedExecutionPlan, ctx.getInputParameters());
  }

  @Override
  public void close() {
    super.close();
    if (this.remoteResultSet != null) {
      this.remoteResultSet.close();
    }
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    StringBuilder builder = new StringBuilder();
    String ind = ExecutionStepInternal.getIndent(depth, indent);
    builder.append(ind);
    builder.append("+ EXECUTE ON NODE ").append(nodeName).append("----------- \n");
    builder.append(subExecuitonPlan.prettyPrint(depth + 1, indent));
    builder.append("  ------------------------------------------- \n");
    builder.append("   |\n");
    builder.append("   V\n");
    return builder.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/misc/SQLFunctionDate.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.exception.QueryParsingException;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.function.SQLFunctionAbstract;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * Builds a date object from the format passed. If no arguments are passed, than the system date is built (like sysdate() function)
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 * @see SQLFunctionSysdate
 */
public class SQLFunctionDate extends SQLFunctionAbstract {
  public static final String NAME = "date";

  private final Date             date;
  private       SimpleDateFormat format;

  /**
   * Get the date at construction to have the same date for all the iteration.
   */
  public SQLFunctionDate() {
    super(NAME);
    date = new Date();
  }

  public Object execute(Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, final Object[] iParams,
      CommandContext iContext) {
    if (iParams.length == 0)
      return date;

    if (iParams[0] == null)
      return null;

    if (iParams[0] instanceof Number)
      return new Date(((Number) iParams[0]).longValue());

    if (format == null) {
      final TimeZone tz =
          iParams.length > 2 ? TimeZone.getTimeZone(iParams[2].toString()) : iContext.getDatabase().getSchema().getTimeZone();

      if (iParams.length > 1)
        format = new SimpleDateFormat((String) iParams[1]);
      else
        format = new SimpleDateFormat(iContext.getDatabase().getSchema().getDateFormat());

      format.setTimeZone(tz);
    }

    try {
      return format.parse((String) iParams[0]);
    } catch (ParseException e) {
      throw new QueryParsingException("Error on formatting date '" + iParams[0] + "' using the format: " + format.toPattern(), e);
    }
  }

  public boolean aggregateResults(final Object[] configuredParameters) {
    return false;
  }

  public String getSyntax() {
    return "date([<date-as-string>] [,<format>] [,<timezone>])";
  }

  @Override
  public Object getResult() {
    format = null;
    return null;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/UpdateMergeStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.database.Record;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.Json;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 09/08/16.
 */
public class UpdateMergeStep extends AbstractExecutionStep {
  private final Json json;

  public UpdateMergeStep(Json json, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.json = json;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        Result result = upstream.next();
        if (result instanceof ResultInternal) {
          if (!(result.getElement().orElse(null) instanceof Document)) {
            ((ResultInternal) result).setElement((Document) result.getElement().get().getRecord());
          }
          if (!(result.getElement().orElse(null) instanceof Document)) {
            return result;
          }
          handleMerge(result.getElement().orElse(null), ctx);
        }
        return result;
      }

      @Override
      public void close() {
        upstream.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private void handleMerge(Record record, CommandContext ctx) {
    final MutableDocument doc = ((Document) record).modify();
    final Map<String, Object> map = json.toMap(record, ctx);
    for (Map.Entry<String, Object> entry : map.entrySet())
      doc.set(entry.getKey(), entry.getValue());
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ UPDATE MERGE\n" + spaces + "  " + json;
    return result;
  }
}


<|start_filename|>engine/src/test/java/performance/PerformanceComplexIndexTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package performance;

import com.arcadedb.GlobalConfiguration;
import com.arcadedb.database.Database;
import com.arcadedb.database.DatabaseFactory;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.database.async.ErrorCallback;
import com.arcadedb.engine.WALFile;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Schema;

import java.util.UUID;

public class PerformanceComplexIndexTest {
  private static final int    TOT       = 10_000_000;
  private static final String TYPE_NAME = "Device";

  public static void main(String[] args) throws Exception {
    new PerformanceComplexIndexTest().run();
  }

  private void run() {
    GlobalConfiguration.PROFILE.setValue("high-performance");
    PerformanceTest.clean();

    final int parallel = 2;

    Database database = new DatabaseFactory(PerformanceTest.DATABASE_PATH).open();
    try {
      if (!database.getSchema().existsType(TYPE_NAME)) {
        database.begin();

        DocumentType v = database.getSchema().createDocumentType(TYPE_NAME, parallel);

        v.createProperty("id", Long.class);
        v.createProperty("name", String.class);
        v.createProperty("surname", String.class);
        v.createProperty("locali", Integer.class);
        v.createProperty("notes1", String.class);

        database.getSchema().createTypeIndex(Schema.INDEX_TYPE.LSM_TREE, false, TYPE_NAME, new String[] { "id" }, 5000000);

        database.commit();
      }
    } finally {
      database.close();
    }

    database = new DatabaseFactory(PerformanceTest.DATABASE_PATH).open();

    long begin = System.currentTimeMillis();

    try {

      database.setReadYourWrites(false);
      database.async().setCommitEvery(5000);
      database.async().setParallelLevel(parallel);
      database.async().setTransactionUseWAL(true);
      database.async().setTransactionSync(WALFile.FLUSH_TYPE.NO);

      database.async().onError(new ErrorCallback() {
        @Override
        public void call(Throwable exception) {
          System.out.println("ERROR: " + exception);
          exception.printStackTrace();
        }
      });

      long row = 0;
      for (; row < TOT; ++row) {
        final MutableDocument record = database.newDocument(TYPE_NAME);

        final String randomString = UUID.randomUUID().toString();

        record.set("id", row);
        record.set("name", randomString);
        record.set("surname", randomString);
        record.set("locali", 10);
        record.set("notes1",
            "This is a long field to check how Arcade behaves with large fields. This is a long field to check how Arcade behaves with large fields.");
//        record.set("notes2",
//            "This is a long field to check how Arcade behaves with large fields. This is a long field to check how Arcade behaves with large fields. This is a long field to check how Arcade behaves with large fields. This is a long field to check how Arcade behaves with large fields.");

        database.async().createRecord(record, null);

        if (row % 100000 == 0)
          System.out.println("Written " + row + " elements in " + (System.currentTimeMillis() - begin) + "ms");
      }

      System.out.println("Inserted " + row + " elements in " + (System.currentTimeMillis() - begin) + "ms");

    } finally {
      database.close();
      System.out.println("Insertion finished in " + (System.currentTimeMillis() - begin) + "ms");
    }
//
//    begin = System.currentTimeMillis();
//    database = new DatabaseFactory(PerformanceTest.DATABASE_PATH).open();
//    try {
//      System.out.println("Lookup all the keys...");
//      for (long id = 0; id < TOT; ++id) {
//        final Cursor<RID> records = database.lookupByKey(TYPE_NAME, new String[] { "id" }, new Object[] { id });
//        Assertions.assertNotNull(records);
//        Assertions.assertEquals(1, records.size(), "Wrong result for lookup of key " + id);
//
//        final Document record = (Document) records.next().getRecord();
//        Assertions.assertEquals(id, record.get("id"));
//
//        if (id % 100000 == 0)
//          System.out.println("Checked " + id + " lookups in " + (System.currentTimeMillis() - begin) + "ms");
//      }
//    } finally {
//      database.close();
//      System.out.println("Lookup finished in " + (System.currentTimeMillis() - begin) + "ms");
//    }

  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ProjectionCalculationStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.Projection;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 12/07/16.
 */
public class ProjectionCalculationStep extends AbstractExecutionStep {
  protected final Projection projection;

  protected long cost = 0;

  public ProjectionCalculationStep(Projection projection, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.projection = projection;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (prev.isEmpty()) {
      throw new IllegalStateException("Cannot calculate projections without a previous source");
    }

    ResultSet parentRs = prev.get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return parentRs.hasNext();
      }

      @Override
      public Result next() {
        Result item = parentRs.next();
        Object oldCurrent = ctx.getVariable("$current");
        ctx.setVariable("$current", item);
        Result result = calculateProjections(ctx, item);
        ctx.setVariable("$current", oldCurrent);
        return result;
      }

      @Override
      public void close() {
        parentRs.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private Result calculateProjections(CommandContext ctx, Result next) {
    long begin = profilingEnabled ? System.nanoTime() : 0;
    try {
      return this.projection.calculateSingle(ctx, next);
    } finally {
      if (profilingEnabled) {
        cost += (System.nanoTime() - begin);
      }
    }
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);

    String result = spaces + "+ CALCULATE PROJECTIONS";
    if (profilingEnabled) {
      result += " (" + getCostFormatted() + ")";
    }
    result += ("\n" + spaces + "  " + projection.toString() + "");
    return result;
  }

  @Override
  public long getCost() {
    return cost;
  }

  @Override
  public boolean canBeCached() {
    return true;
  }

  @Override
  public ExecutionStep copy(CommandContext ctx) {
    return new ProjectionCalculationStep(projection.copy(), ctx, profilingEnabled);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/MatchReverseEdgeTraverser.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.parser.MatchPathItem;
import com.arcadedb.query.sql.parser.Rid;
import com.arcadedb.query.sql.parser.WhereClause;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by luigidellaquila on 15/10/16.
 */
public class MatchReverseEdgeTraverser extends MatchEdgeTraverser {

  private final String startingPointAlias;
  private final String endPointAlias;

  public MatchReverseEdgeTraverser(Result lastUpstreamRecord, EdgeTraversal edge) {
    super(lastUpstreamRecord, edge);
    this.startingPointAlias = edge.edge.in.alias;
    this.endPointAlias = edge.edge.out.alias;
  }

  protected String targetClassName(MatchPathItem item, CommandContext iCommandContext) {
    return edge.getLeftClass();
  }

  protected String targetClusterName(MatchPathItem item, CommandContext iCommandContext) {
    return edge.getLeftCluster();
  }

  protected Rid targetRid(MatchPathItem item, CommandContext iCommandContext) {
    return edge.getLeftRid();
  }

  protected WhereClause getTargetFilter(MatchPathItem item) {
    return edge.getLeftFilter();
  }

  @Override
  protected Iterable<ResultInternal> traversePatternEdge(Identifiable startingPoint, CommandContext iCommandContext) {

    Object qR = this.item.getMethod().executeReverse(startingPoint, iCommandContext);
    if (qR == null) {
      return Collections.emptyList();
    }
    if (qR instanceof ResultInternal) {
      return Collections.singleton((ResultInternal) qR);
    }
    if (qR instanceof Document) {
      return Collections.singleton(new ResultInternal((Document) qR));
    }
    if (qR instanceof Iterable) {
      Iterable iterable = (Iterable) qR;
      List<ResultInternal> result = new ArrayList<>();
      for (Object o : iterable) {
        if (o instanceof Document) {
          result.add(new ResultInternal((Document) o));
        } else if (o instanceof ResultInternal) {
          result.add((ResultInternal) o);
        } else if (o == null) {
        } else {
          throw new UnsupportedOperationException();
        }
      }
      return result;
    }
    return Collections.EMPTY_LIST;
  }

  @Override
  protected String getStartingPointAlias() {
    return this.startingPointAlias;
  }

  @Override
  protected String getEndpointAlias() {
    return endPointAlias;
  }

}


<|start_filename|>integration/src/main/java/com/arcadedb/integration/importer/Source.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.integration.importer;

import com.arcadedb.log.LogManager;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import java.util.logging.Level;

public class Source {
  public final  String                                      url;
  public        InputStream                                 inputStream;
  public final  long                                        totalSize;
  public final  boolean                                     compressed;
  private final com.arcadedb.utility.Callable<Void, Source> resetCallback;
  private final Callable<Void>                              closeCallback;

  public Source(final String url, final InputStream inputStream, final long totalSize, final boolean compressed,
      final com.arcadedb.utility.Callable<Void, Source> resetCallback, final Callable<Void> closeCallback) {
    this.url = url;
    this.inputStream = inputStream;
    this.totalSize = totalSize;
    this.compressed = compressed;
    this.resetCallback = resetCallback;
    this.closeCallback = closeCallback;
  }

  public void reset() throws IOException {
    if (resetCallback != null)
      try {
        resetCallback.call(this);
      } catch (Exception e) {
        LogManager.instance().log(this, Level.SEVERE, "Error on resetting source %s", e, this);
      }
  }

  public void close() {
    try {
      closeCallback.call();
    } catch (Exception e) {
      LogManager.instance().log(this, Level.SEVERE, "Error on closing source %s", e, this);
    }
  }

  @Override
  public String toString() {
    return url + " (compressed=" + compressed + " size=" + totalSize + ")";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/graph/Edge.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.graph;

import com.arcadedb.database.Document;
import com.arcadedb.database.RID;

/**
 * An Edge represents the connection between two vertices in a Property Graph. The edge can have properties and point to the same vertex.
 * The direction of the edge goes from the source vertex to the destination vertex. By default edges are bidirectional, that means they can be traversed from
 * both sides. Unidirectional edges can only be traversed from the direction they were created, never backwards. Edges can be Immutable (read-only) and Mutable.
 *
 * @author <NAME> (<EMAIL>)
 * @see Vertex
 */
public interface Edge extends Document {
  byte RECORD_TYPE = 2;

  MutableEdge modify();

  RID getOut();

  Vertex getOutVertex();

  RID getIn();

  Vertex getInVertex();

  Vertex getVertex(Vertex.DIRECTION iDirection);

  @Override
  default Edge asEdge() {
    return this;
  }

  @Override
  default Edge asEdge(final boolean loadContent) {
    return this;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/GuaranteeEmptyCountStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.ProjectionItem;

import java.util.Map;
import java.util.Optional;

public class GuaranteeEmptyCountStep extends AbstractExecutionStep {

    private final ProjectionItem item;
    private boolean executed = false;

    public GuaranteeEmptyCountStep(
            ProjectionItem oProjectionItem, CommandContext ctx, boolean enableProfiling) {
        super(ctx, enableProfiling);
        this.item = oProjectionItem;
    }

    @Override
    public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
        if (prev.isEmpty()) {
            throw new IllegalStateException("filter step requires a previous step");
        }
        ResultSet upstream = prev.get().syncPull(ctx, nRecords);
        return new ResultSet() {
            @Override
            public boolean hasNext() {
                if (!executed) {
                    return true;
                }

                return upstream.hasNext();
            }

            @Override
            public Result next() {
                if (!hasNext()) {
                    throw new IllegalStateException();
                }

                try {
                    if (upstream.hasNext()) {
                        return upstream.next();
                    }
                    ResultInternal result = new ResultInternal();
                    result.setProperty(item.getProjectionAliasAsString(), 0L);
                    return result;
                } finally {
                    executed = true;
                }
            }

            @Override
            public void close() {
                prev.get().close();
            }

            @Override
            public Optional<ExecutionPlan> getExecutionPlan() {
                return Optional.empty();
            }

            @Override
            public Map<String, Long> getQueryStats() {
                return null;
            }
        };
    }

    @Override
    public ExecutionStep copy(CommandContext ctx) {
        return new GuaranteeEmptyCountStep(item.copy(), ctx, profilingEnabled);
    }

    public boolean canBeCached() {
        return true;
    }

    @Override
    public String prettyPrint(int depth, int indent) {
        return ExecutionStepInternal.getIndent(depth, indent) + "+ GUARANTEE FOR ZERO COUNT ";
    }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ResultSet.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Record;
import com.arcadedb.graph.Edge;
import com.arcadedb.graph.Vertex;

import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Result set returned from queries. This class implements can be used as an Iterator of Result.
 */
public interface ResultSet extends Spliterator<Result>, Iterator<Result>, AutoCloseable {

  @Override
  boolean hasNext();

  @Override
  Result next();

  default void remove() {
    throw new UnsupportedOperationException();
  }

  void close();

  Optional<ExecutionPlan> getExecutionPlan();

  Map<String, Long> getQueryStats();

  default void reset() {
    throw new UnsupportedOperationException("Implement RESET on " + getClass().getSimpleName());
  }

  default boolean tryAdvance(final Consumer<? super Result> action) {
    if (hasNext()) {
      action.accept(next());
      return true;
    }
    return false;
  }

  default long countEntries() {
    long tot = 0;

    while (hasNext()) {
      next();
      tot++;
    }

    return tot;
  }

  default void forEachRemaining(final Consumer<? super Result> action) {
    Spliterator.super.forEachRemaining(action);
  }

  default ResultSet trySplit() {
    return null;
  }

  default long estimateSize() {
    return Long.MAX_VALUE;
  }

  default int characteristics() {
    return ORDERED;
  }

  /**
   * Returns the result set as a stream. IMPORTANT: the stream consumes the result set!
   *
   * @return
   */
  default Stream<Result> stream() {
    return StreamSupport.stream(this, false);
  }

  /**
   * Returns the result set as a stream of elements (filters only the results that are elements - where the isElement() method
   * returns true). IMPORTANT: the stream consumes the result set!
   *
   * @return
   */

  default Stream<Record> elementStream() {
    return StreamSupport.stream(new Spliterator<>() {
      @Override
      public boolean tryAdvance(final Consumer<? super Record> action) {
        while (hasNext()) {
          final Result elem = next();
          if (elem.isElement()) {
            action.accept(elem.getElement().get());
            return true;
          }
        }
        return false;
      }

      @Override
      public Spliterator<Record> trySplit() {
        return null;
      }

      @Override
      public long estimateSize() {
        return Long.MAX_VALUE;
      }

      @Override
      public int characteristics() {
        return ORDERED;
      }
    }, false);
  }

  /**
   * Returns the result set as a stream of vertices (filters only the results that are vertices - where the isVertex() method
   * returns true). IMPORTANT: the stream consumes the result set!
   *
   * @return
   */

  default Stream<Vertex> vertexStream() {
    return StreamSupport.stream(new Spliterator<>() {
      @Override
      public boolean tryAdvance(final Consumer<? super Vertex> action) {
        while (hasNext()) {
          final Result elem = next();
          if (elem.isVertex()) {
            action.accept(elem.getVertex().get());
            return true;
          }
        }
        return false;
      }

      @Override
      public Spliterator<Vertex> trySplit() {
        return null;
      }

      @Override
      public long estimateSize() {
        return Long.MAX_VALUE;
      }

      @Override
      public int characteristics() {
        return ORDERED;
      }
    }, false);
  }

  /**
   * Returns the result set as a stream of vertices (filters only the results that are edges - where the isEdge() method
   * returns true). IMPORTANT: the stream consumes the result set!
   *
   * @return
   */

  default Stream<Edge> edgeStream() {
    return StreamSupport.stream(new Spliterator<>() {
      @Override
      public boolean tryAdvance(final Consumer<? super Edge> action) {
        while (hasNext()) {
          final Result nextElem = next();
          if (nextElem != null && nextElem.isEdge()) {
            action.accept(nextElem.getEdge().get());
            return true;
          }
        }
        return false;
      }

      @Override
      public Spliterator<Edge> trySplit() {
        return null;
      }

      @Override
      public long estimateSize() {
        return Long.MAX_VALUE;
      }

      @Override
      public int characteristics() {
        return ORDERED;
      }
    }, false);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/CountStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;

/**
 * Counts the records from the previous steps.
 * Returns a record with a single property, called "count" containing the count of records received from pervious steps
 *
 * @author <NAME> (luigi.dellaquila-(at)-gmail.com)
 */
public class CountStep extends AbstractExecutionStep {

  private long cost = 0;

  boolean executed = false;

  /**
   * @param ctx              the query context
   * @param profilingEnabled true to enable the profiling of the execution (for SQL PROFILE)
   */
  public CountStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (executed) {
      return new InternalResultSet();
    }
    ResultInternal resultRecord = new ResultInternal();
    executed = true;
    long count = 0;
    while (true) {
      ResultSet prevResult = getPrev().get().syncPull(ctx, nRecords);

      if (!prevResult.hasNext()) {
        long begin = profilingEnabled ? System.nanoTime() : 0;
        try {
          InternalResultSet result = new InternalResultSet();
          resultRecord.setProperty("count", count);
          result.add(resultRecord);
          return result;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }
      while (prevResult.hasNext()) {
        count++;
        prevResult.next();
      }
    }
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ COUNT");
    if (profilingEnabled) {
      result.append(" (").append(getCostFormatted()).append(")");
    }
    return result.toString();
  }

  @Override
  public long getCost() {
    return cost;
  }

  @Override
  public ExecutionStep copy(CommandContext ctx) {
    return new CountStep(ctx, profilingEnabled);
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/WhileBlockExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author <NAME> (<EMAIL>-(at)-orient<EMAIL>)
 */
public class WhileBlockExecutionTest extends TestHelper {
  public WhileBlockExecutionTest() {
    autoStartTx = true;
  }

  @Test
  public void testPlain() {

    String className = "testPlain";

    database.getSchema().createDocumentType(className);

    String script = "";
    script += "LET $i = 0;";
    script += "WHILE ($i < 3){\n";
    script += "  insert into " + className + " set value = $i;\n";
    script += "  LET $i = $i + 1;";
    script += "}";
    script += "SELECT FROM " + className + ";";

    ResultSet results = database.execute("sql", script);

    int tot = 0;
    int sum = 0;
    while (results.hasNext()) {
      Result item = results.next();
      sum += (Integer) item.getProperty("value");
      tot++;
    }
    Assertions.assertEquals(3, tot);
    Assertions.assertEquals(3, sum);
    results.close();
  }

  @Test
  public void testReturn() {
    String className = "testReturn";

    database.getSchema().createDocumentType(className);

    String script = "";
    script += "LET $i = 0;";
    script += "WHILE ($i < 3){\n";
    script += "  insert into " + className + " set value = $i;\n";
    script += "  IF ($i = 1) {";
    script += "    RETURN;";
    script += "  }";
    script += "  LET $i = $i + 1;";
    script += "}";

    ResultSet results = database.execute("sql", script);
    results.close();
    results = database.query("sql", "SELECT FROM " + className);

    int tot = 0;
    int sum = 0;
    while (results.hasNext()) {
      Result item = results.next();
      sum += (Integer) item.getProperty("value");
      tot++;
    }
    Assertions.assertEquals(2, tot);
    Assertions.assertEquals(1, sum);
    results.close();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/InfoExecutionStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by luigidellaquila on 19/12/16.
 */
public class InfoExecutionStep implements ExecutionStep {

  String              name;
  String              type;
  String              javaType;
  String              targetNode;
  String              description;
  long                cost;
  final List<ExecutionStep> subSteps = new ArrayList<>();

  @Override public String getName() {
    return name;
  }

  @Override public String getType() {
    return type;
  }

  @Override public String getTargetNode() {
    return targetNode;
  }

  @Override public String getDescription() {
    return description;
  }

  @Override public List<ExecutionStep> getSubSteps() {
    return subSteps;
  }

  @Override public long getCost() {
    return cost;
  }

  @Override public Result toResult() {
    return null;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setType(String type) {
    this.type = type;
  }

  public void setTargetNode(String targetNode) {
    this.targetNode = targetNode;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public void setCost(long cost) {
    this.cost = cost;
  }

  public String getJavaType() {
    return javaType;
  }

  public void setJavaType(String javaType) {
    this.javaType = javaType;
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/CheckClassTypeStepTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.schema.DocumentType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class CheckClassTypeStepTest {

  @Test
  public void shouldCheckSubclasses() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      BasicCommandContext context = new BasicCommandContext();
      context.setDatabase(db);
      DocumentType parentClass = TestHelper.createRandomType(db);
      DocumentType childClass = TestHelper.createRandomType(db).addSuperType(parentClass);
      CheckClassTypeStep step = new CheckClassTypeStep(childClass.getName(), parentClass.getName(), context, false);

      ResultSet result = step.syncPull(context, 20);
      Assertions.assertEquals(0, result.stream().count());
    });
  }

  @Test
  public void shouldCheckOneType() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      BasicCommandContext context = new BasicCommandContext();
      context.setDatabase(db);
      String className = TestHelper.createRandomType(db).getName();
      CheckClassTypeStep step = new CheckClassTypeStep(className, className, context, false);

      ResultSet result = step.syncPull(context, 20);
      Assertions.assertEquals(0, result.stream().count());
    });
  }

  @Test
  public void shouldThrowExceptionWhenClassIsNotParent() throws Exception {
    try {
      TestHelper.executeInNewDatabase((db) -> {
        BasicCommandContext context = new BasicCommandContext();
        context.setDatabase(db);
        CheckClassTypeStep step = new CheckClassTypeStep(TestHelper.createRandomType(db).getName(), TestHelper.createRandomType(db).getName(), context, false);

        step.syncPull(context, 20);
      });
      Assertions.fail("Expected CommandExecutionException");
    } catch (CommandExecutionException e) {
      // OK
    }
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/method/misc/SQLMethodRemoveAll.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.method.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.MultiValue;

/**
 * Remove all the occurrences of elements from a collection.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 * @see SQLMethodRemove
 */
public class SQLMethodRemoveAll extends AbstractSQLMethod {

    public static final String NAME = "removeall";

    public SQLMethodRemoveAll() {
        super(NAME, 1, -1);
    }

    @Override
    public Object execute(Object self,
                          final Identifiable currentRecord,
                          final CommandContext context,
                          Object result,
                          final Object[] params) {
        if (params != null &&
                params.length > 0 &&
                params[0] != null) {
            Object[] arguments = MultiValue.array(params, Object.class, iArgument -> {
                if (iArgument instanceof String &&
                        ((String) iArgument).startsWith("$")) {
                    return context.getVariable((String) iArgument);
                }
                return iArgument;
            });
            for (Object o : arguments) {
                result = MultiValue.remove(result, o, true);
            }
        }

        return result;
    }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/coll/SQLFunctionList.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.coll;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.MultiValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * This operator add an item in a list. The list accepts duplicates.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLFunctionList extends SQLFunctionMultiValueAbstract<List<Object>> {
  public static final String NAME = "list";

  public SQLFunctionList() {
    super(NAME, 1, -1);
  }

  public Object execute( Object iThis, final Identifiable iCurrentRecord, Object iCurrentResult, final Object[] iParams,
      CommandContext iContext) {
    if (iParams.length > 1)
      // IN LINE MODE
      context = new ArrayList<>();

    for (Object value : iParams) {
      if (value != null) {
        if (iParams.length == 1 && context == null)
          // AGGREGATION MODE (STATEFULL)
          context = new ArrayList<Object>();

        if (value instanceof Map)
          context.add(value);
        else
          MultiValue.add(context, value);
      }
    }
    return context;
  }

  public String getSyntax() {
    return "list(<value>*)";
  }

  public boolean aggregateResults(final Object[] configuredParameters) {
    return false;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/OptionalMatchEdgeTraverser.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by luigidellaquila on 17/10/16.
 */
public class OptionalMatchEdgeTraverser extends MatchEdgeTraverser {
  public static final Result EMPTY_OPTIONAL = new ResultInternal();

  public OptionalMatchEdgeTraverser(Result lastUpstreamRecord, EdgeTraversal edge) {
    super(lastUpstreamRecord, edge);
  }

  protected void init(CommandContext ctx) {
    if (downstream == null) {
      super.init(ctx);
      if (!downstream.hasNext()) {
        List x = new ArrayList();
        x.add(EMPTY_OPTIONAL);
        downstream = x.iterator();
      }
    }
  }

  public Result next(CommandContext ctx) {
    init(ctx);
    if (!downstream.hasNext()) {
      throw new IllegalStateException();
    }

    String endPointAlias = getEndpointAlias();
    Object prevValue = sourceRecord.getProperty(endPointAlias);
    ResultInternal next = downstream.next();

    if (isEmptyOptional(prevValue)) {
      return sourceRecord;
    }
    if (!isEmptyOptional(next)) {
      if (prevValue != null && !equals(prevValue, next.getElement().get())) {
        return null;
      }
    }

    ResultInternal result = new ResultInternal();
    for (String prop : sourceRecord.getPropertyNames()) {
      result.setProperty(prop, sourceRecord.getProperty(prop));
    }
    result.setProperty(endPointAlias, next.getElement().map(x -> toResult(x)).orElse(null));
    return result;
  }

  public static boolean isEmptyOptional(Object elem) {
    if (elem == EMPTY_OPTIONAL) {
      return true;
    }
    return elem instanceof Result && EMPTY_OPTIONAL == ((Result) elem).getElement().orElse(null);

  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/QueryStats.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Database;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * This class contains statistics about graph structure and query execution.
 * <p>
 * To obtain a copy of this object, use
 *
 * @author <NAME>
 */
public class QueryStats {

  public final Map<String, Long> stats = new ConcurrentHashMap<>();

  public static QueryStats get(Database db) {
    return new QueryStats();//TODO
  }

  public long getIndexStats(String indexName, int params, boolean range, boolean additionalRange) {
    String key = generateKey("INDEX", indexName, String.valueOf(params), String.valueOf(range), String.valueOf(additionalRange));
    Long val = stats.get(key);
    if (val != null) {
      return val;
    }
    return -1;
  }

  public void pushIndexStats(String indexName, int params, boolean range, boolean additionalRange, Long value) {
    String key = generateKey("INDEX", indexName, String.valueOf(params), String.valueOf(range), String.valueOf(additionalRange));
    pushValue(key, value);
  }

  public long getAverageOutEdgeSpan(String vertexClass, String edgeClass) {
    String key = generateKey(vertexClass, "-", edgeClass, "->");
    Long val = stats.get(key);
    if (val != null) {
      return val;
    }
    return -1;
  }

  public long getAverageInEdgeSpan(String vertexClass, String edgeClass) {
    String key = generateKey(vertexClass, "<-", edgeClass, "-");
    Long val = stats.get(key);
    if (val != null) {
      return val;
    }
    return -1;
  }

  public long getAverageBothEdgeSpan(String vertexClass, String edgeClass) {
    String key = generateKey(vertexClass, "-", edgeClass, "-");
    Long val = stats.get(key);
    if (val != null) {
      return val;
    }
    return -1;
  }

  public void pushAverageOutEdgeSpan(String vertexClass, String edgeClass, Long value) {
    String key = generateKey(vertexClass, "-", edgeClass, "->");
    pushValue(key, value);
  }

  public void pushAverageInEdgeSpan(String vertexClass, String edgeClass, Long value) {
    String key = generateKey(vertexClass, "<-", edgeClass, "-");
    pushValue(key, value);
  }

  public void pushAverageBothEdgeSpan(String vertexClass, String edgeClass, Long value) {
    String key = generateKey(vertexClass, "-", edgeClass, "-");
    pushValue(key, value);
  }

  private void pushValue(String key, Long value) {
    if (value == null) {
      return;
    }
    Long val = stats.get(key);

    if (val == null) {
      val = value;
    } else {
      //refine this ;-)
      val = ((Double) ((val * .9) + (value * .1))).longValue();
      if (value > 0 && val == 0) {
        val = 1L;
      }
    }
    stats.put(key, val);
  }

  protected String generateKey(String... keys) {
    StringBuilder result = new StringBuilder();
    for (String s : keys) {
      result.append(".->");
      result.append(s);
    }
    return result.toString();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/index/RangeIndex.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.index;

/**
 * Basic Range Index interface. Supports range queries and iterations.
 */
public interface RangeIndex extends Index {
  /**
   * The returning iterator does not skip deleted entries and it might contains duplicated entries.
   * WARNING: this method does not read pending changes in transaction.
   */
  IndexCursor iterator(boolean ascendingOrder);

  /**
   * The returning iterator does not skip deleted entries and it might contains duplicated entries.
   * WARNING: this method does not read pending changes in transaction.
   */
  IndexCursor iterator(boolean ascendingOrder, Object[] fromKeys, boolean inclusive);

  /**
   * The returning iterator does not skip deleted entries and it might contains duplicated entries.
   * WARNING: this method does not read pending changes in transaction.
   */
  IndexCursor range(boolean ascending, Object[] beginKeys, boolean beginKeysInclusive, Object[] endKeys, boolean endKeysInclusive);
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/UpdateStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Type;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author <NAME> (l.dellaquila-(at)-orientdb.com)
 */
public class UpdateStatementExecutionTest extends TestHelper {
  private String className;

  public UpdateStatementExecutionTest() {
    autoStartTx = true;
  }

  @Override
  public void beginTest() {
    DocumentType clazz = database.getSchema().createDocumentType("OUpdateStatementExecutionTest");
    className = clazz.getName();

    for (int i = 0; i < 10; i++) {
      MutableDocument doc = database.newDocument(className);
      doc.set("name", "name" + i);
      doc.set("surname", "surname" + i);
      doc.set("number", 4L);

      List<String> tagsList = new ArrayList<>();
      tagsList.add("foo");
      tagsList.add("bar");
      tagsList.add("baz");
      doc.set("tagsList", tagsList);

      Map<String, String> tagsMap = new HashMap<>();
      tagsMap.put("foo", "foo");
      tagsMap.put("bar", "bar");
      tagsMap.put("baz", "baz");
      doc.set("tagsMap", tagsMap);

      doc.save();
    }
  }

  @Test
  public void testSetString() {
    ResultSet result = database.command("sql", "update " + className + " set surname = 'foo'");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));

    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals("foo", item.getProperty("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testCopyField() {
    ResultSet result = database.command("sql", "update " + className + " set surname = name");

    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals((Object) item.getProperty("name"), item.getProperty("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testSetExpression() {
    ResultSet result = database.command("sql", "update " + className + " set surname = 'foo'+name ");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals("foo" + item.getProperty("name"), item.getProperty("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testConditionalSet() {
    ResultSet result = database.command("sql", "update " + className + " set surname = 'foo' where name = 'name3'");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    boolean found = false;
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      if ("name3".equals(item.getProperty("name"))) {
        Assertions.assertEquals("foo", item.getProperty("surname"));
        found = true;
      }
    }
    Assertions.assertTrue(found);
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testSetOnList() {
    ResultSet result = database.command("sql", "update " + className + " set tagsList[0] = 'abc' where name = 'name3'");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    boolean found = false;
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      if ("name3".equals(item.getProperty("name"))) {
        List<String> tags = new ArrayList<>();
        tags.add("abc");
        tags.add("bar");
        tags.add("baz");
        Assertions.assertEquals(tags, item.getProperty("tagsList"));
        found = true;
      }
    }
    Assertions.assertTrue(found);
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  private void printExecutionPlan(ResultSet result) {
    //ExecutionPlanPrintUtils.printExecutionPlan(result);
  }

  @Test
  public void testSetOnList2() {
    ResultSet result = database.command("sql", "update " + className + " set tagsList[6] = 'abc' where name = 'name3'");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    boolean found = false;
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      if ("name3".equals(item.getProperty("name"))) {
        List<String> tags = new ArrayList<>();
        tags.add("foo");
        tags.add("bar");
        tags.add("baz");
        tags.add(null);
        tags.add(null);
        tags.add(null);
        tags.add("abc");
        Assertions.assertEquals(tags, item.getProperty("tagsList"));
        found = true;
      }
    }
    Assertions.assertTrue(found);
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testSetOnMap() {
    ResultSet result = database.command("sql", "update " + className + " set tagsMap['foo'] = 'abc' where name = 'name3'");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    boolean found = false;
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      if ("name3".equals(item.getProperty("name"))) {
        Map<String, String> tags = new HashMap<>();
        tags.put("foo", "abc");
        tags.put("bar", "bar");
        tags.put("baz", "baz");
        Assertions.assertEquals(tags, item.getProperty("tagsMap"));
        found = true;
      } else {
        Map<String, String> tags = new HashMap<>();
        tags.put("foo", "foo");
        tags.put("bar", "bar");
        tags.put("baz", "baz");
        Assertions.assertEquals(tags, item.getProperty("tagsMap"));
      }
    }
    Assertions.assertTrue(found);
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testPlusAssign() {
    ResultSet result = database.command("sql", "update " + className + " set name += 'foo', newField += 'bar', number += 5");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertTrue(item.getProperty("name").toString().endsWith("foo")); // test concatenate string to string
      Assertions.assertEquals(8, item.getProperty("name").toString().length());
      Assertions.assertEquals("bar", item.getProperty("newField")); // test concatenate null to string
      Assertions.assertEquals((Object) 9L, item.getProperty("number")); // test sum numbers
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testMinusAssign() {
    ResultSet result = database.command("sql", "update " + className + " set number -= 5");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals((Object) (-1L), item.getProperty("number"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testStarAssign() {
    ResultSet result = database.command("sql", "update " + className + " set number *= 5");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals((Object) 20L, item.getProperty("number"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testSlashAssign() {
    ResultSet result = database.command("sql", "update " + className + " set number /= 2");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals((Object) 2L, item.getProperty("number"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemove() {
    ResultSet result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertNotNull(item.getProperty("surname"));
    }

    result.close();
    result = database.command("sql", "update " + className + " remove surname");
    for (int i = 0; i < 1; i++) {
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertNull(item.getProperty("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testContent() {

    ResultSet result = database.command("sql", "update " + className + " content {'name': 'foo', 'secondName': 'bar'}");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals("foo", item.getProperty("name"));
      Assertions.assertEquals("bar", item.getProperty("secondName"));
      Assertions.assertNull(item.getProperty("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testMerge() {

    ResultSet result = database.command("sql", "update " + className + " merge {'name': 'foo', 'secondName': 'bar'}");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 10L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals("foo", item.getProperty("name"));
      Assertions.assertEquals("bar", item.getProperty("secondName"));
      Assertions.assertTrue(item.getProperty("surname").toString().startsWith("surname"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testUpsert1() {

    ResultSet result = database.command("sql", "update " + className + " set foo = 'bar' upsert where name = 'name1'");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      String name = item.getProperty("name");
      Assertions.assertNotNull(name);
      if ("name1".equals(name)) {
        Assertions.assertEquals("bar", item.getProperty("foo"));
      } else {
        Assertions.assertNull(item.getProperty("foo"));
      }
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testUpsertAndReturn() {

    ResultSet result = database.command("sql", "update " + className + " set foo = 'bar' upsert  return after  where name = 'name1' ");

    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals("bar", item.getProperty("foo"));
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testUpsert2() {

    ResultSet result = database.command("sql", "update " + className + " set foo = 'bar' upsert where name = 'name11'");
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals((Object) 1L, item.getProperty("count"));
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    for (int i = 0; i < 11; i++) {
      Assertions.assertTrue(result.hasNext());
      item = result.next();
      Assertions.assertNotNull(item);
      String name = item.getProperty("name");
      Assertions.assertNotNull(name);
      if ("name11".equals(name)) {
        Assertions.assertEquals("bar", item.getProperty("foo"));
      } else {
        Assertions.assertNull(item.getProperty("foo"));
      }
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemove1() {
    String className = "overridden" + this.className;

    DocumentType clazz = database.getSchema().createDocumentType(className);
    clazz.createProperty("theProperty", Type.LIST);

    MutableDocument doc = database.newDocument(className);
    List theList = new ArrayList();
    for (int i = 0; i < 10; i++) {
      theList.add("n" + i);
    }
    doc.set("theProperty", theList);

    doc.save();

    ResultSet result = database.command("sql", "update " + className + " remove theProperty[0]");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    Assertions.assertTrue(result.hasNext());
    item = result.next();
    Assertions.assertNotNull(item);
    List ls = item.getProperty("theProperty");
    Assertions.assertNotNull(ls);
    Assertions.assertEquals(9, ls.size());
    Assertions.assertFalse(ls.contains("n0"));
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemove2() {
    String className = "overridden" + this.className;
    DocumentType clazz = database.getSchema().createDocumentType(className);
    clazz.createProperty("theProperty", Type.LIST);

    MutableDocument doc = database.newDocument(className);
    List theList = new ArrayList();
    for (int i = 0; i < 10; i++) {
      theList.add("n" + i);
    }
    doc.set("theProperty", theList);

    doc.save();

    ResultSet result = database.command("sql", "update " + className + " remove theProperty[0, 1, 3]");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    Assertions.assertTrue(result.hasNext());
    item = result.next();
    Assertions.assertNotNull(item);
    List ls = item.getProperty("theProperty");

    Assertions.assertNotNull(ls);
    Assertions.assertEquals(ls.size(), 7);
    Assertions.assertFalse(ls.contains("n0"));
    Assertions.assertFalse(ls.contains("n1"));
    Assertions.assertTrue(ls.contains("n2"));
    Assertions.assertFalse(ls.contains("n3"));
    Assertions.assertTrue(ls.contains("n4"));

    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemove3() {
    String className = "overriden" + this.className;
    DocumentType clazz = database.getSchema().createDocumentType(className);
    clazz.createProperty("theProperty", Type.EMBEDDED);

    MutableDocument doc = database.newDocument(className);
    MutableDocument emb = database.newDocument(className);
    emb.set("sub", "foo");
    emb.set("aaa", "bar");
    doc.set("theProperty", emb);

    doc.save();

    ResultSet result = database.command("sql", "update " + className + " remove theProperty.sub");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertFalse(result.hasNext());
    result.close();

    result = database.query("sql", "SElect from " + className);
    Assertions.assertTrue(result.hasNext());
    item = result.next();
    Assertions.assertNotNull(item);
    ResultInternal ls = item.getProperty("theProperty");
    Assertions.assertNotNull(ls);
    Assertions.assertFalse(ls.getPropertyNames().contains("sub"));
    Assertions.assertEquals("bar", ls.getProperty("aaa"));
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemoveFromMapSquare() {

    database.command("sql", "UPDATE " + className + " REMOVE tagsMap[\"bar\"]").close();

    ResultSet result = database.query("sql", "SELECT tagsMap FROM " + className);
    printExecutionPlan(result);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals(2, ((Map) item.getProperty("tagsMap")).size());
      Assertions.assertFalse(((Map) item.getProperty("tagsMap")).containsKey("bar"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testRemoveFromMapEquals() {

    database.command("sql", "UPDATE " + className + " REMOVE tagsMap = \"bar\"").close();

    ResultSet result = database.query("sql", "SELECT tagsMap FROM " + className);
    printExecutionPlan(result);
    for (int i = 0; i < 10; i++) {
      Assertions.assertTrue(result.hasNext());
      Result item = result.next();
      Assertions.assertNotNull(item);
      Assertions.assertEquals(2, ((Map) item.getProperty("tagsMap")).size());
      Assertions.assertFalse(((Map) item.getProperty("tagsMap")).containsKey("bar"));
    }
    Assertions.assertFalse(result.hasNext());
    result.close();
  }

  @Test
  public void testReturnBefore() {
    ResultSet result = database.command("sql", "update " + className + " set name = 'foo' RETURN BEFORE where name = 'name1'");
    printExecutionPlan(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertNotNull(item);
    Assertions.assertEquals("name1", item.getProperty("name"));

    Assertions.assertFalse(result.hasNext());
    result.close();
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/text/SQLMethodHash.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.text;

import com.arcadedb.database.Identifiable;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.method.misc.AbstractSQLMethod;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Hash a string supporting multiple algorithm, all those supported by JVM
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLMethodHash extends AbstractSQLMethod {

  public static final String NAME = "hash";

  public static final String HASH_ALGORITHM = "SHA-256";

  public SQLMethodHash() {
    super(NAME, 0, 1);
  }

  @Override
  public String getSyntax() {
    return "hash([<algorithm>])";
  }

  @Override
  public Object execute(final Object iThis, final Identifiable iCurrentRecord, final CommandContext iContext, final Object ioResult,
      final Object[] iParams) {
    if (iThis == null)
      return null;

    final String algorithm = iParams.length > 0 ? iParams[0].toString() : HASH_ALGORITHM;
    try {
      return createHash(iThis.toString(), algorithm);

    } catch (NoSuchAlgorithmException e) {
      throw new CommandExecutionException("hash(): algorithm '" + algorithm + "' is not supported", e);
    } catch (UnsupportedEncodingException e) {
      throw new CommandExecutionException("hash(): encoding 'UTF-8' is not supported", e);
    }
  }

  public static String createHash(final String iInput, String algorithm)
      throws NoSuchAlgorithmException, UnsupportedEncodingException {
    if (algorithm == null)
      algorithm = HASH_ALGORITHM;

    final MessageDigest msgDigest = MessageDigest.getInstance(algorithm);

    return byteArrayToHexStr(msgDigest.digest(iInput.getBytes(StandardCharsets.UTF_8)));
  }

  public static String byteArrayToHexStr(final byte[] data) {
    if (data == null)
      return null;

    final char[] chars = new char[data.length * 2];
    for (int i = 0; i < data.length; i++) {
      final byte current = data[i];
      final int hi = (current & 0xF0) >> 4;
      final int lo = current & 0x0F;
      chars[2 * i] = (char) (hi < 10 ? ('0' + hi) : ('A' + hi - 10));
      chars[2 * i + 1] = (char) (lo < 10 ? ('0' + lo) : ('A' + lo - 10));
    }
    return new String(chars);
  }

}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/IndexCondPair.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.query.sql.parser.AndBlock;
import com.arcadedb.query.sql.parser.BinaryCondition;

import java.util.Objects;

/**
 * For internal use.
 * It is used to keep info about an index range search,
 * where the main condition has the lower bound and the additional condition has the upper bound on last field only
 */
class IndexCondPair {

  final AndBlock        mainCondition;
  final BinaryCondition additionalRange;

  public IndexCondPair(AndBlock keyCondition, BinaryCondition additionalRangeCondition) {
    this.mainCondition = keyCondition;
    this.additionalRange = additionalRangeCondition;
  }

  @Override public boolean equals(Object o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;

    IndexCondPair that = (IndexCondPair) o;

    if (!Objects.equals(mainCondition, that.mainCondition))
      return false;
    return Objects.equals(additionalRange, that.additionalRange);
  }

  @Override public int hashCode() {
    int result = mainCondition != null ? mainCondition.hashCode() : 0;
    result = 31 * result + (additionalRange != null ? additionalRange.hashCode() : 0);
    return result;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/GetValueFromIndexEntryStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.Identifiable;
import com.arcadedb.database.RID;
import com.arcadedb.exception.RecordNotFoundException;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.log.LogManager;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.stream.Collectors;

/**
 * Created by luigidellaquila on 16/03/17.
 */
public class GetValueFromIndexEntryStep extends AbstractExecutionStep {

  private final int[] filterClusterIds;

  // runtime

  private long cost = 0;

  private ResultSet prevResult = null;

  /**
   * @param ctx              the execution context
   * @param filterClusterIds only extract values from these clusters. Pass null if no filtering is needed
   * @param profilingEnabled enable profiling
   */
  public GetValueFromIndexEntryStep(CommandContext ctx, int[] filterClusterIds, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.filterClusterIds = filterClusterIds;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {

    if (prev.isEmpty()) {
      throw new IllegalStateException("filter step requires a previous step");
    }
    ExecutionStepInternal prevStep = prev.get();

    return new ResultSet() {

      public boolean finished = false;

      Result nextItem = null;
      int fetched = 0;

      @Override
      public boolean hasNext() {

        if (fetched >= nRecords || finished) {
          return false;
        }
        if (nextItem == null) {
          fetchNextItem();
        }

        return nextItem != null;

      }

      @Override
      public Result next() {
        if (fetched >= nRecords || finished) {
          throw new IllegalStateException();
        }
        if (nextItem == null) {
          fetchNextItem();
        }
        if (nextItem == null) {
          throw new IllegalStateException();
        }
        Result result = nextItem;
        nextItem = null;
        fetched++;
        return result;
      }

      private void fetchNextItem() {
        nextItem = null;
        if (finished) {
          return;
        }
        if (prevResult == null) {
          prevResult = prevStep.syncPull(ctx, nRecords);
          if (!prevResult.hasNext()) {
            finished = true;
            return;
          }
        }
        while (!finished) {
          while (!prevResult.hasNext()) {
            prevResult = prevStep.syncPull(ctx, nRecords);
            if (!prevResult.hasNext()) {
              finished = true;
              return;
            }
          }
          Result val = prevResult.next();
          long begin = profilingEnabled ? System.nanoTime() : 0;

          try {
            Object finalVal = val.getProperty("rid");
            if (filterClusterIds != null) {
              if (!(finalVal instanceof Identifiable)) {
                continue;
              }
              RID rid = ((Identifiable) finalVal).getIdentity();
              boolean found = false;
              for (int filterClusterId : filterClusterIds) {
                if (rid.getBucketId() < 0 || filterClusterId == rid.getBucketId()) {
                  found = true;
                  break;
                }
              }
              if (!found) {
                continue;
              }
            }

            if (finalVal instanceof RID) {
              final ResultInternal res = new ResultInternal();
              try {
                res.setElement(((RID) finalVal).asDocument());
                nextItem = res;
              } catch (RecordNotFoundException e) {
                LogManager.instance().log(this, Level.WARNING, "Record %s not found. Skip it from the result set", null, finalVal);
                continue;
              }
            } else if (finalVal instanceof Document) {
              final ResultInternal res = new ResultInternal();
              res.setElement((Document) finalVal);
              nextItem = res;
            } else if (finalVal instanceof Result) {
              nextItem = (Result) finalVal;
            }
            break;
          } finally {
            if (profilingEnabled) {
              cost += (System.nanoTime() - begin);
            }
          }
        }
      }

      @Override
      public void close() {

      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ EXTRACT VALUE FROM INDEX ENTRY";
    if (profilingEnabled) {
      result += " (" + getCostFormatted() + ")";
    }
    if (filterClusterIds != null) {
      result += "\n";
      result += spaces;
      result += "  filtering clusters [";
      result += Arrays.stream(filterClusterIds).boxed().map(x -> "" + x).collect(Collectors.joining(","));
      result += "]";
    }
    return result;
  }

  @Override
  public long getCost() {
    return cost;
  }

  @Override
  public boolean canBeCached() {
    return true;
  }

  @Override
  public ExecutionStep copy(CommandContext ctx) {
    return new GetValueFromIndexEntryStep(ctx, this.filterClusterIds, this.profilingEnabled);
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/DeleteVertexExecutionPlanner.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.query.sql.parser.*;

/**
 * Created by luigidellaquila on 08/08/16.
 */
public class DeleteVertexExecutionPlanner {

  private final FromClause  fromClause;
  private final WhereClause whereClause;
  private final boolean     returnBefore;
  private final Limit       limit;

  public DeleteVertexExecutionPlanner(DeleteVertexStatement stm) {
    this.fromClause = stm.getFromClause() == null ? null : stm.getFromClause().copy();
    this.whereClause = stm.getWhereClause() == null ? null : stm.getWhereClause().copy();
    this.returnBefore = stm.isReturnBefore();
    this.limit = stm.getLimit();
  }

  public DeleteExecutionPlan createExecutionPlan(CommandContext ctx, boolean enableProfiling) {
    DeleteExecutionPlan result = new DeleteExecutionPlan(ctx);

    if (handleIndexAsTarget(result, fromClause.getItem().getIndex(), whereClause, ctx)) {
      if (limit != null) {
        throw new CommandExecutionException("Cannot apply a LIMIT on a delete from index");
      }
      if (returnBefore) {
        throw new CommandExecutionException("Cannot apply a RETURN BEFORE on a delete from index");
      }

    } else {
      handleTarget(result, ctx, this.fromClause, this.whereClause, enableProfiling);
      handleLimit(result, ctx, this.limit, enableProfiling);
    }
    handleCastToVertex(result, ctx, enableProfiling);
    handleDelete(result, ctx, enableProfiling);
    handleReturn(result, ctx, this.returnBefore, enableProfiling);
    return result;
  }

  private boolean handleIndexAsTarget(DeleteExecutionPlan result, IndexIdentifier indexIdentifier, WhereClause whereClause,
      CommandContext ctx) {
    if (indexIdentifier == null) {
      return false;
    }
    throw new CommandExecutionException("DELETE VERTEX FROM INDEX is not supported");
  }

  private void handleDelete(DeleteExecutionPlan result, CommandContext ctx, boolean profilingEnabled) {
    result.chain(new DeleteStep(ctx, profilingEnabled));
  }

//  private void handleUnsafe(DeleteExecutionPlan result, CommandContext ctx, boolean unsafe, boolean profilingEnabled) {
//    if (!unsafe) {
//      result.chain(new CheckSafeDeleteStep(ctx, profilingEnabled));
//    }
//  }

  private void handleReturn(DeleteExecutionPlan result, CommandContext ctx, boolean returnBefore, boolean profilingEnabled) {
    if (!returnBefore) {
      result.chain(new CountStep(ctx, profilingEnabled));
    }
  }

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

  private void handleCastToVertex(DeleteExecutionPlan plan, CommandContext ctx, boolean profilingEnabled) {
    plan.chain(new CastToVertexStep(ctx, profilingEnabled));
  }

  private void handleTarget(UpdateExecutionPlan result, CommandContext ctx, FromClause target, WhereClause whereClause, boolean profilingEnabled) {
    SelectStatement sourceStatement = new SelectStatement(-1);
    sourceStatement.setTarget(target);
    sourceStatement.setWhereClause(whereClause);
    SelectExecutionPlanner planner = new SelectExecutionPlanner(sourceStatement);
    result.chain(new SubQueryStep(planner.createExecutionPlan(ctx, profilingEnabled), ctx, ctx, profilingEnabled));
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/UpsertStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.MutableDocument;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.AndBlock;
import com.arcadedb.query.sql.parser.BooleanExpression;
import com.arcadedb.query.sql.parser.FromClause;
import com.arcadedb.query.sql.parser.WhereClause;

import java.util.List;

/**
 * @author <NAME> (<EMAIL>)
 */
public class UpsertStep extends AbstractExecutionStep {
  private final FromClause  commandTarget;
  private final WhereClause initialFilter;

  boolean applied = false;

  public UpsertStep(FromClause target, WhereClause where, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.commandTarget = target;
    this.initialFilter = where;
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (applied) {
      return getPrev().get().syncPull(ctx, nRecords);
    }
    applied = true;
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    if (upstream.hasNext()) {
      return upstream;
    }
    InternalResultSet result = new InternalResultSet();
    result.add(createNewRecord(commandTarget, initialFilter));
    return result;
  }

  private Result createNewRecord(FromClause commandTarget, WhereClause initialFilter) {
    if (commandTarget.getItem().getIdentifier() == null) {
      throw new CommandExecutionException("Cannot execute UPSERT on target '" + commandTarget + "'");
    }

    MutableDocument doc = ctx.getDatabase().newDocument(commandTarget.getItem().getIdentifier().getStringValue());
    UpdatableResult result = new UpdatableResult(doc);
    if (initialFilter != null) {
      setContent(result, initialFilter);
    }
    return result;
  }

  private void setContent(ResultInternal doc, WhereClause initialFilter) {
    List<AndBlock> flattened = initialFilter.flatten();
    if (flattened.size() == 0) {
      return;
    }
    if (flattened.size() > 1) {
      throw new CommandExecutionException("Cannot UPSERT on OR conditions");
    }
    AndBlock andCond = flattened.get(0);
    for (BooleanExpression condition : andCond.getSubBlocks()) {
      condition.transformToUpdateItem().ifPresent(x -> x.applyUpdate(doc, ctx));
    }
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ INSERT (upsert, if needed)\n" + spaces + "  target: " + commandTarget + "\n" + spaces + "  content: " + initialFilter;
    return result;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/misc/SQLFunctionIf.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.log.LogManager;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.function.SQLFunctionAbstract;

import java.util.logging.Level;

/**
 * Returns different values based on the condition. If it's true the first value is returned, otherwise the second one.
 * <br>
 * Syntax: <blockquote>
 *
 * {@literal if(&lt;field|value|expression&gt;, &lt;return_value_if_true&gt; [,&lt;return_value_if_false&gt;])}
 *
 * </blockquote>
 * <br>
 * <br>
 * Examples: <blockquote>
 *
 * <pre>
 * SELECT <b>if(rich, 'rich', 'poor')</b> FROM ...
 * <br>
 * SELECT <b>if( eval( 'salary &gt; 1000000' ), 'rich', 'poor')</b> FROM ...
 * </pre>
 *
 * </blockquote>
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */

public class SQLFunctionIf extends SQLFunctionAbstract {

  public static final String NAME = "if";

  public SQLFunctionIf() {
    super(NAME);
  }

  @Override
  public Object execute(Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, final Object[] iParams, final CommandContext iContext) {

    boolean result;

    try {
      Object condition = iParams[0];
      if (condition instanceof Boolean)
        result = (Boolean) condition;
      else if (condition instanceof String)
        result = Boolean.parseBoolean(condition.toString());
      else if (condition instanceof Number)
        result = ((Number) condition).intValue() > 0;
      else
        return null;

      return result ? iParams[1] : iParams[2];

    } catch (Exception e) {
      LogManager.instance().log(this, Level.SEVERE, "Error during if execution", e);

      return null;
    }
  }

  @Override
  public String getSyntax() {
    return "if(<field|value|expression>, <return_value_if_true> [,<return_value_if_false>])";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ExecutionStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by luigidellaquila on 20/07/16.
 */
public interface ExecutionStep {

  String getName();

  String getType();

  String getTargetNode();

  String getDescription();

  List<ExecutionStep> getSubSteps();

  /**
   * returns the absolute cost (in nanoseconds) of the execution of this step
   *
   * @return the absolute cost (in nanoseconds) of the execution of this step, -1 if not calculated
   */
  default long getCost() {
    return -1L;
  }

  default Result toResult() {
    ResultInternal result = new ResultInternal();
    result.setProperty("name", getName());
    result.setProperty("type", getType());
    result.setProperty("targetNode", getType());
    result.setProperty(InternalExecutionPlan.JAVA_TYPE, getClass().getName());
    result.setProperty("cost", getCost());
    result.setProperty("subSteps",
        getSubSteps() == null ? null : getSubSteps().stream().map(x -> x.toResult()).collect(Collectors.toList()));
    result.setProperty("description", getDescription());
    return result;
  }

}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/ProfileStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author <NAME> (<EMAIL>-(at)-orientdb.<EMAIL>)
 */
public class ProfileStatementExecutionTest {
  @Test
  public void testProfile() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      db.getSchema().createDocumentType("testProfile");
      db.command("sql", "insert into testProfile set name ='foo'");
      db.command("sql", "insert into testProfile set name ='bar'");

      ResultSet result = db.query("sql", "PROFILE SELECT FROM testProfile WHERE name ='bar'");
      Assertions.assertTrue(result.getExecutionPlan().get().prettyPrint(0, 2).contains("μs"));

      result.close();
    });
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/coll/SQLFunctionDifference.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.coll;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * This operator can work inline. Returns the DIFFERENCE between the collections received as parameters. Works also with no
 * collection values.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLFunctionDifference extends SQLFunctionMultiValueAbstract<Set<Object>> {
  public static final String NAME = "difference";

  public SQLFunctionDifference() {
    super(NAME, 2, -1);
  }

  @SuppressWarnings("unchecked")
  public Object execute( Object iThis, Identifiable iCurrentRecord, Object iCurrentResult, final Object[] iParams,
      CommandContext iContext) {
    if (iParams[0] == null)
      return null;

    // IN-LINE MODE (STATELESS)
    final Set<Object> result = new HashSet<Object>();

    boolean first = true;
    for (Object iParameter : iParams) {
      if (first) {
        if (iParameter instanceof Collection<?>) {
          result.addAll((Collection<Object>) iParameter);
        } else {
          result.add(iParameter);
        }
      } else {
        if (iParameter instanceof Collection<?>) {
          result.removeAll((Collection<Object>) iParameter);
        } else {
          result.remove(iParameter);
        }
      }

      first = false;
    }

    return result;

  }

  public String getSyntax() {
    return "difference(<field>, <field> [, <field]*)";
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/FetchFromClustersExecutionStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;

import java.util.*;

/**
 * Created by luigidellaquila on 21/07/16.
 */
public class FetchFromClustersExecutionStep extends AbstractExecutionStep {

  final   List<ExecutionStep> subSteps;
  private boolean             orderByRidAsc  = false;
  private boolean orderByRidDesc = false;

  ResultSet currentResultSet;
  int       currentStep = 0;

  /**
   * iterates over a class and its subTypes
   *
   * @param bucketIds the clusters
   * @param ctx        the query context
   * @param ridOrder   true to sort by RID asc, false to sort by RID desc, null for no sort.
   */
  public FetchFromClustersExecutionStep(int[] bucketIds, CommandContext ctx, Boolean ridOrder, boolean profilingEnabled) {
    super(ctx, profilingEnabled);

    if (Boolean.TRUE.equals(ridOrder)) {
      orderByRidAsc = true;
    } else if (Boolean.FALSE.equals(ridOrder)) {
      orderByRidDesc = true;
    }

    subSteps = new ArrayList<>();
    sort(bucketIds);
    for (int bucketId : bucketIds) {
      FetchFromClusterExecutionStep step = new FetchFromClusterExecutionStep(bucketId, ctx, profilingEnabled);
      if (orderByRidAsc) {
        step.setOrder(FetchFromClusterExecutionStep.ORDER_ASC);
      } else if (orderByRidDesc) {
        step.setOrder(FetchFromClusterExecutionStep.ORDER_DESC);
      }
      subSteps.add(step);
    }
  }

  private void sort(int[] bucketIds) {
    if (orderByRidAsc) {
      Arrays.sort(bucketIds);
    } else if (orderByRidDesc) {
      Arrays.sort(bucketIds);
      //revert order
      for (int i = 0; i < bucketIds.length / 2; i++) {
        int old = bucketIds[i];
        bucketIds[i] = bucketIds[bucketIds.length - 1 - i];
        bucketIds[bucketIds.length - 1 - i] = old;
      }
    }
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    getPrev().ifPresent(x -> x.syncPull(ctx, nRecords));
    return new ResultSet() {

      int totDispatched = 0;

      @Override
      public boolean hasNext() {
        while (true) {
          if (totDispatched >= nRecords) {
            return false;
          }
          if (currentResultSet == null || !currentResultSet.hasNext()) {
            if (currentStep >= subSteps.size()) {
              return false;
            }
            currentResultSet = ((AbstractExecutionStep) subSteps.get(currentStep)).syncPull(ctx, nRecords);
            if (!currentResultSet.hasNext()) {
              currentResultSet = ((AbstractExecutionStep) subSteps.get(currentStep++)).syncPull(ctx, nRecords);
            }
          }
          if (!currentResultSet.hasNext()) {
            continue;
          }
          return true;
        }
      }

      @Override
      public Result next() {
        while (true) {
          if (totDispatched >= nRecords) {
            throw new IllegalStateException();
          }
          if (currentResultSet == null || !currentResultSet.hasNext()) {
            if (currentStep >= subSteps.size()) {
              throw new IllegalStateException();
            }
            currentResultSet = ((AbstractExecutionStep) subSteps.get(currentStep)).syncPull(ctx, nRecords);
            if (!currentResultSet.hasNext()) {
              currentResultSet = ((AbstractExecutionStep) subSteps.get(currentStep++)).syncPull(ctx, nRecords);
            }
          }
          if (!currentResultSet.hasNext()) {
            continue;
          }
          totDispatched++;
          return currentResultSet.next();
        }
      }

      @Override
      public void close() {
        for (ExecutionStep step : subSteps) {
          ((AbstractExecutionStep) step).close();
        }
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return new HashMap<>();
      }
    };

  }

  @Override
  public void sendTimeout() {
    for (ExecutionStep step : subSteps) {
      ((AbstractExecutionStep) step).sendTimeout();
    }
    prev.ifPresent(p -> p.sendTimeout());
  }

  @Override
  public void close() {
    for (ExecutionStep step : subSteps) {
      ((AbstractExecutionStep) step).close();
    }
    prev.ifPresent(p -> p.close());
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    StringBuilder builder = new StringBuilder();
    String ind = ExecutionStepInternal.getIndent(depth, indent);
    builder.append(ind);
    builder.append("+ FETCH FROM BUCKETSS");
    if (profilingEnabled) {
      builder.append(" (").append(getCostFormatted()).append(")");
    }
    builder.append("\n");
    for (int i = 0; i < subSteps.size(); i++) {
      ExecutionStepInternal step = (ExecutionStepInternal) subSteps.get(i);
      builder.append(step.prettyPrint(depth + 1, indent));
      if (i < subSteps.size() - 1) {
        builder.append("\n");
      }
    }
    return builder.toString();
  }

  @Override
  public List<ExecutionStep> getSubSteps() {
    return subSteps;
  }

  @Override
  public long getCost() {
    return subSteps.stream().map(x -> x.getCost()).reduce((a, b) -> a + b).orElse(-1L);
  }

  @Override
  public Result serialize() {
    ResultInternal result = ExecutionStepInternal.basicSerialize(this);
    result.setProperty("orderByRidAsc", orderByRidAsc);
    result.setProperty("orderByRidDesc", orderByRidDesc);
    return result;
  }

  @Override
  public void deserialize(Result fromResult) {
    try {
      ExecutionStepInternal.basicDeserialize(fromResult, this);
      this.orderByRidAsc = fromResult.getProperty("orderByRidAsc");
      this.orderByRidDesc = fromResult.getProperty("orderByRidDesc");
    } catch (Exception e) {
      throw new CommandExecutionException(e);
    }
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/ExpandStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.Identifiable;
import com.arcadedb.database.Record;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;

import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;

/**
 * Expands a result-set.
 * The pre-requisite is that the input element contains only one field (no matter the name)
 */
public class ExpandStep extends AbstractExecutionStep {

  private long cost = 0;

  ResultSet lastResult      = null;
  Iterator  nextSubsequence = null;
  Result    nextElement     = null;

  public ExpandStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    if (prev == null || prev.isEmpty()) {
      throw new CommandExecutionException("Cannot expand without a target");
    }
    return new ResultSet() {
      long localCount = 0;

      @Override
      public boolean hasNext() {
        if (localCount >= nRecords) {
          return false;
        }
        if (nextElement == null) {
          fetchNext(ctx, nRecords);
        }
        return nextElement != null;
      }

      @Override
      public Result next() {
        if (localCount >= nRecords) {
          throw new IllegalStateException();
        }
        if (nextElement == null) {
          fetchNext(ctx, nRecords);
        }
        if (nextElement == null) {
          throw new IllegalStateException();
        }

        Result result = nextElement;
        localCount++;
        nextElement = null;
        fetchNext(ctx, nRecords);
        return result;
      }

      @Override
      public void close() {

      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private void fetchNext(CommandContext ctx, int n) {
    do {
      if (nextSubsequence != null && nextSubsequence.hasNext()) {
        long begin = profilingEnabled ? System.nanoTime() : 0;
        try {
          Object nextElementObj = nextSubsequence.next();
          if (nextElementObj instanceof Result) {
            nextElement = (Result) nextElementObj;
          } else if (nextElementObj instanceof Identifiable) {
            Record record = ((Identifiable) nextElementObj).getRecord();
            if (record == null) {
              continue;
            }
            nextElement = new ResultInternal();
            ((ResultInternal) nextElement).setElement((Document) record);
          } else {
            nextElement = new ResultInternal();
            ((ResultInternal) nextElement).setProperty("value", nextElementObj);
          }
          break;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }

      if (nextSubsequence == null || !nextSubsequence.hasNext()) {
        if (lastResult == null || !lastResult.hasNext()) {
          lastResult = getPrev().get().syncPull(ctx, n);
        }
        if (!lastResult.hasNext()) {
          return;
        }
      }

      Result nextAggregateItem = lastResult.next();
      long begin = profilingEnabled ? System.nanoTime() : 0;
      try {
        if (nextAggregateItem.getPropertyNames().size() == 0) {
          continue;
        }
        if (nextAggregateItem.getPropertyNames().size() > 1) {
          throw new IllegalStateException("Invalid EXPAND on record " + nextAggregateItem);
        }

        String propName = nextAggregateItem.getPropertyNames().iterator().next();
        Object projValue = nextAggregateItem.getProperty(propName);
        if (projValue == null) {
          continue;
        }
        if (projValue instanceof Identifiable) {
          Record rec = ((Identifiable) projValue).getRecord();
          if (rec == null) {
            continue;
          }
          ResultInternal res = new ResultInternal();
          res.setElement((Document) rec);

          nextSubsequence = Collections.singleton(res).iterator();
        } else if (projValue instanceof Result) {
          nextSubsequence = Collections.singleton((Result) projValue).iterator();
        } else if (projValue instanceof Iterator) {
          nextSubsequence = (Iterator) projValue;
        } else if (projValue instanceof Iterable) {
          nextSubsequence = ((Iterable) projValue).iterator();
        }
      } finally {
        if (profilingEnabled) {
          cost += (System.nanoTime() - begin);
        }
      }
    } while (true);

  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ EXPAND";
    if (profilingEnabled) {
      result += " (" + getCostFormatted() + ")";
    }
    return result;
  }

  @Override
  public long getCost() {
    return cost;
  }
}


<|start_filename|>engine/src/test/java/performance/PerformanceComparator.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package performance;

import com.arcadedb.serializer.BinaryComparator;
import com.arcadedb.serializer.UnsignedBytesComparator;

import java.util.Comparator;
import java.util.UUID;

public class PerformanceComparator {
  private static final int REPEAT_TEST  = 3;
  private static final int ARRAY_LENGTH = 10000;

  public static void main(String[] args) throws Exception {
    new PerformanceComparator().run();
  }

  private void run() {
    System.out.println("Generating random strings...");

    final String[] array = new String[ARRAY_LENGTH];
    for (int i = 0; i < ARRAY_LENGTH; i++) {
      array[i] = UUID.randomUUID().toString();
    }

    for (int t = 0; t < REPEAT_TEST; t++)
      test(array);
  }

  private void test(String[] array) {
    System.out.println("Starting comparison matrix for Java Std...");

    long beginTime = System.currentTimeMillis();

    final int[][] results1 = new int[ARRAY_LENGTH][ARRAY_LENGTH];
    for (int i = 0; i < ARRAY_LENGTH; i++) {
      for (int k = 0; k < ARRAY_LENGTH; k++)
        results1[i][k] = BinaryComparator.compareBytes(array[i].getBytes(), array[k].getBytes());
    }

    System.out.println("Comparison matrix finishes in " + (System.currentTimeMillis() - beginTime) + "ms");

    System.out.println("Starting comparison matrix for Java Unsafe is available...");

    beginTime = System.currentTimeMillis();

    final Comparator<byte[]> comparator = UnsignedBytesComparator.BEST_COMPARATOR;

    final int[][] results2 = new int[ARRAY_LENGTH][ARRAY_LENGTH];
    for (int i = 0; i < ARRAY_LENGTH; i++) {
      for (int k = 0; k < ARRAY_LENGTH; k++)
        results2[i][k] = comparator.compare(array[i].getBytes(), array[k].getBytes());
    }

    System.out.println("Comparison matrix finishes in " + (System.currentTimeMillis() - beginTime) + "ms");

    for (int i = 0; i < ARRAY_LENGTH; i++)
      for (int k = 0; k < ARRAY_LENGTH; k++)
        if (results1[i][k] < 0 && results2[i][k] >= 0 || results1[i][k] > 0 && results2[i][k] <= 0)
          System.out.println("Error on results");
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/database/Document.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.database;

import com.arcadedb.schema.DocumentType;
import org.json.JSONObject;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Set;

public interface Document extends Record {
  byte RECORD_TYPE = 0;

  MutableDocument modify();

  DetachedDocument detach();

  boolean has(String propertyName);

  Object get(String propertyName);

  String getString(String propertyName);

  Boolean getBoolean(String propertyName);

  Byte getByte(String propertyName);

  Short getShort(String propertyName);

  Integer getInteger(String propertyName);

  Long getLong(String propertyName);

  Float getFloat(String propertyName);

  Double getDouble(String propertyName);

  BigDecimal getDecimal(String propertyName);

  Date getDate(String propertyName);

  EmbeddedDocument getEmbedded(String propertyName);

  Set<String> getPropertyNames();

  DocumentType getType();

  String getTypeName();

  JSONObject toJSON();

  Map<String, Object> toMap();

  @Override
  default Document asDocument() {
    return this;
  }

  @Override
  default Document asDocument(final boolean loadContent) {
    return this;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/IfStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.BooleanExpression;
import com.arcadedb.query.sql.parser.Statement;

import java.util.List;

/**
 * Created by luigidellaquila on 19/09/16.
 */
public class IfStep extends AbstractExecutionStep {
    public List<Statement> positiveStatements;
    public List<Statement> negativeStatements;
    BooleanExpression condition;
    ScriptExecutionPlan positivePlan;
    ScriptExecutionPlan negativePlan;
    Boolean conditionMet = null;


    public IfStep(CommandContext ctx, boolean profilingEnabled) {
        super(ctx, profilingEnabled);
    }

    @Override
    public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
        init(ctx);
        if (conditionMet) {
            initPositivePlan(ctx);
            return positivePlan.fetchNext(nRecords);
        } else {
            initNegativePlan(ctx);
            if (negativePlan != null) {
                return negativePlan.fetchNext(nRecords);
            }
        }
        return new InternalResultSet();

    }

    protected void init(CommandContext ctx) {
        if (conditionMet == null) {
            conditionMet = condition.evaluate((Result) null, ctx);
        }
    }

    public void initPositivePlan(CommandContext ctx) {
        if (positivePlan == null) {
            BasicCommandContext subCtx1 = new BasicCommandContext();
            subCtx1.setParent(ctx);
            ScriptExecutionPlan positivePlan = new ScriptExecutionPlan(subCtx1);
            for (Statement stm : positiveStatements) {
                positivePlan.chain(stm.createExecutionPlan(subCtx1, profilingEnabled), profilingEnabled);
            }
            setPositivePlan(positivePlan);
        }
    }

    public void initNegativePlan(CommandContext ctx) {
        if (negativePlan == null && negativeStatements != null) {
            if (negativeStatements.size() > 0) {
                BasicCommandContext subCtx2 = new BasicCommandContext();
                subCtx2.setParent(ctx);
                ScriptExecutionPlan negativePlan = new ScriptExecutionPlan(subCtx2);
                for (Statement stm : negativeStatements) {
                    negativePlan.chain(stm.createExecutionPlan(subCtx2, profilingEnabled), profilingEnabled);
                }
                setNegativePlan(negativePlan);
            }
        }
    }


    public BooleanExpression getCondition() {
        return condition;
    }

    public void setCondition(BooleanExpression condition) {
        this.condition = condition;
    }

    public ScriptExecutionPlan getPositivePlan() {
        return positivePlan;
    }

    public void setPositivePlan(ScriptExecutionPlan positivePlan) {
        this.positivePlan = positivePlan;
    }

    public ScriptExecutionPlan getNegativePlan() {
        return negativePlan;
    }

    public void setNegativePlan(ScriptExecutionPlan negativePlan) {
        this.negativePlan = negativePlan;
    }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/SleepStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author <NAME> (<EMAIL>-(at)-orient<EMAIL>)
 */
public class SleepStatementExecutionTest extends TestHelper {
  @Test
  public void testBasic() {
    long begin = System.currentTimeMillis();
    ResultSet result = database.command("sql", "sleep 1000");
    Assertions.assertTrue(System.currentTimeMillis() - begin >= 1000);
    //printExecutionPlan(null, result);
    Assertions.assertNotNull(result);
    Assertions.assertTrue(result.hasNext());
    Result item = result.next();
    Assertions.assertEquals("sleep", item.getProperty("operation"));
    Assertions.assertFalse(result.hasNext());
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/graph/SQLFunctionMoveFiltered.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.graph;

import com.arcadedb.database.Database;
import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.MultiValue;
import com.arcadedb.query.sql.executor.SQLEngine;
import com.arcadedb.query.sql.executor.SQLFunctionFiltered;
import com.arcadedb.utility.Callable;
import com.arcadedb.utility.FileUtils;

/**
 * Created by luigidellaquila on 03/01/17.
 */
public abstract class SQLFunctionMoveFiltered extends SQLFunctionMove implements SQLFunctionFiltered {

  protected static int supernodeThreshold = 1000; // move to some configuration

  public SQLFunctionMoveFiltered() {
    super(NAME, 1, 2);
  }

  public SQLFunctionMoveFiltered(final String iName, final int iMin, final int iMax) {
    super(iName, iMin, iMax);
  }

  @Override
  public Object execute(final Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult,
      final Object[] iParameters, final Iterable<Identifiable> iPossibleResults, final CommandContext iContext) {
    final String[] labels;
    if (iParameters != null && iParameters.length > 0 && iParameters[0] != null)
      labels = MultiValue.array(iParameters, String.class, new Callable<Object, Object>() {

        @Override
        public Object call(final Object iArgument) {
          return FileUtils.getStringContent(iArgument);
        }
      });
    else
      labels = null;

    return SQLEngine.foreachRecord(new Callable<Object, Identifiable>() {
      @Override
      public Object call(final Identifiable iArgument) {
        return move(iContext.getDatabase(), iArgument, labels, iPossibleResults);
      }
    }, iThis, iContext);

  }

  protected abstract Object move(Database graph, Identifiable iArgument, String[] labels,
      Iterable<Identifiable> iPossibleResults);

}


<|start_filename|>integration/src/test/java/com/arcadedb/integration/importer/Neo4jImporterIT.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.integration.importer;

import com.arcadedb.database.Database;
import com.arcadedb.database.DatabaseFactory;
import com.arcadedb.graph.Edge;
import com.arcadedb.graph.Vertex;
import com.arcadedb.index.IndexCursor;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.utility.FileUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class Neo4jImporterIT {
  private final static String DATABASE_PATH = "target/databases/neo4j";

  @Test
  public void testImportOK() throws IOException {
    final File databaseDirectory = new File(DATABASE_PATH);

    try {
      final URL inputFile = Neo4jImporterIT.class.getClassLoader().getResource("neo4j-export-mini.jsonl");

      final Neo4jImporter importer = new Neo4jImporter(("-i " + inputFile.getFile() + " -d " + DATABASE_PATH + " -o -decimalType double").split(" "));
      importer.run();

      Assertions.assertFalse(importer.isError());

      Assertions.assertTrue(databaseDirectory.exists());

      try (final DatabaseFactory factory = new DatabaseFactory(DATABASE_PATH)) {
        try (Database database = factory.open()) {
          DocumentType personType = database.getSchema().getType("User");
          Assertions.assertNotNull(personType);
          Assertions.assertEquals(3, database.countType("User", true));

          IndexCursor cursor = database.lookupByKey("User", "id", "0");
          Assertions.assertTrue(cursor.hasNext());
          Vertex v = cursor.next().asVertex();
          Assertions.assertEquals("Adam", v.get("name"));
          Assertions.assertEquals("2015-07-04T19:32:24", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(v.getLong("born")));

          Map<String, Object> place = (Map<String, Object>) v.get("place");
          Assertions.assertEquals(33.46789, place.get("latitude"));
          Assertions.assertNull(place.get("height"));

          Assertions.assertEquals(Arrays.asList("Sam", "Anna", "Grace"), v.get("kids"));

          DocumentType friendType = database.getSchema().getType("KNOWS");
          Assertions.assertNotNull(friendType);
          Assertions.assertEquals(1, database.countType("KNOWS", true));

          Iterator<Edge> relationships = v.getEdges(Vertex.DIRECTION.OUT, "KNOWS").iterator();
          Assertions.assertTrue(relationships.hasNext());
          Edge e = relationships.next();

          Assertions.assertEquals(1993, e.get("since"));
          Assertions.assertEquals("P5M1DT12H", e.get("bffSince"));
        }
      }
      Assertions.assertTrue(DatabaseFactory.getActiveDatabaseInstances().isEmpty(), "Found active databases: " + DatabaseFactory.getActiveDatabaseInstances());
    } finally {
      FileUtils.deleteRecursively(databaseDirectory);
    }
  }

  @Test
  public void testImportNoFile() throws IOException {
    final URL inputFile = Neo4jImporterIT.class.getClassLoader().getResource("neo4j-export-mini.jsonl");
    final Neo4jImporter importer = new Neo4jImporter(("-i " + inputFile.getFile() + "2 -d " + DATABASE_PATH + " -o").split(" "));
    try {
      importer.run();
      Assertions.fail("Expected File Not Found Exception");
    } catch (IllegalArgumentException e) {
    }
    Assertions.assertTrue(importer.isError());
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/ParallelExecStepTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import com.arcadedb.database.RID;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by luigidellaquila on 26/07/16.
 */
public class ParallelExecStepTest {

  @Test
  public void test() throws Exception {
    TestHelper.executeInNewDatabase((db) -> {
      CommandContext ctx = new BasicCommandContext();
      List<InternalExecutionPlan> subPlans = new ArrayList<>();
      for (int i = 0; i < 4; i++) {
        FetchFromRidsStep step0 = new FetchFromRidsStep(Collections.singleton(new RID(db, 12, i)), ctx, false);
        FetchFromRidsStep step1 = new FetchFromRidsStep(Collections.singleton(new RID(db, 12, i)), ctx, false);
        InternalExecutionPlan plan = new SelectExecutionPlan(ctx);
        plan.getSteps().add(step0);
        plan.getSteps().add(step1);
        subPlans.add(plan);
      }

      ParallelExecStep step = new ParallelExecStep(subPlans, ctx, false);

      SelectExecutionPlan plan = new SelectExecutionPlan(ctx);
      plan.getSteps().add(new FetchFromRidsStep(Collections.singleton(new RID(db, 12, 100)), ctx, false));
      plan.getSteps().add(step);
      plan.getSteps().add(new FetchFromRidsStep(Collections.singleton(new RID(db, 12, 100)), ctx, false));
    });
  }
}


<|start_filename|>engine/src/test/java/com/arcadedb/query/sql/executor/ExplainStatementExecutionTest.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.TestHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Optional;

/**
 * @author <NAME> (<EMAIL>-(at)-orientdb.<EMAIL>)
 */
public class ExplainStatementExecutionTest extends TestHelper {
  @Test
  public void testExplainSelectNoTarget() {
    ResultSet result = database.query("sql", "explain select 1 as one, 2 as two, 2+3");
    Assertions.assertTrue(result.hasNext());
    Result next = result.next();
    Assertions.assertNotNull(next.getProperty("executionPlan"));
    Assertions.assertNotNull(next.getProperty("executionPlanAsString"));

    Optional<ExecutionPlan> plan = result.getExecutionPlan();
    Assertions.assertTrue(plan.isPresent());
    Assertions.assertTrue(plan.get() instanceof SelectExecutionPlan);

    result.close();
  }
}


<|start_filename|>server/src/main/resources/static/api.html<|end_filename|>
<div class="tab-pane fade" id="tab-api" role="tabpanel">
  <div class="row">
    <div class="col-12">
      <h4><i class="fa fa-plug"></i> HTTP API</h4>
      <br>
      <div class="row">
        <div class="col-1">
          <b>Method</b>
        </div>
        <div class="col-3">
          <b>URL</b>
        </div>
        <div class="col-8">
          <b>Description</b>
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/begin/{database}</pre>
        </div>
        <div class="col-8">
          Begins a transaction on the server managed as a session. The response header contains the session id. Set this id in the following requests to execute
          them in the same transaction scope. See also <b>/commit</b> and <b>/rollback</b>.
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/command/{database}</pre>
        </div>
        <div class="col-8">
          Executes a non-idempotent command.
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/commit/{database}</pre>
        </div>
        <div class="col-8">
          Commits a transaction on the server. Set the session id obtained with the <b>/begin</b> command as a header of the request. See also <b>/begin</b> and
          <b>/rollback</b>.
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/create/{database}</pre>
        </div>
        <div class="col-8">
          Creates a database
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          GET
        </div>
        <div class="col-3">
          <pre>/api/v1/databases</pre>
        </div>
        <div class="col-8">
          Returns the list of databases the current authenticated user can access to.
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/document/{database}</pre>
        </div>
        <div class="col-8">
          Creates a new document
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          GET
        </div>
        <div class="col-3">
          <pre>/api/v1/document/{database}/{rid}</pre>
        </div>
        <div class="col-8">
          Returns a document by record id (rid)
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/drop/{database}</pre>
        </div>
        <div class="col-8">
          Drops a database
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          GET
        </div>
        <div class="col-3">
          <pre>/api/v1/query/{database}</pre>
        </div>
        <div class="col-8">
          Executes an idempotent commands, like SELECT ad MATCH.
        </div>
      </div>
      <div class="row">
        <div class="col-1">
          POST
        </div>
        <div class="col-3">
          <pre>/api/v1/rollback/{database}</pre>
        </div>
        <div class="col-8">
          Rollbacks a transaction on the server. Set the session id obtained with the <b>/begin</b> command as a header of the request. See also
          <b>/begin</b> and <b>/commit</b>.
        </div>
      </div>

      <div class="row">
        <div class="col-1">
          GET
        </div>
        <div class="col-3">
          <pre>/api/v1/server</pre>
        </div>
        <div class="col-8">
          Returns the current HA configuration.
        </div>
      </div>

      <hr>
    </div>
  </div>
</div>


<|start_filename|>package/src/main/scripts/server.bat<|end_filename|>
@REM
@REM Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
@REM
@REM Licensed under the Apache License, Version 2.0 (the "License");
@REM you may not use this file except in compliance with the License.
@REM You may obtain a copy of the License at
@REM
@REM     http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing, software
@REM distributed under the License is distributed on an "AS IS" BASIS,
@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@REM See the License for the specific language governing permissions and
@REM limitations under the License.
@REM

@echo off
rem
rem Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
rem
rem Licensed under the Apache License, Version 2.0 (the "License");
rem you may not use this file except in compliance with the License.
rem You may obtain a copy of the License at
rem
rem     http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.

echo ""
echo " █████╗ ██████╗  ██████╗ █████╗ ██████╗ ███████╗██████╗ ██████╗ "
echo "██╔══██╗██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝██╔══██╗██╔══██╗"
echo "███████║██████╔╝██║     ███████║██║  ██║█████╗  ██║  ██║██████╔╝"
echo "██╔══██║██╔══██╗██║     ██╔══██║██║  ██║██╔══╝  ██║  ██║██╔══██╗"
echo "██║  ██║██║  ██║╚██████╗██║  ██║██████╔╝███████╗██████╔╝██████╔╝"
echo "╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝╚═════╝ ╚══════╝╚═════╝ ╚═════╝ "
echo "PLAY WITH DATA                                    arcadedb.com"

rem Guess ARCADEDB_HOME if not defined
set CURRENT_DIR=%cd%

if exist "%JAVA_HOME:"=%\bin\java.exe" goto setJavaHome
set JAVA=java
goto okJava

:setJavaHome
set JAVA="%JAVA_HOME:"=%\bin\java"

:okJava
if not "%ARCADEDB_HOME%" == "" goto gotHome
set ARCADEDB_HOME=%CURRENT_DIR%
if exist "%ARCADEDB_HOME%\bin\server.bat" goto okHome
cd ..
set ARCADEDB_HOME=%cd%
cd %CURRENT_DIR%

:gotHome
if exist "%ARCADEDB_HOME%\bin\server.bat" goto okHome
echo The ARCADEDB_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end

:okHome
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=

:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs

:doneSetArgs

set JAVA_OPTS_SCRIPT=-XX:+HeapDumpOnOutOfMemoryError -Djava.awt.headless=true -Dfile.encoding=UTF8

rem ARCADEDB memory options, default uses the available RAM. To set it to a specific value, like 2GB of heap, use "-Xms2G -Xmx2G"
set ARCADEDB_OPTS_MEMORY=

set ARCADEDB_JMX=-Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9998


rem TO DEBUG ARCADEDB SERVER RUN IT WITH THESE OPTIONS:
rem -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044
rem AND ATTACH TO THE CURRENT HOST, PORT 1044

call %JAVA% -server %JAVA_OPTS% %ARCADEDB_OPTS_MEMORY% %JAVA_OPTS_SCRIPT% %ARCADEDB_JMX% %ARCADEDB_SETTINGS% %CMD_LINE_ARGS% -cp "%ARCADEDB_HOME%\lib\*" com.arcadedb.server.ArcadeDBServer

:end


<|start_filename|>gremlin/src/main/java/org/apache/tinkerpop/gremlin/arcadedb/structure/ArcadeIoRegistrationStrategy.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package org.apache.tinkerpop.gremlin.arcadedb.structure;

import org.apache.tinkerpop.gremlin.arcadedb.structure.io.ArcadeIoRegistry;
import org.apache.tinkerpop.gremlin.process.traversal.IO;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IoStep;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;

public class ArcadeIoRegistrationStrategy extends AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy>
    implements TraversalStrategy.ProviderOptimizationStrategy {

  private static final ArcadeIoRegistrationStrategy INSTANCE = new ArcadeIoRegistrationStrategy();

  @Override
  public void apply(Traversal.Admin<?, ?> traversal) {
    if (traversal.getStartStep() instanceof IoStep) {
      IoStep ioStep = (IoStep) traversal.getStartStep();
      ioStep.configure(IO.registry, new ArcadeIoRegistry());
    }
  }

  public static ArcadeIoRegistrationStrategy instance() {
    return INSTANCE;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/graph/Vertex.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.graph;

import com.arcadedb.database.Document;
import com.arcadedb.database.Identifiable;

/**
 * A Vertex represents the main information in a Property Graph. Vertices are connected with edges. Vertices can be Immutable (read-only) and Mutable.
 *
 * @author <NAME> (<EMAIL>)
 * @see Edge
 */
public interface Vertex extends Document {
  byte RECORD_TYPE = 1;

  enum DIRECTION {
    OUT, IN, BOTH
  }

  MutableVertex modify();

  MutableEdge newEdge(String edgeType, Identifiable toVertex, boolean bidirectional, final Object... properties);

  ImmutableLightEdge newLightEdge(String edgeType, Identifiable toVertex, boolean bidirectional);

  long countEdges(DIRECTION direction, String edgeType);

  Iterable<Edge> getEdges();

  Iterable<Edge> getEdges(DIRECTION direction, String... edgeTypes);

  /**
   * Returns all the connected vertices, both directions, any edge type.
   *
   * @return An iterator of PIndexCursorEntry entries
   */
  Iterable<Vertex> getVertices();

  /**
   * Returns the connected vertices.
   *
   * @param direction Direction between OUT, IN or BOTH
   *
   * @return An iterator of PIndexCursorEntry entries
   */
  Iterable<Vertex> getVertices(DIRECTION direction, String... edgeTypes);

  boolean isConnectedTo(Identifiable toVertex);

  boolean isConnectedTo(Identifiable toVertex, DIRECTION direction);

  @Override
  default Vertex asVertex() {
    return this;
  }

  @Override
  default Vertex asVertex(final boolean loadContent) {
    return this;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/method/misc/SQLMethodAsBoolean.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.method.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;

/**
 * @author <NAME> (Geomatys)
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLMethodAsBoolean extends AbstractSQLMethod {

    public static final String NAME = "asboolean";

    public SQLMethodAsBoolean() {
        super(NAME);
    }

    @Override
    public Object execute(Object self,
                          Identifiable currentRecord,
                          CommandContext context,
                          Object result,
                          Object[] params) {
        if (result != null) {
            if (result instanceof String) {
                result = Boolean.valueOf(((String) result).trim());
            } else if (result instanceof Number) {
                return ((Number) result).intValue() != 0;
            }
        }
        return result;
    }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/SetDocumentClassStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.Document;
import com.arcadedb.database.MutableDocument;
import com.arcadedb.database.Record;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.query.sql.parser.Identifier;

import java.util.Map;
import java.util.Optional;

/**
 * Assigns a class to documents coming from upstream
 *
 * @author <NAME> (luigi.dellaquila-(at)-gmail.com)
 */
public class SetDocumentClassStep extends AbstractExecutionStep {
  private final String targetClass;

  public SetDocumentClassStep(Identifier targetClass, CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.targetClass = targetClass.getStringValue();
  }

  @Override
  public ResultSet syncPull(CommandContext ctx, int nRecords) throws TimeoutException {
    ResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new ResultSet() {
      @Override
      public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override
      public Result next() {
        Result result = upstream.next();

        if (result.isElement()) {
          Record element = result.getElement().get().getRecord();
          if (!(result instanceof ResultInternal)) {
            result = new UpdatableResult((MutableDocument) element);
          } else {
            ((ResultInternal) result).setElement((Document) element);
          }
        }
        return result;
      }

      @Override
      public void close() {
        upstream.close();
      }

      @Override
      public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override
      public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  @Override
  public String prettyPrint(int depth, int indent) {
    String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ SET TYPE\n" + spaces + "  " + this.targetClass;
    return result;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/function/misc/SQLFunctionCount.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.function.misc;

import com.arcadedb.database.Identifiable;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.function.math.SQLFunctionMathAbstract;

/**
 * Count the record that contains a field. Use * to indicate the record instead of the field. Uses the context to save the counter
 * number. When different Number class are used, take the class with most precision.
 *
 * @author <NAME> (l.garulli--(at)--gmail.com)
 */
public class SQLFunctionCount extends SQLFunctionMathAbstract {
  public static final String NAME = "count";

  private long total = 0;

  public SQLFunctionCount() {
    super(NAME, 1, 1);
  }

  public Object execute(Object iThis, Identifiable iCurrentRecord, Object iCurrentResult, final Object[] iParams, CommandContext iContext) {
    if (iParams.length == 0 || iParams[0] != null)
      total++;

    return total;
  }

  public boolean aggregateResults() {
    return true;
  }

  public String getSyntax() {
    return "count(<field>|*)";
  }

  @Override
  public Object getResult() {
    return total;
  }
}


<|start_filename|>engine/src/main/java/com/arcadedb/query/sql/executor/AbstractUnrollStep.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.exception.CommandExecutionException;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;

/**
 * unwinds a result-set.
 *
 * @author <NAME> (<EMAIL>)
 */
public abstract class AbstractUnrollStep extends AbstractExecutionStep {


  ResultSet        lastResult      = null;
  Iterator<Result> nextSubsequence = null;
  Result           nextElement     = null;

  public AbstractUnrollStep(CommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
  }

  @Override public void reset() {
    this.lastResult = null;
    this.nextSubsequence = null;
    this.nextElement = null;
  }

  @Override public ResultSet syncPull(CommandContext ctx, int nRecords) {
    if (prev == null || prev.isEmpty()) {
      throw new CommandExecutionException("Cannot expand without a target");
    }
    return new ResultSet() {
      long localCount = 0;

      @Override public boolean hasNext() {
        if (localCount >= nRecords) {
          return false;
        }
        if (nextElement == null) {
          fetchNext(ctx, nRecords);
        }
        return nextElement != null;
      }

      @Override public Result next() {
        if (localCount >= nRecords) {
          throw new IllegalStateException();
        }
        if (nextElement == null) {
          fetchNext(ctx, nRecords);
        }
        if (nextElement == null) {
          throw new IllegalStateException();
        }

        Result result = nextElement;
        localCount++;
        nextElement = null;
        fetchNext(ctx, nRecords);
        return result;
      }

      @Override public void close() {

      }

      @Override public Optional<ExecutionPlan> getExecutionPlan() {
        return Optional.empty();
      }

      @Override public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private void fetchNext(CommandContext ctx, int n) {
    do {
      if (nextSubsequence != null && nextSubsequence.hasNext()) {
        nextElement = nextSubsequence.next();
        break;
      }

      if (nextSubsequence == null || !nextSubsequence.hasNext()) {
        if (lastResult == null || !lastResult.hasNext()) {
          lastResult = getPrev().get().syncPull(ctx, n);
        }
        if (!lastResult.hasNext()) {
          return;
        }
      }

      Result nextAggregateItem = lastResult.next();
      nextSubsequence = unroll(nextAggregateItem, ctx).iterator();

    } while (true);

  }

  protected abstract Collection<Result> unroll(final Result doc, final CommandContext iContext);

}


<|start_filename|>integration/src/main/java/com/arcadedb/integration/importer/SourceSchema.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.integration.importer;

import com.arcadedb.integration.importer.format.FormatImporter;

import java.util.HashMap;
import java.util.Map;

public class SourceSchema {
  private final FormatImporter formatImporter;
  private final Source         source;
  private final AnalyzedSchema      schema;
  private final Map<String, String> options = new HashMap<>();

  public SourceSchema(final FormatImporter formatImporter, final Source source, final AnalyzedSchema schema) {
    this.formatImporter = formatImporter;
    this.source = source;
    this.schema = schema;
  }

  public SourceSchema set(final String option, final String value) {
    options.put(option, value);
    return this;
  }

  public AnalyzedSchema getSchema() {
    return schema;
  }

  public Source getSource() {
    return source;
  }

  public Map<String, String> getOptions() {
    return options;
  }

  public FormatImporter getContentImporter() {
    return formatImporter;
  }
}


<|start_filename|>server/src/main/java/com/arcadedb/server/ha/message/ReplicaConnectFullResyncResponse.java<|end_filename|>
/*
 * Copyright © 2021-present Arcade Data Ltd (<EMAIL>)
 *
 * 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.
 */
package com.arcadedb.server.ha.message;

import com.arcadedb.database.Binary;
import com.arcadedb.server.ArcadeDBServer;
import com.arcadedb.server.ha.HAServer;

import java.util.HashSet;
import java.util.Set;

public class ReplicaConnectFullResyncResponse extends HAAbstractCommand {
  private long        lastMessageNumber;
  private Set<String> databases;

  public ReplicaConnectFullResyncResponse() {
  }

  public ReplicaConnectFullResyncResponse(final long lastMessageNumber, final Set<String> databases) {
    this.databases = databases;
  }

  @Override
  public HACommand execute(final HAServer server, final String remoteServerName, final long messageNumber) {
    return null;
  }

  @Override
  public void toStream(final Binary stream) {
    stream.putLong(lastMessageNumber);
    stream.putUnsignedNumber(databases.size());
    for (String db : databases)
      stream.putString(db);
  }

  @Override
  public void fromStream(ArcadeDBServer server, final Binary stream) {
    lastMessageNumber = stream.getLong();
    databases = new HashSet<>();
    final int fileCount = (int) stream.getUnsignedNumber();
    for (int i = 0; i < fileCount; ++i)
      databases.add(stream.getString());
  }

  public long getLastMessageNumber() {
    return lastMessageNumber;
  }

  public Set<String> getDatabases() {
    return databases;
  }

  @Override
  public String toString() {
    return "fullResync(lastMessageNumber=" + lastMessageNumber + " dbs=" + databases + ")";
  }
}
