/*jshint globalstrict:false, strict:false, maxlen: 500 */ /*global assertEqual, AQL_EXPLAIN */ //////////////////////////////////////////////////////////////////////////////// /// @brief tests for Ahuacatl, subqueries /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 triagens GmbH, Cologne, Germany /// /// 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. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// var jsunity = require("jsunity"); var helper = require("@arangodb/aql-helper"); var getQueryResults = helper.getQueryResults; var findExecutionNodes = helper.findExecutionNodes; const { db } = require("@arangodb"); //////////////////////////////////////////////////////////////////////////////// /// @brief test suite //////////////////////////////////////////////////////////////////////////////// function ahuacatlSubqueryTestSuite () { return { //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryReference1 : function () { var expected = [ [ [ 1 ], [ 1 ] ] ]; var actual = getQueryResults("LET a = (RETURN 1) LET b = a RETURN [ a, b ]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryReference2 : function () { var expected = [ [ [ 1 ], [ 1 ], [ 1 ] ] ]; var actual = getQueryResults("LET a = (RETURN 1) LET b = a LET c = b RETURN [ a, b, c ]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryReference3 : function () { var expected = [ [ [ 1 ], [ 1 ], [ 1 ] ] ]; var actual = getQueryResults("LET a = (RETURN 1) LET b = a LET c = a RETURN [ a, b, c ]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryReferences1 : function () { var expected = [ [ 1 ] ]; var actual = getQueryResults("LET a = true ? (RETURN 1) : (RETURN 0) RETURN a"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryReferences2 : function () { var expected = [ [ 1 ] ]; var actual = getQueryResults("LET a = true ? (RETURN 1) : (RETURN 0) LET b = a RETURN b"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryDependent1 : function () { var expected = [ 2, 4, 6 ]; var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 1, 2, 3 ] RETURN i * 2) RETURN s[i - 1]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryDependent2 : function () { var expected = [ 2, 4, 6 ]; var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET t = (FOR k IN [ 1, 2, 3 ] RETURN k) LET s = (FOR j IN [ 1, 2, 3 ] RETURN t[i - 1] * 2) RETURN s[i - 1]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery evaluation //////////////////////////////////////////////////////////////////////////////// testSubqueryDependent3 : function () { var expected = [ 4, 5, 6, 8, 10, 12, 12, 15, 18 ]; var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] FOR j IN [ 4, 5, 6 ] LET s = (FOR k IN [ 1 ] RETURN i * j) RETURN s[0]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery optimization //////////////////////////////////////////////////////////////////////////////// testSubqueryIndependent1 : function () { var expected = [ 9, 8, 7 ]; var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 9, 8, 7 ] RETURN j) RETURN s[i - 1]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery optimization //////////////////////////////////////////////////////////////////////////////// testSubqueryIndependent2 : function () { var expected = [ [ 9, 8, 7 ] ]; var actual = getQueryResults("LET s = (FOR j IN [ 9, 8, 7 ] RETURN j) RETURN s"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery optimization //////////////////////////////////////////////////////////////////////////////// testSubqueryIndependent3 : function () { var expected = [ [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ] ]; var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] FOR j IN [ 1, 2, 3 ] LET s = (FOR k IN [ 25 ] RETURN k) RETURN s"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery optimization //////////////////////////////////////////////////////////////////////////////// testSubqueryIndependent4 : function () { var expected = [ [ 2, 4, 6 ] ]; var actual = getQueryResults("LET a = (FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 1, 2 ] RETURN j) RETURN i * s[1]) RETURN a"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery optimization //////////////////////////////////////////////////////////////////////////////// testSubqueryOutVariableName : function () { var XPResult = AQL_EXPLAIN("FOR u IN _users LET theLetVariable = (FOR j IN _users RETURN j) RETURN theLetVariable"); var SubqueryNode = findExecutionNodes(XPResult, "SubqueryNode")[0]; assertEqual(SubqueryNode.outVariable.name, "theLetVariable"); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery as function parameter //////////////////////////////////////////////////////////////////////////////// testSubqueryAsFunctionParameter1: function () { var expected = [ [ 1, 2, 3 ] ]; var actual = getQueryResults("RETURN PASSTHRU(FOR i IN [ 1, 2, 3 ] RETURN i)"); assertEqual(expected, actual); actual = getQueryResults("RETURN PASSTHRU((FOR i IN [ 1, 2, 3 ] RETURN i))"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery as function parameter //////////////////////////////////////////////////////////////////////////////// testSubqueryAsFunctionParameter2: function () { var expected = [ 6 ]; var actual = getQueryResults("RETURN MAX(APPEND((FOR i IN [ 1, 2, 3 ] RETURN i), (FOR i IN [ 4, 5, 6 ] RETURN i)))"); assertEqual(expected, actual); actual = getQueryResults("RETURN MAX(APPEND(FOR i IN [ 1, 2, 3 ] RETURN i, FOR i IN [ 4, 5, 6 ] RETURN i))"); assertEqual(expected, actual); expected = [ 9 ]; actual = getQueryResults("RETURN MAX(APPEND(FOR i IN [ 9, 7, 8 ] RETURN i, [ 1 ]))"); assertEqual(expected, actual); expected = [ [ 1, 2, 3, 4, 5, 6 ] ]; actual = getQueryResults("RETURN UNION((FOR i IN [ 1, 2, 3 ] RETURN i), (FOR i IN [ 4, 5, 6 ] RETURN i))"); assertEqual(expected, actual); actual = getQueryResults("RETURN UNION(FOR i IN [ 1, 2, 3 ] RETURN i, FOR i IN [ 4, 5, 6 ] RETURN i)"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test copying of subquery results //////////////////////////////////////////////////////////////////////////////// testIndependentSubqueries1: function () { var expected = [ [ [ ], [ ] ] ]; // use a single block for (var i = 1; i <= 100; ++i) { expected[0][0].push(i); expected[0][1].push(i); } var actual = getQueryResults("LET a = (FOR i IN 1..100 RETURN i) LET b = (FOR i IN 1..100 RETURN i) RETURN [a, b]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test copying of subquery results //////////////////////////////////////////////////////////////////////////////// testIndependentSubqueries2: function () { var expected = [ [ [ ], [ ] ] ]; // use multiple blocks for (var i = 1; i <= 10000; ++i) { expected[0][0].push(i); expected[0][1].push(i); } var actual = getQueryResults("LET a = (FOR i IN 1..10000 RETURN i) LET b = (FOR i IN 1..10000 RETURN i) RETURN [a, b]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test copying of subquery results //////////////////////////////////////////////////////////////////////////////// testDependentSubqueries1: function () { var expected = [ [ [ ], [ ] ] ]; for (var i = 1; i <= 100; ++i) { expected[0][0].push(i); expected[0][1].push(i); } var actual = getQueryResults("LET a = (FOR i IN 1..100 RETURN i) LET b = (FOR i IN 1..100 FILTER i IN a RETURN i) RETURN [a, b]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test copying of subquery results //////////////////////////////////////////////////////////////////////////////// testDependentSubqueries2: function () { var expected = [ [ [ ], [ ] ] ]; for (var i = 1; i <= 100; ++i) { if (i < 50) { expected[0][0].push(i); } else { expected[0][1].push(i); } } var actual = getQueryResults("LET a = (FOR i IN 1..100 FILTER i < 50 RETURN i) LET b = (FOR i IN 1..100 FILTER i NOT IN a RETURN i) RETURN [a, b]"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test copying of subquery results //////////////////////////////////////////////////////////////////////////////// testDependentSubqueries3: function () { var expected = [ ]; for (var i = 1; i <= 2000; ++i) { expected.push(i); } var actual = getQueryResults("LET a = (FOR i IN 1..2000 RETURN i) FOR i IN 1..10000 FILTER i IN a RETURN i"); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief test limit with offset in a subquery /// This test was introduced due to a bug (that never made it into devel) where /// the offset in the LimitBlock was mutated during a subquery run and not reset /// in between. //////////////////////////////////////////////////////////////////////////////// testSubqueriesWithLimitAndOffset: function () { const query = ` FOR i IN 2..4 LET a = (FOR j IN [0, i, i+10] LIMIT 1, 1 RETURN j) RETURN FIRST(a)`; const expected = [ 2, 3, 4 ]; var actual = getQueryResults(query); assertEqual(expected, actual); }, //////////////////////////////////////////////////////////////////////////////// /// @brief this tests a rather complex interna of AQL execution combinations /// A subquery should only be executed if it has an input row /// A count collect block will produce an output even if it does not get an input /// specifically it will rightfully count 0. /// The insert block will write into the collection if it gets an input. /// So the assertion here is, that if a subquery has no input, than all it's /// Parts do not have side-effects, but the subquery still prduces valid results //////////////////////////////////////////////////////////////////////////////// testCollectWithinEmptyNestedSubquery: function () { const colName = "UnitTestSubqueryCollection"; try { db._create(colName); const query = ` FOR k IN 1..2 LET sub1 = ( FOR x IN [] LET sub2 = ( COLLECT WITH COUNT INTO q INSERT {counted: q} INTO ${colName} RETURN NEW ) RETURN sub2 ) RETURN [k, sub1] `; const expected = [ [1, []], [2, []]]; var actual = getQueryResults(query); assertEqual(expected, actual); assertEqual(db[colName].count(), 0); } finally { db._drop(colName); } } }; } jsunity.run(ahuacatlSubqueryTestSuite); return jsunity.done();