diff --git a/CHANGELOG b/CHANGELOG index 42a2d98e44..c36a4b2b51 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ v3.3.4 (XXXX-XX-XX) ------------------- +* fix issue #4677: AQL WITH with bind parameters results in "access after data-modification" + for two independent UPSERTs + * fix issue #4457: create /var/tmp/arangod with correct user in supervisor mode * remove long disfunctional admin/long_echo handler diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index e963352dbf..43fa3113df 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -1700,7 +1700,7 @@ void Ast::validateAndOptimize() { if (ctx->filterDepth >= 0) { ++ctx->filterDepth; } - + if (node->type == NODE_TYPE_FILTER) { TRI_ASSERT(ctx->filterDepth == -1); ctx->filterDepth = 0; @@ -1712,6 +1712,12 @@ void Ast::validateAndOptimize() { // NOOPT will turn all function optimizations off ++(ctx->stopOptimizationRequests); } + } else if (node->type == NODE_TYPE_COLLECTION_LIST) { + // a collection list is produced by WITH a, b, c + // or by traversal declarations + // we do not want to descend further, in order to not track + // the collections in collectionsFirstSeen + return false; } else if (node->type == NODE_TYPE_COLLECTION) { // note the level on which we first saw a collection ctx->collectionsFirstSeen.emplace(node->getString(), ctx->nestingLevel); diff --git a/js/server/tests/aql/aql-multi-modify.js b/js/server/tests/aql/aql-multi-modify.js index 3e3b6a54e2..9d6eb651d5 100644 --- a/js/server/tests/aql/aql-multi-modify.js +++ b/js/server/tests/aql/aql-multi-modify.js @@ -120,6 +120,52 @@ function ahuacatlMultiModifySuite () { assertTrue(found); }, + testWithDeclarationAndModification : function () { + var q = "WITH @@cn RETURN (INSERT {} INTO @@cn)"; + var actual = AQL_EXECUTE(q, { "@cn": cn1 }); + assertEqual([ [ ] ], actual.json); + assertEqual(1, actual.stats.writesExecuted); + assertEqual(1, c1.count()); + assertEqual(0, c2.count()); + }, + + testWithDeclarationsAndSingleModificationMultipleCollections : function () { + var q = "WITH @@cn1, @@cn2 RETURN (INSERT {} INTO @@cn1)"; + var actual = AQL_EXECUTE(q, { "@cn1": cn1, "@cn2" : cn2 }); + assertEqual([ [ ] ], actual.json); + assertEqual(1, actual.stats.writesExecuted); + assertEqual(1, c1.count()); + assertEqual(0, c2.count()); + }, + + testWithDeclarationsAndMultiModificationMultipleCollections : function () { + var q = "WITH @@cn1, @@cn2 RETURN [(INSERT {} INTO @@cn1), (INSERT {} INTO @@cn2)]"; + var actual = AQL_EXECUTE(q, { "@cn1": cn1, "@cn2" : cn2 }); + assertEqual([ [ [ ], [ ] ] ], actual.json); + assertEqual(2, actual.stats.writesExecuted); + assertEqual(1, c1.count()); + assertEqual(1, c2.count()); + }, + + testWithDeclarationsAndModificationWriteRead : function () { + var q = "WITH @@cn1, @@cn2 RETURN [(INSERT {} INTO @@cn1), (FOR doc IN @@cn2 RETURN doc)]"; + var actual = AQL_EXECUTE(q, { "@cn1": cn1, "@cn2" : cn2 }); + assertEqual([ [ [ ], [ ] ] ], actual.json); + assertEqual(1, actual.stats.writesExecuted); + assertEqual(1, c1.count()); + assertEqual(0, c2.count()); + }, + + testWithDeclarationSameCollectionWriteThenRead : function () { + var q = "WITH @@cn1 RETURN [(INSERT {} INTO @@cn1), (FOR doc IN @@cn1 RETURN doc)]"; + assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, q, { "@cn1": cn1 }); + }, + + testWithDeclarationAndModificationMultipleCollectionsThenRead : function () { + var q = "WITH @@cn1 LET x = (INSERT {} INTO @@cn1) FOR doc IN @@cn1 RETURN doc"; + assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, q, { "@cn1": cn1 }); + }, + testTraversalAfterModification : function () { var q = "INSERT { _key: '1', foo: 'bar' } INTO @@cn FOR doc IN OUTBOUND 'v/1' @@e RETURN doc"; assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, q, { "@cn": cn1, "@e": cn3 });