From a2c4cc9c95b86583b32dc4a339a4d305a9468264 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 27 Dec 2019 11:25:10 +0100 Subject: [PATCH] Fix TRAVERSE in batch, with newly created vertices and edges (in tx) Resolves: #9096 --- .../sql/executor/DepthFirstTraverseStep.java | 16 ++++++++-- .../orient/core/sql/executor/ORidSet.java | 16 ++++++---- .../core/sql/executor/ORidSetIterator.java | 7 +++- .../OTraverseStatementExecutionTest.java | 32 +++++++++++++++++++ 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/DepthFirstTraverseStep.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/DepthFirstTraverseStep.java index dc5971f2fa9..f15bf5b0f53 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/DepthFirstTraverseStep.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/DepthFirstTraverseStep.java @@ -2,6 +2,7 @@ import com.orientechnologies.orient.core.command.OCommandContext; import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.db.record.ridbag.ORidBag; import com.orientechnologies.orient.core.sql.parser.OInteger; import com.orientechnologies.orient.core.sql.parser.OTraverseProjectionItem; import com.orientechnologies.orient.core.sql.parser.OWhereClause; @@ -57,7 +58,7 @@ private OResult toTraverseResult(OResult item) { OTraverseResult res = null; if (item instanceof OTraverseResult) { res = (OTraverseResult) item; - } else if (item.isElement() && item.getElement().get().getIdentity().isPersistent()) { + } else if (item.isElement() && item.getElement().get().getIdentity().isValid()) { res = new OTraverseResult(); res.setElement(item.getElement().get()); res.depth = 0; @@ -72,7 +73,7 @@ private OResult toTraverseResult(OResult item) { } else { res = new OTraverseResult(); for (String key : item.getPropertyNames()) { - res.setProperty(key, item.getProperty(key)); + res.setProperty(key, convert(item.getProperty(key))); } for (String md : item.getMetadataKeys()) { res.setMetadata(md, item.getMetadata(md)); @@ -82,7 +83,16 @@ private OResult toTraverseResult(OResult item) { return res; } - @Override + public Object convert(Object value) { + if (value instanceof ORidBag) { + List result = new ArrayList(); + ((ORidBag) value).forEach(x -> result.add(x)); + return result; + } + return value; + } + + @Override protected void fetchNextResults(OCommandContext ctx, int nRecords) { if (!this.entryPoints.isEmpty()) { OTraverseResult item = (OTraverseResult) this.entryPoints.remove(0); diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSet.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSet.java index 41157ae59ff..08ada4d0858 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSet.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSet.java @@ -3,6 +3,7 @@ import com.orientechnologies.orient.core.id.ORID; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -24,6 +25,8 @@ public class ORidSet implements Set { */ protected long[][][] content = new long[8][][]; + protected Set negatives = new HashSet<>(); + long size = 0; protected int maxArraySize; @@ -37,14 +40,14 @@ public ORidSet() { /** * - * @param bucketSize + * @param bucketSize */ public ORidSet(int bucketSize) { maxArraySize = bucketSize; } @Override public int size() { - return size <= Integer.MAX_VALUE ? (int) size : Integer.MAX_VALUE; + return size + negatives.size() <= Integer.MAX_VALUE ? (int) size + negatives.size() : Integer.MAX_VALUE; } @Override public boolean isEmpty() { @@ -52,7 +55,7 @@ public ORidSet(int bucketSize) { } @Override public boolean contains(Object o) { - if (size == 0L) { + if (size == 0L && negatives.size() == 0) { return false; } if (!(o instanceof ORID)) { @@ -65,7 +68,7 @@ public ORidSet(int bucketSize) { int cluster = identifiable.getClusterId(); long position = identifiable.getClusterPosition(); if (cluster < 0 || position < 0) { - return false; + return negatives.contains(identifiable); } long positionByte = (position / 63); int positionBit = (int) (position % 63); @@ -113,7 +116,7 @@ public ORidSet(int bucketSize) { int cluster = identifiable.getClusterId(); long position = identifiable.getClusterPosition(); if (cluster < 0 || position < 0) { - throw new IllegalArgumentException("negative RID");//TODO + return negatives.add(identifiable); } long positionByte = (position / 63); int positionBit = (int) (position % 63); @@ -195,7 +198,7 @@ private static long[] expandClusterArray(long[] original, int positionByteInt) { int cluster = identifiable.getClusterId(); long position = identifiable.getClusterPosition(); if (cluster < 0 || position < 0) { - throw new IllegalArgumentException("negative RID");//TODO + return negatives.remove(o); } long positionByte = (position / 63); int positionBit = (int) (position % 63); @@ -258,6 +261,7 @@ private static long[] expandClusterArray(long[] original, int positionByteInt) { @Override public void clear() { content = new long[8][][]; size = 0; + this.negatives.clear(); } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSetIterator.java b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSetIterator.java index d09fbcd2c7b..dabe8717453 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSetIterator.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/executor/ORidSetIterator.java @@ -10,20 +10,25 @@ */ public class ORidSetIterator implements Iterator { + private final Iterator negativesIterator; private ORidSet set; int currentCluster = -1; long currentId = -1; ORidSetIterator(ORidSet set) { this.set = set; + this.negativesIterator = set.negatives.iterator(); fetchNext(); } @Override public boolean hasNext() { - return currentCluster >= 0; + return negativesIterator.hasNext() || currentCluster >= 0; } @Override public ORID next() { + if (negativesIterator.hasNext()) { + return negativesIterator.next(); + } if (!hasNext()) { throw new IllegalStateException(); } diff --git a/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OTraverseStatementExecutionTest.java b/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OTraverseStatementExecutionTest.java index 8145ade9194..ef57f58b9a5 100644 --- a/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OTraverseStatementExecutionTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/sql/executor/OTraverseStatementExecutionTest.java @@ -7,6 +7,8 @@ import org.junit.BeforeClass; import org.junit.Test; +import java.util.Collection; + /** * @author Luigi Dell'Aquila (l.dellaquila-(at)-orientdb.com) */ @@ -165,4 +167,34 @@ public void testBreadthFirst() { result.close(); } + + @Test + public void testTraverseInBatchTx(){ + String script = ""; + script += ""; + + script += "drop class testTraverseInBatchTx_V if exists unsafe;"; + script += "create class testTraverseInBatchTx_V extends V;"; + script += "create property testTraverseInBatchTx_V.name STRING;"; + script += "drop class testTraverseInBatchTx_E if exists unsafe;"; + script += "create class testTraverseInBatchTx_E extends 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"; + + OResultSet result = db.execute("sql", script); + Assert.assertTrue(result.hasNext()); + OResult item = result.next(); + Object val = item.getProperty("value"); + Assert.assertTrue(val instanceof Collection); + Assert.assertEquals(1, ((Collection)val).size()); + result.close(); + + } }