diff --git a/CHANGELOG b/CHANGELOG index a3c4cae5e9..cdee3ea515 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,14 @@ v2.7.0 (XXXX-XX-XX) ------------------- +* additionally allow the following characters in document keys: + + `(` `)` `+` `,` `=` `;` `$` `!` `*` `'` `%` + + +v2.7.0-rc1 (XXXX-XX-XX) +----------------------- + * added extra AQL functions for date and time calculation and manipulation. These functions were contributed by GitHub users @CoDEmanX and @friday. A big thanks for their work! diff --git a/Documentation/Books/Users/NamingConventions/DocumentKeys.mdpp b/Documentation/Books/Users/NamingConventions/DocumentKeys.mdpp index 4e676a8b2b..0ae7627539 100644 --- a/Documentation/Books/Users/NamingConventions/DocumentKeys.mdpp +++ b/Documentation/Books/Users/NamingConventions/DocumentKeys.mdpp @@ -8,9 +8,9 @@ restrictions: * The key must be at least 1 byte and at most 254 bytes long. Empty keys are disallowed when specified (though it may be valid to completely omit the *_key* attribute from a document) -* It must consist of the letters a-z (lower or upper case), the digits 0-9, - the underscore (`_`), dash (`-`), colon (`:`), dot (.) or at-mark (`@`) characters - only +* It must consist of the letters a-z (lower or upper case), the digits 0-9 + or any of the following punctuation characters: + `_` `-` `:` `.` `@` `(` `)` `+` `,` `=` `;` `$` `!` `*` `'` `%` * Any other characters, especially multi-byte sequences, whitespace or punctuation characters cannot be used inside key values * The key must be unique within the collection it is used diff --git a/UnitTests/HttpInterface/api-document-key-spec.rb b/UnitTests/HttpInterface/api-document-key-spec.rb index 21e1b888f4..d2fbb806c8 100644 --- a/UnitTests/HttpInterface/api-document-key-spec.rb +++ b/UnitTests/HttpInterface/api-document-key-spec.rb @@ -119,24 +119,17 @@ describe ArangoDB do "\\rabcd", "abcd defg", "abcde/bdbg", + "a/a", "/a", "adbfbgb/", "öööää", "müller", - "m+ller", "\\\"invalid", - "'invalid", "\\\\invalid", "\\\\\\\\invalid", - ";invalid", - ",invalid", - "!invalid", "?invalid", - "$invalid", "#invalid", - "%invalid", "&invalid", - "(invalid)", "[invalid]", "a" * 255 ] @@ -183,12 +176,57 @@ describe ArangoDB do "-foobar", "_foobar", "@foobar", + "(valid)", + "%valid", + "$valid", + "$$bill,y'all", + "'valid", + "'a-key-is-a-key-is-a-key'", + "m+ller", + ";valid", + ",valid", + "!valid!", + ":", + ":::", + ":-:-:", + ";", + ";;;;;;;;;;", + "(", + ")", + "()xoxo()", + "%", + "%-%-%-%", + ":-)", + "!", + "!!!!", + "'", + "''''", + "this-key's-valid.", + "=", + "==================================================", + "-=-=-=___xoxox-", + "*", + "(*)", + "****", ".", "...", "-", "--", "_", "__", + "(" * 254, # 254 bytes is the maximum allowed length + ")" * 254, # 254 bytes is the maximum allowed length + "," * 254, # 254 bytes is the maximum allowed length + ":" * 254, # 254 bytes is the maximum allowed length + ";" * 254, # 254 bytes is the maximum allowed length + "*" * 254, # 254 bytes is the maximum allowed length + "=" * 254, # 254 bytes is the maximum allowed length + "-" * 254, # 254 bytes is the maximum allowed length + "%" * 254, # 254 bytes is the maximum allowed length + "@" * 254, # 254 bytes is the maximum allowed length + "'" * 254, # 254 bytes is the maximum allowed length + "." * 254, # 254 bytes is the maximum allowed length + "!" * 254, # 254 bytes is the maximum allowed length "_" * 254, # 254 bytes is the maximum allowed length "a" * 254 # 254 bytes is the maximum allowed length ] diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 823f1e69ce..f93e5dae36 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -90,6 +90,7 @@ #include "V8/v8-utils.h" #include "V8Server/ApplicationV8.h" #include "VocBase/auth.h" +#include "VocBase/KeyGenerator.h" #include "VocBase/server.h" #include "Wal/LogfileManager.h" @@ -856,10 +857,13 @@ int ArangoServer::startupServer () { LOG_FATAL_AND_EXIT("unable to start WAL logfile manager"); } + // ............................................................................. // prepare the various parts of the Arango server // ............................................................................. + KeyGenerator::Initialize(); + if (_dispatcherThreads < 1) { _dispatcherThreads = 1; } diff --git a/arangod/VocBase/KeyGenerator.cpp b/arangod/VocBase/KeyGenerator.cpp index f102146de7..40494a1cac 100644 --- a/arangod/VocBase/KeyGenerator.cpp +++ b/arangod/VocBase/KeyGenerator.cpp @@ -39,6 +39,49 @@ #include "VocBase/vocbase.h" +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookup table for key checks +//////////////////////////////////////////////////////////////////////////////// + +std::array KeyGenerator::LookupTable; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialize the lookup table for key checks +//////////////////////////////////////////////////////////////////////////////// + +void KeyGenerator::Initialize () { + for (int c = 0; c < 256; ++c) { + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_' || + c == ':' || + c == '-' || + c == '@' || + c == '.' || + c == '(' || + c == ')' || + c == '+' || + c == ',' || + c == '=' || + c == ';' || + c == '$' || + c == '!' || + c == '*' || + c == '\'' || + c == '%') { + LookupTable[c] = true; + } + else { + LookupTable[c] = false; + } + } +} + // ----------------------------------------------------------------------------- // --SECTION-- GENERAL KEY GENERATOR // ----------------------------------------------------------------------------- @@ -233,24 +276,18 @@ TraditionalKeyGenerator::~TraditionalKeyGenerator () { //////////////////////////////////////////////////////////////////////////////// bool TraditionalKeyGenerator::validateKey (char const* key) { - char const* p = key; + unsigned char const* p = reinterpret_cast(key); + unsigned char const* s = p; while (true) { - char c = *p; + unsigned char c = *p; if (c == '\0') { - return ((p - key) > 0) && - ((p - key) <= TRI_VOC_KEY_MAX_LENGTH); + return ((p - s) > 0) && + ((p - s) <= TRI_VOC_KEY_MAX_LENGTH); } - if ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '_' || - c == ':' || - c == '-' || - c == '@' || - c == '.') { + if (LookupTable[c]) { ++p; continue; } diff --git a/arangod/VocBase/KeyGenerator.h b/arangod/VocBase/KeyGenerator.h index 2be86eb586..06d951cd86 100644 --- a/arangod/VocBase/KeyGenerator.h +++ b/arangod/VocBase/KeyGenerator.h @@ -103,6 +103,12 @@ class KeyGenerator { public: +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialize the lookup table for key checks +//////////////////////////////////////////////////////////////////////////////// + + static void Initialize (); + //////////////////////////////////////////////////////////////////////////////// /// @brief get the generator type from JSON //////////////////////////////////////////////////////////////////////////////// @@ -168,6 +174,13 @@ class KeyGenerator { //////////////////////////////////////////////////////////////////////////////// bool _allowUserKeys; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookup table for key checks +//////////////////////////////////////////////////////////////////////////////// + + static std::array LookupTable; + }; // ----------------------------------------------------------------------------- diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/modules/org/arangodb/arango-database.js b/js/apps/system/_admin/aardvark/APP/frontend/js/modules/org/arangodb/arango-database.js index 308e38f144..df1f2fb0a2 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/modules/org/arangodb/arango-database.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/modules/org/arangodb/arango-database.js @@ -78,7 +78,7 @@ ArangoDatabase.indexRegex = /^([a-zA-Z0-9\-_]+)\/([0-9]+)$/; /// @brief key regex //////////////////////////////////////////////////////////////////////////////// -ArangoDatabase.keyRegex = /^([a-zA-Z0-9_:\-@\.])+$/; +ArangoDatabase.keyRegex = /^([a-zA-Z0-9_:\-@\.\(\)\+,=;\$!\*'%])+$/; //////////////////////////////////////////////////////////////////////////////// /// @brief append the waitForSync parameter to a URL diff --git a/js/client/modules/org/arangodb/arango-database.js b/js/client/modules/org/arangodb/arango-database.js index b2e68d4b8f..b75740df54 100644 --- a/js/client/modules/org/arangodb/arango-database.js +++ b/js/client/modules/org/arangodb/arango-database.js @@ -77,7 +77,7 @@ ArangoDatabase.indexRegex = /^([a-zA-Z0-9\-_]+)\/([0-9]+)$/; /// @brief key regex //////////////////////////////////////////////////////////////////////////////// -ArangoDatabase.keyRegex = /^([a-zA-Z0-9_:\-@\.])+$/; +ArangoDatabase.keyRegex = /^([a-zA-Z0-9_:\-@\.\(\)\+,=;\$!\*'%])+$/; //////////////////////////////////////////////////////////////////////////////// /// @brief append the waitForSync parameter to a URL diff --git a/js/common/tests/shell-document.js b/js/common/tests/shell-document.js index 41269c4fca..b295d12336 100644 --- a/js/common/tests/shell-document.js +++ b/js/common/tests/shell-document.js @@ -294,7 +294,7 @@ function CollectionDocumentSuite () { //////////////////////////////////////////////////////////////////////////////// testSaveInvalidDocumentKeyValue : function () { - [ "", " ", " a", "a ", "/", "|", "#", "a/a" ].forEach(function (key) { + [ "", " ", " ", " a", "a ", "/", "|", "#", "a/a", "\0", "\r", "\n", "\t", "\"", "[", "]", "{", "}", "\\" ].forEach(function (key) { try { collection.save({ _key: key }); fail(); @@ -310,7 +310,15 @@ function CollectionDocumentSuite () { //////////////////////////////////////////////////////////////////////////////// testSaveSpecialCharsDocumentKey : function () { - [ ":", "-", "_", "@", "a@b", "a@b.c", "a-b-c", "_a", "@a", "@a-b", ":80", ":_", "@:_" ].forEach(function (key) { + [ ":", "-", "_", "@", "a@b", "a@b.c", "a-b-c", "_a", "@a", "@a-b", ":80", ":_", "@:_", + "0", "1", "123456", "0123456", "true", "false", "a", "A", "a1", "A1", "01ab01", "01AB01", + "abcd-efgh", "abcd_efgh", "Abcd_Efgh", "@", "@@", "abc@foo.bar", "@..abc-@-foo__bar", + ".foobar", "-foobar", "_foobar", "@foobar", "(valid)", "%valid", "$valid", + "$$bill,y'all", "'valid", "'a-key-is-a-key-is-a-key'", "m+ller", ";valid", ",valid", "!valid!", + ":", ":::", ":-:-:", ";", ";;;;;;;;;;", "(", ")", "()xoxo()", "%", + "%-%-%-%", ":-)", "!", "!!!!", "'", "''''", "this-key's-valid.", "=", + "==================================================", "-=-=-=___xoxox-", + "*", "(*)", "****", ".", "...", "-", "--", "_", "__" ].forEach(function (key) { var doc1 = collection.save({ _key: key, value: key }); assertEqual(key, doc1._key); assertEqual(cn + "/" + key, doc1._id);