summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline/variables.cpp
diff options
context:
space:
mode:
authorJames Wahlin <james.wahlin@10gen.com>2017-04-03 17:11:35 -0400
committerJames Wahlin <james.wahlin@10gen.com>2017-04-04 15:24:30 -0400
commita1849d63c6a1ed7697a402bd73510fb8479f9262 (patch)
tree065770d51976b1ec19ec5cbe678929c803af0de6 /src/mongo/db/pipeline/variables.cpp
parentcea6b6072ba2432a97d5aeb56b3f9892f802668d (diff)
downloadmongo-a1849d63c6a1ed7697a402bd73510fb8479f9262.tar.gz
SERVER-28597 Move agg Variables into separate files
Diffstat (limited to 'src/mongo/db/pipeline/variables.cpp')
-rw-r--r--src/mongo/db/pipeline/variables.cpp168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp
new file mode 100644
index 00000000000..199517b5f7a
--- /dev/null
+++ b/src/mongo/db/pipeline/variables.cpp
@@ -0,0 +1,168 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/pipeline/variables.h"
+
+namespace mongo {
+
+constexpr Variables::Id Variables::kRootId;
+constexpr Variables::Id Variables::kRemoveId;
+
+const StringMap<Variables::Id> Variables::kBuiltinVarNameToId = {{"ROOT"_sd, kRootId},
+ {"REMOVE"_sd, kRemoveId}};
+
+void Variables::uassertValidNameForUserWrite(StringData varName) {
+ // System variables users allowed to write to (currently just one)
+ if (varName == "CURRENT") {
+ return;
+ }
+
+ uassert(16866, "empty variable names are not allowed", !varName.empty());
+
+ const bool firstCharIsValid =
+ (varName[0] >= 'a' && varName[0] <= 'z') || (varName[0] & '\x80') // non-ascii
+ ;
+
+ uassert(16867,
+ str::stream() << "'" << varName
+ << "' starts with an invalid character for a user variable name",
+ firstCharIsValid);
+
+ for (size_t i = 1; i < varName.size(); i++) {
+ const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') ||
+ (varName[i] >= 'A' && varName[i] <= 'Z') || (varName[i] >= '0' && varName[i] <= '9') ||
+ (varName[i] == '_') || (varName[i] & '\x80') // non-ascii
+ ;
+
+ uassert(16868,
+ str::stream() << "'" << varName << "' contains an invalid character "
+ << "for a variable name: '"
+ << varName[i]
+ << "'",
+ charIsValid);
+ }
+}
+
+void Variables::uassertValidNameForUserRead(StringData varName) {
+ uassert(16869, "empty variable names are not allowed", !varName.empty());
+
+ const bool firstCharIsValid = (varName[0] >= 'a' && varName[0] <= 'z') ||
+ (varName[0] >= 'A' && varName[0] <= 'Z') || (varName[0] & '\x80') // non-ascii
+ ;
+
+ uassert(16870,
+ str::stream() << "'" << varName
+ << "' starts with an invalid character for a variable name",
+ firstCharIsValid);
+
+ for (size_t i = 1; i < varName.size(); i++) {
+ const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') ||
+ (varName[i] >= 'A' && varName[i] <= 'Z') || (varName[i] >= '0' && varName[i] <= '9') ||
+ (varName[i] == '_') || (varName[i] & '\x80') // non-ascii
+ ;
+
+ uassert(16871,
+ str::stream() << "'" << varName << "' contains an invalid character "
+ << "for a variable name: '"
+ << varName[i]
+ << "'",
+ charIsValid);
+ }
+}
+
+void Variables::setValue(Id id, const Value& value) {
+ uassert(17199, "can't use Variables::setValue to set a reserved builtin variable", id >= 0);
+ auto varIndex = static_cast<size_t>(id);
+ invariant(varIndex < _numVars);
+ _rest[varIndex] = value;
+}
+
+Value Variables::getValue(Id id) const {
+ if (id < 0) {
+ // This is a reserved id for a builtin variable.
+ switch (id) {
+ case Variables::kRootId:
+ return Value(_root);
+ case Variables::kRemoveId:
+ return Value();
+ default:
+ MONGO_UNREACHABLE;
+ }
+ }
+
+ auto varIndex = static_cast<size_t>(id);
+ invariant(varIndex < _numVars);
+ return _rest[varIndex];
+}
+
+Document Variables::getDocument(Id id) const {
+ if (id == Variables::kRootId) {
+ // For the common case of ROOT, avoid round-tripping through Value.
+ return _root;
+ }
+
+ const Value var = getValue(id);
+ if (var.getType() == Object)
+ return var.getDocument();
+
+ return Document();
+}
+
+Variables::Id VariablesParseState::defineVariable(StringData name) {
+ // Caller should have validated before hand by using Variables::uassertValidNameForUserWrite.
+ massert(17275,
+ "Can't redefine a non-user-writable variable",
+ Variables::kBuiltinVarNameToId.find(name) == Variables::kBuiltinVarNameToId.end());
+
+ Variables::Id id = _idGenerator->generateId();
+ invariant(id >= 0);
+ _variables[name] = id;
+ return id;
+}
+
+Variables::Id VariablesParseState::getVariable(StringData name) const {
+ auto it = _variables.find(name);
+ if (it != _variables.end()) {
+ // Found a user-defined variable.
+ return it->second;
+ }
+
+ it = Variables::kBuiltinVarNameToId.find(name);
+ if (it != Variables::kBuiltinVarNameToId.end()) {
+ // This is a builtin variable.
+ return it->second;
+ }
+
+ // If we didn't find either a user-defined or builtin variable, then we reject everything other
+ // than CURRENT. If this is CURRENT, then we treat it as equivalent to ROOT.
+ uassert(17276, str::stream() << "Use of undefined variable: " << name, name == "CURRENT");
+ return Variables::kRootId;
+}
+}