diff --git a/3rdParty/velocypack/include/velocypack/Slice.h b/3rdParty/velocypack/include/velocypack/Slice.h index 845518d958..6a8822627e 100644 --- a/3rdParty/velocypack/include/velocypack/Slice.h +++ b/3rdParty/velocypack/include/velocypack/Slice.h @@ -489,6 +489,12 @@ class Slice { return Slice(current); } + // tests whether the Slice is an empty array + bool isEmptyArray() const noexcept { return head() == 0x01; } + + // tests whether the Slice is an empty object + bool isEmptyObject() const noexcept { return head() == 0x0a; } + // translates an integer key into a string Slice translate() const; diff --git a/arangod/Utils/Transaction.cpp b/arangod/Utils/Transaction.cpp index f9aac39d3c..216b33ee2a 100644 --- a/arangod/Utils/Transaction.cpp +++ b/arangod/Utils/Transaction.cpp @@ -761,6 +761,10 @@ VPackSlice Transaction::extractKeyFromDocument(VPackSlice slice) { slice = slice.resolveExternal(); } TRI_ASSERT(slice.isObject()); + + if (slice.isEmptyObject()) { + return VPackSlice(); + } // a regular document must have at least the three attributes // _key, _id and _rev (in this order). _key must be the first attribute // however this method may also be called for remove markers, which only @@ -791,9 +795,12 @@ VPackSlice Transaction::extractIdFromDocument(VPackSlice slice) { slice = slice.resolveExternal(); } TRI_ASSERT(slice.isObject()); + + if (slice.isEmptyObject()) { + return VPackSlice(); + } // a regular document must have at least the three attributes // _key, _id and _rev (in this order). _id must be the second attribute - TRI_ASSERT(slice.length() >= 2); uint8_t const* p = slice.begin() + slice.findDataOffset(slice.head()); @@ -824,9 +831,12 @@ VPackSlice Transaction::extractFromFromDocument(VPackSlice slice) { slice = slice.resolveExternal(); } TRI_ASSERT(slice.isObject()); + + if (slice.isEmptyObject()) { + return VPackSlice(); + } // this method must only be called on edges // this means we must have at least the attributes _key, _id, _from, _to and _rev - TRI_ASSERT(slice.length() >= 5); uint8_t const* p = slice.begin() + slice.findDataOffset(slice.head()); VPackValueLength count = 0; @@ -857,9 +867,12 @@ VPackSlice Transaction::extractToFromDocument(VPackSlice slice) { if (slice.isExternal()) { slice = slice.resolveExternal(); } + + if (slice.isEmptyObject()) { + return VPackSlice(); + } // this method must only be called on edges // this means we must have at least the attributes _key, _id, _from, _to and _rev - TRI_ASSERT(slice.length() >= 5); uint8_t const* p = slice.begin() + slice.findDataOffset(slice.head()); VPackValueLength count = 0; diff --git a/js/server/tests/aql/aql-optimizer-indexes.js b/js/server/tests/aql/aql-optimizer-indexes.js index ccccf9933c..0ab13e18f8 100644 --- a/js/server/tests/aql/aql-optimizer-indexes.js +++ b/js/server/tests/aql/aql-optimizer-indexes.js @@ -226,6 +226,216 @@ function optimizerIndexesTestSuite () { assertEqual(0, results.stats.scannedIndex); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _key +//////////////////////////////////////////////////////////////////////////////// + + testFakeKey : function () { + var query = "LET t = { _key: 'test12' } FOR i IN " + c.name() + " FILTER i._key == t._key RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _key +//////////////////////////////////////////////////////////////////////////////// + + testFakeKeyNonConst : function () { + var query = "LET t = NOOPT({ _key: 'test12' }) FOR i IN " + c.name() + " FILTER i._key == t._key RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _id +//////////////////////////////////////////////////////////////////////////////// + + testFakeId : function () { + var query = "LET t = { _id: 'test12' } FOR i IN " + c.name() + " FILTER i._key == t._id RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _id +//////////////////////////////////////////////////////////////////////////////// + + testFakeIdNonConst : function () { + var query = "LET t = NOOPT({ _id: 'test12' }) FOR i IN " + c.name() + " FILTER i._key == t._id RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _rev +//////////////////////////////////////////////////////////////////////////////// + + testFakeRev : function () { + var query = "LET t = { _rev: 'test12' } FOR i IN " + c.name() + " FILTER i._key == t._rev RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _rev +//////////////////////////////////////////////////////////////////////////////// + + testFakeRevNonConst : function () { + var query = "LET t = NOOPT({ _rev: 'test12' }) FOR i IN " + c.name() + " FILTER i._key == t._rev RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _from +//////////////////////////////////////////////////////////////////////////////// + + testFakeFrom : function () { + var query = "LET t = { _from: 'test12' } FOR i IN " + c.name() + " FILTER i._key == t._from RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _from +//////////////////////////////////////////////////////////////////////////////// + + testFakeFromNonConst : function () { + var query = "LET t = NOOPT({ _from: 'test12' }) FOR i IN " + c.name() + " FILTER i._key == t._from RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _to +//////////////////////////////////////////////////////////////////////////////// + + testFakeTo : function () { + var query = "LET t = { _to: 'test12' } FOR i IN " + c.name() + " FILTER i._key == t._to RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test fake _to +//////////////////////////////////////////////////////////////////////////////// + + testFakeToNonConst : function () { + var query = "LET t = NOOPT({ _to: 'test12' }) FOR i IN " + c.name() + " FILTER i._key == t._to RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query); + + var results = AQL_EXECUTE(query); + assertEqual([ 12 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertEqual(1, results.stats.scannedIndex); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test index usage ////////////////////////////////////////////////////////////////////////////////