From 4de5cb9ecc5bcf52d30d74835c10af1527ac973c Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 29 Jul 2016 16:12:01 +0200 Subject: [PATCH] Implement index optimization on SELECT queries new executor --- .../core/sql/executor/FetchFromIndexStep.java | 179 +++++++- .../orient/core/sql/executor/FilterStep.java | 2 +- .../core/sql/executor/IndexCondPair.java | 42 ++ .../sql/executor/IndexSearchDescriptor.java | 6 +- .../sql/executor/OExecutionStepInternal.java | 5 + .../sql/executor/OSelectExecutionPlanner.java | 124 ++++- .../orient/core/sql/executor/OrderByStep.java | 2 +- .../core/sql/executor/ParallelExecStep.java | 22 +- .../orient/core/sql/parser/OAndBlock.java | 15 +- .../core/sql/parser/OBaseExpression.java | 14 + .../core/sql/parser/OBaseIdentifier.java | 4 + .../sql/parser/OBinaryCompareOperator.java | 2 +- .../core/sql/parser/OBooleanExpression.java | 11 + .../orient/core/sql/parser/OCollection.java | 4 + .../orient/core/sql/parser/OExpression.java | 9 +- .../core/sql/parser/OLevelZeroIdentifier.java | 4 + .../orient/core/sql/parser/OOrBlock.java | 12 + .../OSelectStatementExecutionTest.java | 434 ++++++++++++++++++ 18 files changed, 846 insertions(+), 45 deletions(-) create mode 100644 core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexCondPair.java diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FetchFromIndexStep.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FetchFromIndexStep.java index 68aa0ed9d3d..1a6f5301ed6 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FetchFromIndexStep.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FetchFromIndexStep.java @@ -18,15 +18,20 @@ * Created by luigidellaquila on 23/07/16. */ public class FetchFromIndexStep extends AbstractExecutionStep { - private final OIndex index; - private final OBooleanExpression condition; + private final OIndex index; + private final OBinaryCondition additional; + + OBooleanExpression condition; + private boolean inited = false; private OIndexCursor cursor; - public FetchFromIndexStep(OIndex index, OBooleanExpression condition, OCommandContext ctx) { + public FetchFromIndexStep(OIndex index, OBooleanExpression condition, OBinaryCondition additionalRangeCondition, + OCommandContext ctx) { super(ctx); this.index = index; this.condition = condition; + this.additional = additionalRangeCondition; } @Override public OTodoResultSet syncPull(OCommandContext ctx, int nRecords) throws OTimeoutException { @@ -72,20 +77,47 @@ private void init() { } inited = true; - if (index.getDefinition() == null) { - return; + if (condition != null) { + init(condition); } + } + } - if (condition instanceof OBinaryCondition) { - processBinaryCondition(); - } else if (condition instanceof OBetweenCondition) { - processBetweenCondition(); - } else { - throw new OCommandExecutionException("search for index for " + condition + " is not supported yet"); - } + private void init(OBooleanExpression condition) { + if (index.getDefinition() == null) { + return; + } + if (condition == null) { + throw new OCommandExecutionException("Implement search from index without condition!"); + } else if (condition instanceof OBinaryCondition) { + processBinaryCondition(); + } else if (condition instanceof OBetweenCondition) { + processBetweenCondition(); + } else if (condition instanceof OAndBlock) { + processAndBlock(); + } else { + throw new OCommandExecutionException("search for index for " + condition + " is not supported yet"); } } + /** + * it's not key = [...] but a real condition on field names, already ordered (field names will be ignored) + */ + private void processAndBlock() { + OCollection fromKey = indexKeyFrom((OAndBlock) condition, additional); + OCollection toKey = indexKeyTo((OAndBlock) condition, additional); + boolean fromKeyIncluded = indexKeyFromIncluded((OAndBlock) condition, additional); + boolean toKeyIncluded = indexKeyToIncluded((OAndBlock) condition, additional); + init(fromKey, fromKeyIncluded, toKey, toKeyIncluded); + } + + private void init(OCollection fromKey, boolean fromKeyIncluded, OCollection toKey, boolean toKeyIncluded) { + Object secondValue = fromKey.execute((OResult) null, ctx); + Object thirdValue = toKey.execute((OResult) null, ctx); + OIndexDefinition indexDef = index.getDefinition(); + cursor = index.iterateEntriesBetween(toBetweenIndexKey(indexDef, secondValue), fromKeyIncluded, toBetweenIndexKey(indexDef, thirdValue), toKeyIncluded, isOrderAsc()); + } + private void processBetweenCondition() { OIndexDefinition definition = index.getDefinition(); OExpression key = ((OBetweenCondition) condition).getFirst(); @@ -97,7 +129,7 @@ private void processBetweenCondition() { Object secondValue = second.execute((OResult) null, ctx); Object thirdValue = third.execute((OResult) null, ctx); - cursor = index.iterateEntriesBetween(secondValue, true, thirdValue, true, true); + cursor = index.iterateEntriesBetween(toBetweenIndexKey(definition, secondValue), true, toBetweenIndexKey(definition, thirdValue), true, isOrderAsc()); } private void processBinaryCondition() { @@ -112,6 +144,9 @@ private void processBinaryCondition() { } private Collection toIndexKey(OIndexDefinition definition, Object rightValue) { + if (definition.getFields().size() == 1 && rightValue instanceof Collection) { + rightValue = ((Collection) rightValue).iterator().next(); + } rightValue = definition.createValue(rightValue); if (!(rightValue instanceof Collection)) { rightValue = Collections.singleton(rightValue); @@ -119,6 +154,18 @@ private Collection toIndexKey(OIndexDefinition definition, Object rightValue) { return (Collection) rightValue; } + private Object toBetweenIndexKey(OIndexDefinition definition, Object rightValue) { + if (definition.getFields().size() == 1 && rightValue instanceof Collection) { + rightValue = ((Collection) rightValue).iterator().next(); + } + rightValue = definition.createValue(rightValue); + + if (definition.getFields().size()>1 && !(rightValue instanceof Collection)) { + rightValue = Collections.singleton(rightValue); + } + return rightValue; + } + private OIndexCursor createCursor(OBinaryCompareOperator operator, OIndexDefinition definition, Object value, OCommandContext ctx) { boolean orderAsc = isOrderAsc(); @@ -139,7 +186,7 @@ private OIndexCursor createCursor(OBinaryCompareOperator operator, OIndexDefinit } private boolean isOrderAsc() { - return true; + return true;//TODO } @Override public void asyncPull(OCommandContext ctx, int nRecords, OExecutionCallback callback) throws OTimeoutException { @@ -150,8 +197,110 @@ private boolean isOrderAsc() { } + private OCollection indexKeyFrom(OAndBlock keyCondition, OBinaryCondition additional) { + OCollection result = new OCollection(-1); + for (OBooleanExpression exp : keyCondition.getSubBlocks()) { + if (exp instanceof OBinaryCondition) { + OBinaryCondition binaryCond = ((OBinaryCondition) exp); + OBinaryCompareOperator operator = binaryCond.getOperator(); + if ((operator instanceof OEqualsCompareOperator) || (operator instanceof OGtOperator) + || (operator instanceof OGeOperator)) { + result.add(binaryCond.getRight()); + } else if (additional != null) { + result.add(additional.getRight()); + } + } else { + throw new UnsupportedOperationException("Cannot execute index query with " + exp); + } + } + return result; + } + + private OCollection indexKeyTo(OAndBlock keyCondition, OBinaryCondition additional) { + OCollection result = new OCollection(-1); + for (OBooleanExpression exp : keyCondition.getSubBlocks()) { + if (exp instanceof OBinaryCondition) { + OBinaryCondition binaryCond = ((OBinaryCondition) exp); + OBinaryCompareOperator operator = binaryCond.getOperator(); + if ((operator instanceof OEqualsCompareOperator) || (operator instanceof OLtOperator) + || (operator instanceof OLeOperator)) { + result.add(binaryCond.getRight()); + } else if (additional != null) { + result.add(additional.getRight()); + } + } else { + throw new UnsupportedOperationException("Cannot execute index query with " + exp); + } + } + return result; + } + + private boolean indexKeyFromIncluded(OAndBlock keyCondition, OBinaryCondition additional) { + OBooleanExpression exp = keyCondition.getSubBlocks().get(keyCondition.getSubBlocks().size() - 1); + if (exp instanceof OBinaryCondition) { + OBinaryCompareOperator operator = ((OBinaryCondition) exp).getOperator(); + OBinaryCompareOperator additionalOperator = additional == null ? null : ((OBinaryCondition) additional).getOperator(); + if (isGreaterOperator(operator)) { + if(isIncludeOperator(operator)) { + return true; + }else { + return false; + } + } else if (additionalOperator==null || (isIncludeOperator(additionalOperator) && isGreaterOperator(additionalOperator))) { + return true; + } else { + return false; + } + } else { + throw new UnsupportedOperationException("Cannot execute index query with " + exp); + } + } + + private boolean isGreaterOperator(OBinaryCompareOperator operator) { + if (operator == null) { + return false; + } + return operator instanceof OGeOperator || operator instanceof OGtOperator; + } + + private boolean isLessOperator(OBinaryCompareOperator operator) { + if (operator == null) { + return false; + } + return operator instanceof OLeOperator || operator instanceof OLtOperator; + } + + private boolean isIncludeOperator(OBinaryCompareOperator operator) { + if (operator == null) { + return false; + } + return operator instanceof OGeOperator || operator instanceof OLeOperator; + } + + private boolean indexKeyToIncluded(OAndBlock keyCondition, OBinaryCondition additional) { + OBooleanExpression exp = keyCondition.getSubBlocks().get(keyCondition.getSubBlocks().size() - 1); + if (exp instanceof OBinaryCondition) { + OBinaryCompareOperator operator = ((OBinaryCondition) exp).getOperator(); + OBinaryCompareOperator additionalOperator = additional == null ? null : ((OBinaryCondition) additional).getOperator(); + if (isLessOperator(operator)) { + if(isIncludeOperator(operator)) { + return true; + }else{ + return false; + } + } else if (additionalOperator == null || (isIncludeOperator(additionalOperator) && isLessOperator(additionalOperator))) { + return true; + } else { + return false; + } + } else { + throw new UnsupportedOperationException("Cannot execute index query with " + exp); + } + } + @Override public String prettyPrint(int depth, int indent) { return OExecutionStepInternal.getIndent(depth, indent) + "+ FETCH FROM INDEX " + index.getName() + "\n" + - OExecutionStepInternal.getIndent(depth, indent) + " " + condition; + OExecutionStepInternal.getIndent(depth, indent) + " " + condition + (additional == null ? "" : " and " + additional); } + } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FilterStep.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FilterStep.java index 16777221a93..b38806b3ef2 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FilterStep.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/FilterStep.java @@ -114,7 +114,7 @@ private void fetchNextItem() { } @Override public String prettyPrint(int depth, int indent) { - return OExecutionStepInternal.getIndent(depth, indent) + "+ CALCULATE WHERE CONDITION : " + whereClause.toString(); + return OExecutionStepInternal.getIndent(depth, indent) + "+ CALCULATE WHERE CONDITION \n" +OExecutionStepInternal.getIndent(depth, indent)+" "+ whereClause.toString(); } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexCondPair.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexCondPair.java new file mode 100644 index 00000000000..ebbd3c5994f --- /dev/null +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexCondPair.java @@ -0,0 +1,42 @@ +package com.orientechnologies.orient.core.sql.executor; + +import com.orientechnologies.orient.core.sql.parser.OAndBlock; +import com.orientechnologies.orient.core.sql.parser.OBinaryCondition; + +/** + * 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 { + + OAndBlock mainCondition; + OBinaryCondition additionalRange; + + public IndexCondPair(OAndBlock keyCondition, OBinaryCondition 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 (mainCondition != null ? !mainCondition.equals(that.mainCondition) : that.mainCondition != null) + return false; + if (additionalRange != null ? !additionalRange.equals(that.additionalRange) : that.additionalRange != null) + return false; + + return true; + } + + @Override public int hashCode() { + int result = mainCondition != null ? mainCondition.hashCode() : 0; + result = 31 * result + (additionalRange != null ? additionalRange.hashCode() : 0); + return result; + } +} diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexSearchDescriptor.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexSearchDescriptor.java index 9a27cba9458..76f7ad9a7c5 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexSearchDescriptor.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/IndexSearchDescriptor.java @@ -3,6 +3,7 @@ import com.orientechnologies.orient.core.command.OCommandContext; import com.orientechnologies.orient.core.index.OIndex; import com.orientechnologies.orient.core.sql.parser.OAndBlock; +import com.orientechnologies.orient.core.sql.parser.OBinaryCondition; import com.orientechnologies.orient.core.sql.parser.OBooleanExpression; /** @@ -11,11 +12,14 @@ public class IndexSearchDescriptor { protected OIndex idx; protected OAndBlock keyCondition; + protected OBinaryCondition additionalRangeCondition; protected OBooleanExpression remainingCondition; - public IndexSearchDescriptor(OIndex idx, OAndBlock keyCondition, OBooleanExpression remainingCondition) { + public IndexSearchDescriptor(OIndex idx, OAndBlock keyCondition, OBinaryCondition additional, + OBooleanExpression remainingCondition) { this.idx = idx; this.keyCondition = keyCondition; + this.additionalRangeCondition = additional; this.remainingCondition = remainingCondition; } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OExecutionStepInternal.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OExecutionStepInternal.java index 289f4f0e3eb..34ea78edccf 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OExecutionStepInternal.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OExecutionStepInternal.java @@ -57,4 +57,9 @@ default String getTargetNode() { default List getSubSteps() { return Collections.EMPTY_LIST; } + + default List getSubExecutionPlans() { + return Collections.EMPTY_LIST; + } + } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OSelectExecutionPlanner.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OSelectExecutionPlanner.java index 9eb31495e3b..74c7eade01c 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OSelectExecutionPlanner.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OSelectExecutionPlanner.java @@ -105,12 +105,59 @@ private void optimizeQuery() { } if (whereClause != null) { flattenedWhereClause = whereClause.flatten(); + //this helps index optimization + flattenedWhereClause = moveFlattededEqualitiesLeft(flattenedWhereClause); } extractAggregateProjections(); + addOrderByProjections(); extractSubQueries(); } + /** + * re-writes a list of flat AND conditions, moving left all the equality operations + * + * @param flattenedWhereClause + * @return + */ + private List moveFlattededEqualitiesLeft(List flattenedWhereClause) { + if (flattenedWhereClause == null) { + return null; + } + + List result = new ArrayList<>(); + for (OAndBlock block : flattenedWhereClause) { + List equalityExpressions = new ArrayList<>(); + List nonEqualityExpressions = new ArrayList<>(); + OAndBlock newBlock = block.copy(); + for (OBooleanExpression exp : newBlock.getSubBlocks()) { + if (exp instanceof OBinaryCondition) { + if (((OBinaryCondition) exp).getOperator() instanceof OEqualsCompareOperator) { + equalityExpressions.add(exp); + } else { + nonEqualityExpressions.add(exp); + } + } else { + nonEqualityExpressions.add(exp); + } + } + OAndBlock newAnd = new OAndBlock(-1); + newAnd.getSubBlocks().addAll(equalityExpressions); + newAnd.getSubBlocks().addAll(nonEqualityExpressions); + result.add(newAnd); + } + + return result; + } + + private void addOrderByProjections() { + if (orderApplied || orderBy == null || orderBy.getItems().size() == 0 || projection == null || projection.getItems() == null + || (projection.getItems().size() == 1 && projection.getItems().get(0).isAll())) { + return; + } + //TODO + } + private void extractAggregateProjections() { if (projection == null) { return; @@ -193,8 +240,6 @@ private void addGroupByExpressionsToProjections() { } groupBy = newGroupBy; - - //TODO check ORDER BY and see if that projection has to be also propagated } } @@ -263,7 +308,7 @@ private void handleIndexAsTarget(OSelectExecutionPlan result, OIndexIdentifier i OBooleanExpression condition = andBlock.getSubBlocks().get(0); switch (indexIdentifier.getType()) { case INDEX: - result.chain(new FetchFromIndexStep(index, condition, ctx)); + result.chain(new FetchFromIndexStep(index, condition, null, ctx)); break; case VALUES: @@ -273,7 +318,7 @@ private void handleIndexAsTarget(OSelectExecutionPlan result, OIndexIdentifier i case VALUESDESC: // result.chain(new FetchFromIndexValuesStep(index, condition, ctx, false)); - throw new OCommandExecutionException("indexvalues*: is not yet supported");//TODO + throw new OCommandExecutionException("indexvaluesdesc: is not yet supported");//TODO } } @@ -352,6 +397,7 @@ private boolean handleClassAsTargetWithIndex(OSelectExecutionPlan plan, OIdentif if (flattenedWhereClause == null || flattenedWhereClause.size() == 0) { return false; } + OClass clazz = ctx.getDatabase().getMetadata().getSchema().getClass(identifier.getStringValue()); Set> indexes = clazz.getIndexes(); @@ -365,8 +411,10 @@ private boolean handleClassAsTargetWithIndex(OSelectExecutionPlan plan, OIdentif if (indexSearchDescriptors.size() == 1) { IndexSearchDescriptor desc = indexSearchDescriptors.get(0); - plan.chain(new FetchFromIndexStep(desc.idx, desc.keyCondition, ctx)); - plan.chain(new FilterStep(createWhereFrom(desc.remainingCondition), ctx)); + plan.chain(new FetchFromIndexStep(desc.idx, desc.keyCondition, desc.additionalRangeCondition, ctx)); + if (desc.remainingCondition != null && !desc.remainingCondition.isEmpty()) { + plan.chain(new FilterStep(createWhereFrom(desc.remainingCondition), ctx)); + } this.whereClause = null; this.flattenedWhereClause = null; } else { @@ -377,13 +425,27 @@ private boolean handleClassAsTargetWithIndex(OSelectExecutionPlan plan, OIdentif return true; } + private OExpression toExpression(OCollection right) { + OLevelZeroIdentifier id0 = new OLevelZeroIdentifier(-1); + id0.setCollection(right); + OBaseIdentifier id1 = new OBaseIdentifier(-1); + id1.setLevelZero(id0); + OBaseExpression id2 = new OBaseExpression(-1); + id2.setIdentifier(id1); + OExpression result = new OExpression(-1); + result.setMathExpression(id2); + return result; + } + private void createParallelIndexFetch(OSelectExecutionPlan plan, List indexSearchDescriptors, OCommandContext ctx) { List subPlans = new ArrayList<>(); for (IndexSearchDescriptor desc : indexSearchDescriptors) { OSelectExecutionPlan subPlan = new OSelectExecutionPlan(ctx); - subPlan.chain(new FetchFromIndexStep(desc.idx, desc.keyCondition, ctx)); - subPlan.chain(new FilterStep(createWhereFrom(desc.remainingCondition), ctx)); + subPlan.chain(new FetchFromIndexStep(desc.idx, desc.keyCondition, desc.additionalRangeCondition, ctx)); + if (desc.remainingCondition != null && !desc.remainingCondition.isEmpty()) { + subPlan.chain(new FilterStep(createWhereFrom(desc.remainingCondition), ctx)); + } subPlans.add(subPlan); } plan.chain(new ParallelExecStep(subPlans, ctx)); @@ -443,6 +505,7 @@ private IndexSearchDescriptor buildIndexSearchDescriptor(OCommandContext ctx, OI String fieldName = left.getDefaultAlias().getStringValue(); if (indexField.equals(fieldName)) { OBinaryCompareOperator operator = ((OBinaryCondition) singleExp).getOperator(); + //TODO check right to see if it's early calculated!!! if (operator instanceof OEqualsCompareOperator) { found = true; OBinaryCondition condition = new OBinaryCondition(-1); @@ -461,6 +524,15 @@ private IndexSearchDescriptor buildIndexSearchDescriptor(OCommandContext ctx, OI condition.setRight(((OBinaryCondition) singleExp).getRight().copy()); indexKeyValue.getSubBlocks().add(condition); blockIterator.remove(); + //look for the opposite condition, on the same field, for range queries (the other side of the range) + while (blockIterator.hasNext()) { + OBooleanExpression next = blockIterator.next(); + if (createsRangeWith((OBinaryCondition) singleExp, next)) { + result.additionalRangeCondition = (OBinaryCondition) next; + blockIterator.remove(); + break; + } + } break; } } @@ -478,6 +550,25 @@ private IndexSearchDescriptor buildIndexSearchDescriptor(OCommandContext ctx, OI return null; } + private boolean createsRangeWith(OBinaryCondition left, OBooleanExpression next) { + if (!(next instanceof OBinaryCondition)) { + return false; + } + OBinaryCondition right = (OBinaryCondition) next; + if (!left.getLeft().equals(right.getLeft())) { + return false; + } + OBinaryCompareOperator leftOperator = left.getOperator(); + OBinaryCompareOperator rightOperator = right.getOperator(); + if (leftOperator instanceof OGeOperator || leftOperator instanceof OGtOperator) { + return rightOperator instanceof OLeOperator || rightOperator instanceof OLtOperator; + } + if (leftOperator instanceof OLeOperator || leftOperator instanceof OLtOperator) { + return rightOperator instanceof OGeOperator || rightOperator instanceof OGtOperator; + } + return false; + } + private boolean allowsRangeQueries(OIndex index) { return index.supportsOrderedIterations(); } @@ -490,24 +581,27 @@ private boolean allowsRangeQueries(OIndex index) { */ private List commonFactor(List indexSearchDescriptors) { //index, key condition, additional filter (to aggregate in OR) - Map> aggregation = new HashMap<>(); + Map> aggregation = new HashMap<>(); for (IndexSearchDescriptor item : indexSearchDescriptors) { - Map filtersForIndex = aggregation.get(item.idx); + Map filtersForIndex = aggregation.get(item.idx); if (filtersForIndex == null) { filtersForIndex = new HashMap<>(); aggregation.put(item.idx, filtersForIndex); } - OOrBlock existingAdditionalConditions = filtersForIndex.get(item.keyCondition); + IndexCondPair extendedCond = new IndexCondPair(item.keyCondition, item.additionalRangeCondition); + + OOrBlock existingAdditionalConditions = filtersForIndex.get(extendedCond); if (existingAdditionalConditions == null) { existingAdditionalConditions = new OOrBlock(-1); - filtersForIndex.put(item.keyCondition, existingAdditionalConditions); + filtersForIndex.put(extendedCond, existingAdditionalConditions); } existingAdditionalConditions.getSubBlocks().add(item.remainingCondition); } List result = new ArrayList<>(); - for (Map.Entry> item : aggregation.entrySet()) { - for (Map.Entry filters : item.getValue().entrySet()) { - result.add(new IndexSearchDescriptor(item.getKey(), filters.getKey(), filters.getValue())); + for (Map.Entry> item : aggregation.entrySet()) { + for (Map.Entry filters : item.getValue().entrySet()) { + result.add(new IndexSearchDescriptor(item.getKey(), filters.getKey().mainCondition, filters.getKey().additionalRange, + filters.getValue())); } } return result; diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OrderByStep.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OrderByStep.java index f98030b3f6f..3e939caa21e 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OrderByStep.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/OrderByStep.java @@ -92,6 +92,6 @@ private void init(OExecutionStepInternal p, OCommandContext ctx) { } @Override public String prettyPrint(int depth, int indent) { - return OExecutionStepInternal.getIndent(depth, indent) + "+ SORT"; + return OExecutionStepInternal.getIndent(depth, indent) + "+ "+orderBy; } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ParallelExecStep.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ParallelExecStep.java index 2c9bfc0ca00..999e8909151 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ParallelExecStep.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ParallelExecStep.java @@ -11,14 +11,14 @@ * @author Luigi Dell'Aquila */ public class ParallelExecStep extends AbstractExecutionStep { - private final List subExecuitonPlans; + private final List subExecutionPlans; int current = 0; private OTodoResultSet currentResultSet = null; public ParallelExecStep(List subExecuitonPlans, OCommandContext ctx) { super(ctx); - this.subExecuitonPlans = subExecuitonPlans; + this.subExecutionPlans = subExecuitonPlans; } @Override public OTodoResultSet syncPull(OCommandContext ctx, int nRecords) throws OTimeoutException { @@ -68,11 +68,11 @@ public ParallelExecStep(List subExecuitonPlans, OCommand void fetchNext(OCommandContext ctx, int nRecords) { do { - if (current > subExecuitonPlans.size()) { + if (current >= subExecutionPlans.size()) { currentResultSet = null; return; } - currentResultSet = subExecuitonPlans.get(current).fetchNext(nRecords); + currentResultSet = subExecutionPlans.get(current).fetchNext(nRecords); if (!currentResultSet.hasNext()) { current++; } @@ -91,14 +91,14 @@ void fetchNext(OCommandContext ctx, int nRecords) { String result = ""; String ind = OExecutionStepInternal.getIndent(depth, indent); - int[] blockSizes = new int[subExecuitonPlans.size()]; + int[] blockSizes = new int[subExecutionPlans.size()]; - for (int i = 0; i < subExecuitonPlans.size(); i++) { - OInternalExecutionPlan currentPlan = subExecuitonPlans.get(subExecuitonPlans.size() - 1 - i); + for (int i = 0; i < subExecutionPlans.size(); i++) { + OInternalExecutionPlan currentPlan = subExecutionPlans.get(subExecutionPlans.size() - 1 - i); String partial = currentPlan.prettyPrint(0, indent); String[] partials = partial.split("\n"); - blockSizes[subExecuitonPlans.size() - 1 - i] = partials.length + 2; + blockSizes[subExecutionPlans.size() - 1 - i] = partials.length + 2; result = "+-------------------------\n" + result; for (int j = 0; j < partials.length; j++) { String p = partials[partials.length - 1 - j]; @@ -114,7 +114,7 @@ void fetchNext(OCommandContext ctx, int nRecords) { result += foot(blockSizes); result = ind + result; result = result.replaceAll("\n", "\n" + ind); - result = head(depth, indent, subExecuitonPlans.size()) + "\n" + result; + result = head(depth, indent, subExecutionPlans.size()) + "\n" + result; return result; } @@ -199,4 +199,8 @@ private String spaces(int num) { private String appendPipe(String p) { return "| " + p; } + + public List getSubExecutionPlans() { + return (List) subExecutionPlans; + } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OAndBlock.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OAndBlock.java index 45d24ea986a..096de263bf7 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OAndBlock.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OAndBlock.java @@ -169,7 +169,7 @@ public OAndBlock copy() { for (OBooleanExpression exp : subBlocks) { result.subBlocks.add(exp.copy()); } - return null; + return result; } @Override public boolean equals(Object o) { @@ -189,5 +189,18 @@ public OAndBlock copy() { @Override public int hashCode() { return subBlocks != null ? subBlocks.hashCode() : 0; } + + @Override public boolean isEmpty() { + if (subBlocks.isEmpty()) { + return true; + } + for (OBooleanExpression block : subBlocks) { + if (!block.isEmpty()) { + return false; + } + } + return true; + } + } /* JavaCC - OriginalChecksum=cf1f66cc86cfc93d357f9fcdfa4a4604 (do not edit this line) */ diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseExpression.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseExpression.java index 0a7a20b1d9b..764a7172090 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseExpression.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseExpression.java @@ -192,6 +192,16 @@ public AggregationContext getAggregationContext(OCommandContext ctx) { } } + @Override public OBaseExpression copy() { + OBaseExpression result = new OBaseExpression(-1); + result.number = number == null ? null : number.copy(); + result.identifier = identifier == null ? null : identifier.copy(); + result.inputParam = inputParam == null ? null : inputParam.copy(); + result.string = string; + result.modifier = modifier == null ? null : modifier.copy(); + return result; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -222,6 +232,10 @@ public AggregationContext getAggregationContext(OCommandContext ctx) { result = 31 * result + (modifier != null ? modifier.hashCode() : 0); return result; } + + public void setIdentifier(OBaseIdentifier identifier) { + this.identifier = identifier; + } } /* JavaCC - OriginalChecksum=71b3e2d1b65c923dc7cfe11f9f449d2b (do not edit this line) */ diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseIdentifier.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseIdentifier.java index e3178aefacc..b6819a0f4d1 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseIdentifier.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBaseIdentifier.java @@ -172,6 +172,10 @@ public AggregationContext getAggregationContext(OCommandContext ctx) { } } + public void setLevelZero(OLevelZeroIdentifier levelZero) { + this.levelZero = levelZero; + } + public OBaseIdentifier copy() { OBaseIdentifier result = new OBaseIdentifier(-1); result.levelZero = levelZero==null?null:levelZero.copy(); diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBinaryCompareOperator.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBinaryCompareOperator.java index 9e3c034407a..f2958dfb65a 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBinaryCompareOperator.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBinaryCompareOperator.java @@ -8,7 +8,7 @@ public interface OBinaryCompareOperator { boolean supportsBasicCalculation(); - SimpleNode copy(); + OBinaryCompareOperator copy(); default boolean isRangeOperator(){ return false; diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBooleanExpression.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBooleanExpression.java index 5c24593d2bf..b1ed2f90dfe 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBooleanExpression.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OBooleanExpression.java @@ -52,6 +52,10 @@ public abstract class OBooleanExpression extends SimpleNode { public void toString(Map params, StringBuilder builder) { builder.append("true"); } + + @Override public boolean isEmpty() { + return false; + } }; public static final OBooleanExpression FALSE = new OBooleanExpression(0) { @@ -91,6 +95,9 @@ public void toString(Map params, StringBuilder builder) { builder.append("false"); } + @Override public boolean isEmpty() { + return false; + } }; public OBooleanExpression(int id) { @@ -148,4 +155,8 @@ protected OAndBlock encapsulateInAndBlock(OBooleanExpression item) { public abstract boolean needsAliases(Set aliases); public abstract OBooleanExpression copy(); + + public boolean isEmpty() { + return false; + } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OCollection.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OCollection.java index a2834968d9c..fefe8dd615e 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OCollection.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OCollection.java @@ -44,6 +44,10 @@ public void toString(Map params, StringBuilder builder) { builder.append("]"); } + public void add(OExpression exp){ + this.expressions.add(exp); + } + public Object execute(OIdentifiable iCurrentRecord, OCommandContext ctx) { List result = new ArrayList(); for (OExpression exp : expressions) { diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OExpression.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OExpression.java index 40745d693e8..15dc23b0994 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OExpression.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OExpression.java @@ -118,7 +118,10 @@ public Object execute(OResult iCurrentRecord, OCommandContext ctx) { } public boolean isBaseIdentifier() { - if (value instanceof OMathExpression) { + if (mathExpression != null) { + return mathExpression.isBaseIdentifier(); + } + if (value instanceof OMathExpression) {//only backward stuff, remote it return ((OMathExpression) value).isBaseIdentifier(); } @@ -366,5 +369,9 @@ public OExpression copy() { result = 31 * result + (booleanValue != null ? booleanValue.hashCode() : 0); return result; } + + public void setMathExpression(OMathExpression mathExpression) { + this.mathExpression = mathExpression; + } } /* JavaCC - OriginalChecksum=9c860224b121acdc89522ae97010be01 (do not edit this line) */ diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OLevelZeroIdentifier.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OLevelZeroIdentifier.java index ec3f7f0f3b8..e0172e45b7b 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OLevelZeroIdentifier.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OLevelZeroIdentifier.java @@ -201,5 +201,9 @@ public OLevelZeroIdentifier copy() { result = 31 * result + (collection != null ? collection.hashCode() : 0); return result; } + + public void setCollection(OCollection collection) { + this.collection = collection; + } } /* JavaCC - OriginalChecksum=0305fcf120ba9395b4c975f85cdade72 (do not edit this line) */ diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OOrBlock.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OOrBlock.java index 0fd406b7c37..ea08443baf4 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OOrBlock.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OOrBlock.java @@ -174,5 +174,17 @@ public List flatten() { @Override public int hashCode() { return subBlocks != null ? subBlocks.hashCode() : 0; } + + @Override public boolean isEmpty() { + if (subBlocks.isEmpty()) { + return true; + } + for (OBooleanExpression block : subBlocks) { + if (!block.isEmpty()) { + return false; + } + } + return true; + } } /* JavaCC - OriginalChecksum=98d3077303a598705894dbb7bd4e1573 (do not edit this line) */ diff --git a/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OSelectStatementExecutionTest.java b/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OSelectStatementExecutionTest.java index dc4526662f0..b9e00f2a81d 100644 --- a/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OSelectStatementExecutionTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OSelectStatementExecutionTest.java @@ -14,6 +14,7 @@ import org.junit.Test; import java.util.List; +import java.util.Optional; /** * Created by luigidellaquila on 07/07/16. @@ -888,6 +889,439 @@ public class OSelectStatementExecutionTest { result.close(); } + @Test public void testFetchFromClassWithIndex() { + String className = "testFetchFromClassWithIndex"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createIndex(className + ".name", OClass.INDEX_TYPE.NOTUNIQUE, "name"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name2'"); + printExecutionPlan(result); + + Assert.assertTrue(result.hasNext()); + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertEquals("name2", next.getProperty("name")); + + Assert.assertFalse(result.hasNext()); + + Optional p = result.getExecutionPlan(); + Assert.assertTrue(p.isPresent()); + OExecutionPlan p2 = p.get(); + Assert.assertTrue(p2 instanceof OSelectExecutionPlan); + OSelectExecutionPlan plan = (OSelectExecutionPlan) p2; + Assert.assertEquals(1, plan.getSteps().size()); + Assert.assertEquals(FetchFromIndexStep.class, plan.getSteps().get(0).getClass()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes() { + String className = "testFetchFromClassWithIndexes"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name", OClass.INDEX_TYPE.NOTUNIQUE, "name"); + clazz.createIndex(className + ".surname", OClass.INDEX_TYPE.NOTUNIQUE, "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name2' or surname = 'surname3'"); + printExecutionPlan(result); + + Assert.assertTrue(result.hasNext()); + for (int i = 0; i < 2; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertTrue("name2".equals(next.getProperty("name")) || ("surname3".equals(next.getProperty("surname")))); + } + + Assert.assertFalse(result.hasNext()); + + Optional p = result.getExecutionPlan(); + Assert.assertTrue(p.isPresent()); + OExecutionPlan p2 = p.get(); + Assert.assertTrue(p2 instanceof OSelectExecutionPlan); + OSelectExecutionPlan plan = (OSelectExecutionPlan) p2; + Assert.assertEquals(1, plan.getSteps().size()); + Assert.assertEquals(ParallelExecStep.class, plan.getSteps().get(0).getClass()); + ParallelExecStep parallel = (ParallelExecStep) plan.getSteps().get(0); + Assert.assertEquals(2, parallel.getSubExecutionPlans().size()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes2() { + String className = "testFetchFromClassWithIndexes2"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name", OClass.INDEX_TYPE.NOTUNIQUE, "name"); + clazz.createIndex(className + ".surname", OClass.INDEX_TYPE.NOTUNIQUE, "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.save(); + } + + OTodoResultSet result = db + .query("select from " + className + " where foo is not null and (name = 'name2' or surname = 'surname3')"); + printExecutionPlan(result); + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes3() { + String className = "testFetchFromClassWithIndexes3"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name", OClass.INDEX_TYPE.NOTUNIQUE, "name"); + clazz.createIndex(className + ".surname", OClass.INDEX_TYPE.NOTUNIQUE, "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where foo < 100 and (name = 'name2' or surname = 'surname3')"); + printExecutionPlan(result); + + Assert.assertTrue(result.hasNext()); + for (int i = 0; i < 2; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertTrue("name2".equals(next.getProperty("name")) || ("surname3".equals(next.getProperty("surname")))); + } + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes4() { + String className = "testFetchFromClassWithIndexes4"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name", OClass.INDEX_TYPE.NOTUNIQUE, "name"); + clazz.createIndex(className + ".surname", OClass.INDEX_TYPE.NOTUNIQUE, "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + + " where foo < 100 and ((name = 'name2' and foo < 20) or surname = 'surname3') and ( 4<5 and foo < 50)"); + printExecutionPlan(result); + + Assert.assertTrue(result.hasNext()); + for (int i = 0; i < 2; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertTrue("name2".equals(next.getProperty("name")) || ("surname3".equals(next.getProperty("surname")))); + } + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes5() { + String className = "testFetchFromClassWithIndexes5"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name3' and surname >= 'surname1'"); + printExecutionPlan(result); + + Assert.assertTrue(result.hasNext()); + for (int i = 0; i < 1; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertEquals("name3", next.getProperty("name")); + } + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes6() { + String className = "testFetchFromClassWithIndexes6"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name3' and surname > 'surname3'"); + printExecutionPlan(result); + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes7() { + String className = "testFetchFromClassWithIndexes7"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name3' and surname >= 'surname3'"); + printExecutionPlan(result); + for (int i = 0; i < 1; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertEquals("name3", next.getProperty("name")); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes8() { + String className = "testFetchFromClassWithIndexes8"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name3' and surname < 'surname3'"); + printExecutionPlan(result); + + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes9() { + String className = "testFetchFromClassWithIndexes9"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name = 'name3' and surname <= 'surname3'"); + printExecutionPlan(result); + for (int i = 0; i < 1; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + Assert.assertEquals("name3", next.getProperty("name")); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + + @Test public void testFetchFromClassWithIndexes10() { + String className = "testFetchFromClassWithIndexes10"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name > 'name3' "); + printExecutionPlan(result); + for (int i = 0; i < 6; i++) { + Assert.assertTrue(result.hasNext()); + OResult next = result.next(); + Assert.assertNotNull(next); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes11() { + String className = "testFetchFromClassWithIndexes11"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name >= 'name3' "); + printExecutionPlan(result); + for (int i = 0; i < 7; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes12() { + String className = "testFetchFromClassWithIndexes12"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name < 'name3' "); + printExecutionPlan(result); + for (int i = 0; i < 3; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes13() { + String className = "testFetchFromClassWithIndexes13"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name <= 'name3' "); + printExecutionPlan(result); + for (int i = 0; i < 4; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + } + Assert.assertFalse(result.hasNext()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes14() { + String className = "testFetchFromClassWithIndexes14"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name > 'name3' and name < 'name5'"); + printExecutionPlan(result); + for (int i = 0; i < 1; i++) { + OResult next = result.next(); + Assert.assertNotNull(next); + } + Assert.assertFalse(result.hasNext()); + OSelectExecutionPlan plan = (OSelectExecutionPlan) result.getExecutionPlan().get(); + Assert.assertEquals(1, plan.getSteps().size()); + result.close(); + } + + @Test public void testFetchFromClassWithIndexes15() { + String className = "testFetchFromClassWithIndexes15"; + OClass clazz = db.getMetadata().getSchema().createClass(className); + clazz.createProperty("name", OType.STRING); + clazz.createProperty("surname", OType.STRING); + clazz.createIndex(className + ".name_surname", OClass.INDEX_TYPE.NOTUNIQUE, "name", "surname"); + + for (int i = 0; i < 10; i++) { + ODocument doc = db.newInstance(className); + doc.setProperty("name", "name" + i); + doc.setProperty("surname", "surname" + i); + doc.setProperty("foo", i); + doc.save(); + } + + OTodoResultSet result = db.query("select from " + className + " where name > 'name6' and name = 'name3' and surname > 'surname2' and surname < 'surname5' "); + printExecutionPlan(result); + Assert.assertFalse(result.hasNext()); + OSelectExecutionPlan plan = (OSelectExecutionPlan) result.getExecutionPlan().get(); + Assert.assertEquals(2, plan.getSteps().size()); + result.close(); + } + + public void stressTestNew() { String className = "stressTestNew"; db.getMetadata().getSchema().createClass(className);