summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/ops/SConscript4
-rw-r--r--src/mongo/db/ops/field_checker.cpp72
-rw-r--r--src/mongo/db/ops/field_checker.h55
-rw-r--r--src/mongo/db/ops/field_checker_test.cpp118
4 files changed, 247 insertions, 2 deletions
diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript
index 23cde6d7c12..7eb9e5f64e8 100644
--- a/src/mongo/db/ops/SConscript
+++ b/src/mongo/db/ops/SConscript
@@ -2,7 +2,8 @@
Import("env")
-env.StaticLibrary('update', ['path_support.cpp'],
+env.StaticLibrary('update', ['field_checker.cpp',
+ 'path_support.cpp'],
LIBDEPS=['$BUILD_DIR/mongo/bson',
'$BUILD_DIR/mongo/foundation',
'$BUILD_DIR/mongo/db/common'])
@@ -11,4 +12,3 @@ env.CppUnitTest('field_checker_test', ['field_checker_test.cpp'], LIBDEPS=['upda
env.CppUnitTest('path_support_test', ['path_support_test.cpp'],
LIBDEPS=['update', '$BUILD_DIR/mongo/mutable_bson_test_utils'])
-
diff --git a/src/mongo/db/ops/field_checker.cpp b/src/mongo/db/ops/field_checker.cpp
new file mode 100644
index 00000000000..7c097a8ac95
--- /dev/null
+++ b/src/mongo/db/ops/field_checker.cpp
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2013 10gen 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/>.
+ */
+
+#include "mongo/db/ops/field_checker.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/db/field_ref.h"
+
+namespace mongo {
+namespace fieldchecker {
+
+ Status basicIsUpdatable(const FieldRef& field) {
+ StringData firstPart = field.getPart(0);
+ if (firstPart.compare("_id") == 0) {
+ return Status(ErrorCodes::BadValue, "updated cannot affect the _id");
+ }
+ return Status::OK();
+ }
+
+ Status isUpdatable(const FieldRef& field) {
+ Status status = basicIsUpdatable(field);
+ if (! status.isOK()) {
+ return status;
+ }
+
+ StringData firstPart = field.getPart(0);
+ if (firstPart[0] == '$') {
+ return Status(ErrorCodes::BadValue, "field name cannot start with $");
+ }
+
+ return Status::OK();
+ }
+
+ Status isUpdatableLegacy(const FieldRef& field) {
+ return basicIsUpdatable(field);
+ }
+
+ bool isPositional(const FieldRef& fieldRef, size_t* pos, size_t* count) {
+
+ // 'count' is optional.
+ size_t dummy;
+ if (count == NULL) {
+ count = &dummy;
+ }
+
+ *count = 0;
+ size_t size = fieldRef.numParts();
+ for (size_t i=0; i<size; i++) {
+ StringData fieldPart = fieldRef.getPart(i);
+ if ((fieldPart.size() == 1) && (fieldPart[0] == '$')) {
+ if (*count == 0) *pos = i;
+ (*count)++;
+ }
+ }
+ return *count > 0;
+ }
+
+} // namespace fieldchecker
+} // namespace mongo
diff --git a/src/mongo/db/ops/field_checker.h b/src/mongo/db/ops/field_checker.h
new file mode 100644
index 00000000000..1a375e16942
--- /dev/null
+++ b/src/mongo/db/ops/field_checker.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2013 10gen 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/>.
+ */
+
+#pragma once
+
+#include "mongo/base/status.h"
+
+namespace mongo {
+
+ class FieldRef;
+
+ namespace fieldchecker {
+
+ /**
+ * Returns OK if all the below conditions on 'field' are valid:
+ * + Non-empty
+ * + Not the _id field (or a subfield of the _id field, such as _id.x.y)
+ * + Does not start or end with a '.'
+ * + Does not start with a $
+ * Otherwise returns a code indicating cause of failure.
+ */
+ Status isUpdatable(const FieldRef& field);
+
+ /**
+ * Same behavior of isUpdatable but allowing update fields to start with '$'. This
+ * supports $unset on legacy fields.
+ */
+ Status isUpdatableLegacy(const FieldRef& field);
+
+ /**
+ * Returns true, the position 'pos' of the first $-sign if present in 'fieldRef', and
+ * how many other $-signs were found in 'count'. Otherwise return false.
+ *
+ * Note:
+ * isPositional assumes that the field is updatable. Call isUpdatable() above to
+ * verify.
+ */
+ bool isPositional(const FieldRef& fieldRef, size_t* pos, size_t* count = NULL);
+
+ } // namespace fieldchecker
+
+} // namespace mongo
diff --git a/src/mongo/db/ops/field_checker_test.cpp b/src/mongo/db/ops/field_checker_test.cpp
new file mode 100644
index 00000000000..715cc5c45ea
--- /dev/null
+++ b/src/mongo/db/ops/field_checker_test.cpp
@@ -0,0 +1,118 @@
+/**
+ * Copyright 2013 10gen Inc.
+ *
+ * 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.
+ */
+
+#include "mongo/db/ops/field_checker.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/base/status.h"
+#include "mongo/db/field_ref.h"
+#include "mongo/unittest/unittest.h"
+
+namespace {
+
+ using mongo::ErrorCodes;
+ using mongo::FieldRef;
+ using mongo::fieldchecker::isUpdatable;
+ using mongo::fieldchecker::isUpdatableLegacy;
+ using mongo::fieldchecker::isPositional;
+ using mongo::Status;
+
+ TEST(IsUpdatable, Basics) {
+ FieldRef fieldRef;
+ fieldRef.parse("x");
+ ASSERT_OK(isUpdatable(fieldRef));
+ }
+
+ TEST(IsUpdatable, DottedFields) {
+ FieldRef fieldRef;
+ fieldRef.parse("x.y.z");
+ ASSERT_OK(isUpdatable(fieldRef));
+ }
+
+ TEST(IsUpdatable, SpecialIDField) {
+ FieldRef fieldRefID;
+ fieldRefID.parse("_id");
+ ASSERT_NOT_OK(isUpdatable(fieldRefID));
+
+ FieldRef fieldRefIDX;
+ fieldRefIDX.parse("_id.x");
+ ASSERT_NOT_OK(isUpdatable(fieldRefIDX));
+
+ FieldRef fieldRefXID;
+ fieldRefXID.parse("x._id");
+ ASSERT_OK(isUpdatable(fieldRefXID));
+
+ FieldRef fieldRefXIDZ;
+ fieldRefXIDZ.parse("x._id.z");
+ ASSERT_OK(isUpdatable(fieldRefXIDZ));
+ }
+
+ TEST(IsUpdatable, PositionalFields) {
+ FieldRef fieldRefXDollar;
+ fieldRefXDollar.parse("x.$");
+ ASSERT_OK(isUpdatable(fieldRefXDollar));
+
+ FieldRef fieldRefXDollarZ;
+ fieldRefXDollarZ.parse("x.$.z");
+ ASSERT_OK(isUpdatable(fieldRefXDollarZ));
+
+ // A document never starts with an array.
+ FieldRef fieldRefDollarB;
+ fieldRefDollarB.parse("$.b");
+ ASSERT_NOT_OK(isUpdatable(fieldRefDollarB));
+
+ FieldRef fieldRefDollar;
+ fieldRefDollar.parse("$foo");
+ ASSERT_NOT_OK(isUpdatable(fieldRefDollar));
+ }
+
+ TEST(IsUpdatableLegacy, Basics) {
+ FieldRef fieldRefDollar;
+ fieldRefDollar.parse("$foo");
+ ASSERT_OK(isUpdatableLegacy(fieldRefDollar));
+ }
+
+ TEST(isPositional, EntireArrayItem) {
+ FieldRef fieldRefPositional;
+ fieldRefPositional.parse("a.$");
+ size_t pos;
+ size_t count;
+ ASSERT_TRUE(isPositional(fieldRefPositional, &pos, &count));
+ ASSERT_EQUALS(pos, 1u);
+ ASSERT_EQUALS(count, 1u);
+ }
+
+ TEST(isPositional, ArraySubObject) {
+ FieldRef fieldRefPositional;
+ fieldRefPositional.parse("a.$.b");
+ size_t pos;
+ size_t count;
+ ASSERT_TRUE(isPositional(fieldRefPositional, &pos, &count));
+ ASSERT_EQUALS(pos, 1u);
+ ASSERT_EQUALS(count, 1u);
+ }
+
+ TEST(isPositional, MultiplePositional) {
+ FieldRef fieldRefPositional;
+ fieldRefPositional.parse("a.$.b.$.c");
+ size_t pos;
+ size_t count;
+ ASSERT_TRUE(isPositional(fieldRefPositional, &pos, &count));
+ ASSERT_EQUALS(pos, 1u);
+ ASSERT_EQUALS(count, 2u);
+ }
+
+} // unnamed namespace