/**
* Copyright (C) 2012 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 .
*
* 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/s/catalog/type_collection.h"
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/util/bson_extract.h"
#include "mongo/util/assert_util.h"
namespace mongo {
namespace {
const BSONField kNoBalance("noBalance");
const BSONField kDropped("dropped");
} // namespace
const std::string CollectionType::ConfigNS = "config.collections";
const BSONField CollectionType::fullNs("_id");
const BSONField CollectionType::epoch("lastmodEpoch");
const BSONField CollectionType::updatedAt("lastmod");
const BSONField CollectionType::keyPattern("key");
const BSONField CollectionType::defaultCollation("defaultCollation");
const BSONField CollectionType::unique("unique");
StatusWith CollectionType::fromBSON(const BSONObj& source) {
CollectionType coll;
{
std::string collFullNs;
Status status = bsonExtractStringField(source, fullNs.name(), &collFullNs);
if (!status.isOK())
return status;
coll._fullNs = NamespaceString{collFullNs};
}
{
OID collEpoch;
Status status = bsonExtractOIDFieldWithDefault(source, epoch.name(), OID(), &collEpoch);
if (!status.isOK())
return status;
coll._epoch = collEpoch;
}
{
BSONElement collUpdatedAt;
Status status = bsonExtractTypedField(source, updatedAt.name(), Date, &collUpdatedAt);
if (!status.isOK())
return status;
coll._updatedAt = collUpdatedAt.Date();
}
{
bool collDropped;
Status status = bsonExtractBooleanField(source, kDropped.name(), &collDropped);
if (status.isOK()) {
coll._dropped = collDropped;
} else if (status == ErrorCodes::NoSuchKey) {
// Dropped can be missing in which case it is presumed false
} else {
return status;
}
}
{
BSONElement collKeyPattern;
Status status = bsonExtractTypedField(source, keyPattern.name(), Object, &collKeyPattern);
if (status.isOK()) {
BSONObj obj = collKeyPattern.Obj();
if (obj.isEmpty()) {
return Status(ErrorCodes::ShardKeyNotFound, "empty shard key");
}
coll._keyPattern = KeyPattern(obj.getOwned());
} else if (status == ErrorCodes::NoSuchKey) {
// Sharding key can only be missing if the collection is dropped
if (!coll.getDropped()) {
return {status.code(),
str::stream() << "Shard key for collection " << coll._fullNs->ns()
<< " is missing, but the collection is not marked as "
"dropped. This is an indication of corrupted sharding "
"metadata."};
}
} else {
return status;
}
}
{
BSONElement collDefaultCollation;
Status status =
bsonExtractTypedField(source, defaultCollation.name(), Object, &collDefaultCollation);
if (status.isOK()) {
BSONObj obj = collDefaultCollation.Obj();
if (obj.isEmpty()) {
return Status(ErrorCodes::BadValue, "empty defaultCollation");
}
coll._defaultCollation = obj.getOwned();
} else if (status != ErrorCodes::NoSuchKey) {
return status;
}
}
{
bool collUnique;
Status status = bsonExtractBooleanField(source, unique.name(), &collUnique);
if (status.isOK()) {
coll._unique = collUnique;
} else if (status == ErrorCodes::NoSuchKey) {
// Key uniqueness can be missing in which case it is presumed false
} else {
return status;
}
}
{
bool collNoBalance;
Status status = bsonExtractBooleanField(source, kNoBalance.name(), &collNoBalance);
if (status.isOK()) {
coll._allowBalance = !collNoBalance;
} else if (status == ErrorCodes::NoSuchKey) {
// No balance can be missing in which case it is presumed as false
} else {
return status;
}
}
return StatusWith(coll);
}
Status CollectionType::validate() const {
// These fields must always be set
if (!_fullNs.is_initialized()) {
return Status(ErrorCodes::NoSuchKey, "missing ns");
}
if (!_fullNs->isValid()) {
return Status(ErrorCodes::BadValue, "invalid namespace " + _fullNs->toString());
}
if (!_epoch.is_initialized()) {
return Status(ErrorCodes::NoSuchKey, "missing epoch");
}
if (!_updatedAt.is_initialized()) {
return Status(ErrorCodes::NoSuchKey, "missing updated at timestamp");
}
if (!_dropped.get_value_or(false)) {
if (!_epoch->isSet()) {
return Status(ErrorCodes::BadValue, "invalid epoch");
}
if (Date_t() == _updatedAt.get()) {
return Status(ErrorCodes::BadValue, "invalid updated at timestamp");
}
if (!_keyPattern.is_initialized()) {
return Status(ErrorCodes::NoSuchKey, "missing key pattern");
} else {
invariant(!_keyPattern->toBSON().isEmpty());
}
}
return Status::OK();
}
BSONObj CollectionType::toBSON() const {
BSONObjBuilder builder;
if (_fullNs) {
builder.append(fullNs.name(), _fullNs->toString());
}
builder.append(epoch.name(), _epoch.get_value_or(OID()));
builder.append(updatedAt.name(), _updatedAt.get_value_or(Date_t()));
builder.append(kDropped.name(), _dropped.get_value_or(false));
// These fields are optional, so do not include them in the metadata for the purposes of
// consuming less space on the config servers.
if (_keyPattern.is_initialized()) {
builder.append(keyPattern.name(), _keyPattern->toBSON());
}
if (!_defaultCollation.isEmpty()) {
builder.append(defaultCollation.name(), _defaultCollation);
}
if (_unique.is_initialized()) {
builder.append(unique.name(), _unique.get());
}
if (_allowBalance.is_initialized()) {
builder.append(kNoBalance.name(), !_allowBalance.get());
}
return builder.obj();
}
std::string CollectionType::toString() const {
return toBSON().toString();
}
void CollectionType::setNs(const NamespaceString& fullNs) {
invariant(fullNs.isValid());
_fullNs = fullNs;
}
void CollectionType::setEpoch(OID epoch) {
_epoch = epoch;
}
void CollectionType::setUpdatedAt(Date_t updatedAt) {
_updatedAt = updatedAt;
}
void CollectionType::setKeyPattern(const KeyPattern& keyPattern) {
invariant(!keyPattern.toBSON().isEmpty());
_keyPattern = keyPattern;
}
} // namespace mongo