diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index 69664ce9f7..417166aea5 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -1012,7 +1012,7 @@ AstNode* Ast::createNodeValueString (char const* value, THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } - if (*value == '\0') { + if (length == 0) { // performance optimization: // return a pointer to the singleton empty string node // note: these nodes are never registered nor freed diff --git a/js/common/tests/shell-attributes.js b/js/common/tests/shell-attributes.js index b148ff1826..eb3796f917 100644 --- a/js/common/tests/shell-attributes.js +++ b/js/common/tests/shell-attributes.js @@ -485,6 +485,87 @@ function AttributesSuite () { assertEqual("foo\u0001bar\u0000baz", result[0].abc); assertEqual("\u0000test\u0000test", result[0].def); assertEqual("abc\u0000", result[0]["123"]); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test values containing NULL bytes +//////////////////////////////////////////////////////////////////////////////// + + testNullBytesValues : function () { + var docs = [ + { value: "\u0000zero\u0000", _key: "first" }, + { value: "\u0000\r\nnew line", _key: "second" }, + { value: "\u0000zero", _key: "third" }, + { value: "0\r\nxxx", _key: "fourth" } + ]; + + docs.forEach(function(doc) { + c.insert(doc); + }); + + // test return value of document() + docs.forEach(function(doc) { + var d = c.document(doc._key); + assertEqual(doc.value, d.value); + }); + + // test return value of AQL, using literals + docs.forEach(function(doc) { + var result = db._query("FOR doc IN @@collection FILTER doc.value == " + + JSON.stringify(doc.value) + " RETURN doc", { + "@collection" : c.name() + }).toArray(); + + assertEqual(1, result.length, doc); + assertEqual(doc.value, result[0].value); + assertEqual(doc._key, result[0]._key); + }); + + // test return value of AQL, using bind parameters + docs.forEach(function(doc) { + var result = db._query("FOR doc IN @@collection FILTER doc.value == @value RETURN doc", { + "@collection" : c.name(), + value: doc.value + }).toArray(); + + assertEqual(1, result.length, doc); + assertEqual(doc.value, result[0].value); + assertEqual(doc._key, result[0]._key); + }); + + // test return value of AQL + var result; + result = db._query("FOR doc IN @@collection FILTER SUBSTRING(doc.value, 0, 5) == @value RETURN doc._key", { + "@collection" : c.name(), + value: "\u0000zero" + }).toArray().sort(); + + assertEqual(2, result.length); + assertEqual([ "first", "third" ], result); + + result = db._query("FOR doc IN @@collection FILTER SUBSTRING(doc.value, 0, 6) == @value RETURN doc._key", { + "@collection" : c.name(), + value: "\u0000zero\u0000" + }).toArray(); + + assertEqual(1, result.length); + assertEqual([ "first" ], result); + + result = db._query("FOR doc IN @@collection FILTER SUBSTRING(doc.value, 0, 1) == " + + JSON.stringify("\0") + " RETURN doc._key", { + "@collection" : c.name() + }).toArray().sort(); + + assertEqual(3, result.length); + assertEqual([ "first", "second", "third" ], result); + + result = db._query("FOR doc IN @@collection FILTER SUBSTRING(doc.value, 0, 1) == @value RETURN doc._key", { + "@collection" : c.name(), + value: "\0", + }).toArray().sort(); + + assertEqual(3, result.length); + assertEqual([ "first", "second", "third" ], result); } }; diff --git a/lib/Basics/json-utilities.cpp b/lib/Basics/json-utilities.cpp index 712b76c306..fb079bf1e0 100644 --- a/lib/Basics/json-utilities.cpp +++ b/lib/Basics/json-utilities.cpp @@ -244,14 +244,18 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs, case TRI_JSON_STRING_REFERENCE: { // same for STRING and STRING_REFERENCE int res; + size_t const nl = lhs->_value._string.length - 1; + size_t const nr = rhs->_value._string.length - 1; if (useUTF8) { res = TRI_compare_utf8(lhs->_value._string.data, - lhs->_value._string.length - 1, + nl, rhs->_value._string.data, - rhs->_value._string.length - 1); + nr); } else { - res = strcmp(lhs->_value._string.data, rhs->_value._string.data); + // beware of strings containing NUL bytes + size_t len = nl < nr ? nl : nr; + res = memcmp(lhs->_value._string.data, rhs->_value._string.data, len); } if (res < 0) { return -1; @@ -259,7 +263,12 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs, else if (res > 0) { return 1; } - return 0; + // res == 0 + if (nl == nr) { + return 0; + } + // res == 0, but different string lengths + return nl < nr ? -1 : 1; } case TRI_JSON_ARRAY: {