From c81781d4064998e1cd5d98f6e89c9359f2b7e323 Mon Sep 17 00:00:00 2001 From: Hari Khalsa Date: Wed, 1 May 2013 10:01:08 -0400 Subject: SERVER-8791 SERVER-9212 remove IndexSpec --- src/mongo/SConscript | 2 +- src/mongo/db/cloner.cpp | 1 - src/mongo/db/db.cpp | 3 +- src/mongo/db/index.cpp | 42 ++----- src/mongo/db/index.h | 3 - src/mongo/db/index/2d_index_cursor.cpp | 1 + src/mongo/db/index/btree_key_generator.cpp | 7 +- src/mongo/db/index/btree_key_generator.h | 6 + src/mongo/db/index/catalog_hack.h | 29 +++-- src/mongo/db/index/emulated_cursor.h | 2 +- src/mongo/db/index/hash_access_method.cpp | 35 ++---- src/mongo/db/index/hash_access_method.h | 11 +- src/mongo/db/index_legacy.cpp | 81 +++++++++++++ src/mongo/db/index_legacy.h | 67 ++++++++++ src/mongo/db/index_names.cpp | 16 +++ src/mongo/db/index_names.h | 8 ++ src/mongo/db/index_selection.cpp | 3 +- src/mongo/db/index_selection.h | 4 +- src/mongo/db/indexkey.cpp | 104 ---------------- src/mongo/db/indexkey.h | 110 ----------------- src/mongo/db/keypattern.cpp | 12 -- src/mongo/db/keypattern.h | 6 - src/mongo/db/namespace_details.cpp | 2 - src/mongo/db/namespace_details.h | 20 +-- src/mongo/db/pdfile.cpp | 9 +- src/mongo/db/query_optimizer_internal.cpp | 5 +- src/mongo/db/query_plan.cpp | 6 +- src/mongo/db/query_plan.h | 1 - src/mongo/db/queryutil.cpp | 7 ++ src/mongo/db/queryutil.h | 2 +- src/mongo/db/scanandorder.cpp | 3 +- src/mongo/db/scanandorder.h | 2 +- src/mongo/dbtests/namespacetests.cpp | 189 ++++++++++++++++++++--------- src/mongo/s/d_split.cpp | 13 +- 34 files changed, 390 insertions(+), 422 deletions(-) create mode 100644 src/mongo/db/index_legacy.cpp create mode 100644 src/mongo/db/index_legacy.h delete mode 100644 src/mongo/db/indexkey.cpp delete mode 100644 src/mongo/db/indexkey.h (limited to 'src/mongo') diff --git a/src/mongo/SConscript b/src/mongo/SConscript index a7873303039..e3ea7d89dc3 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -431,6 +431,7 @@ serverOnlyFiles = [ "db/curop.cpp", "db/prefetch.cpp", "db/repl/write_concern.cpp", "db/btreecursor.cpp", + "db/index_legacy.cpp", "db/index_selection.cpp", "db/index/2d_access_method.cpp", "db/index/2d_index_cursor.cpp", @@ -486,7 +487,6 @@ serverOnlyFiles = [ "db/curop.cpp", "db/dbcommands.cpp", "db/compact.cpp", "db/dbcommands_admin.cpp", - "db/indexkey.cpp", # most commands are only for mongod "db/commands/apply_ops.cpp", diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp index 11a3939220c..38948b3e5de 100644 --- a/src/mongo/db/cloner.cpp +++ b/src/mongo/db/cloner.cpp @@ -297,7 +297,6 @@ namespace mongo { } extern bool inDBRepair; - extern const int DefaultIndexVersionNumber; // from indexkey.cpp void ensureIdIndexForNewNs(const char *ns); bool Cloner::go(const char *masterHost, string& errmsg, const string& fromdb, bool logForRepl, bool slaveOk, bool useReplAuth, bool snapshot, bool mayYield, bool mayBeInterrupted, int *errCode) { diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 1513796145b..ba655255002 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -33,6 +33,7 @@ #include "mongo/db/dbmessage.h" #include "mongo/db/dbwebserver.h" #include "mongo/db/dur.h" +#include "mongo/db/index_names.h" #include "mongo/db/index_rebuilder.h" #include "mongo/db/initialize_server_global_state.h" #include "mongo/db/instance.h" @@ -368,7 +369,7 @@ namespace mongo { for ( ; cursor && cursor->ok(); cursor->advance()) { const BSONObj index = cursor->current(); const BSONObj key = index.getObjectField("key"); - const string plugin = KeyPattern::findPluginName(key); + const string plugin = IndexNames::findPluginName(key); if (IndexNames::existedBefore24(plugin)) continue; diff --git a/src/mongo/db/index.cpp b/src/mongo/db/index.cpp index 3a9c2b1fa9f..c71b2a34195 100644 --- a/src/mongo/db/index.cpp +++ b/src/mongo/db/index.cpp @@ -26,8 +26,8 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/background.h" #include "mongo/db/btree.h" -#include "mongo/db/fts/fts_enabled.h" -#include "mongo/db/fts/fts_spec.h" +#include "mongo/db/index_legacy.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_cursor.h" #include "mongo/db/index/index_descriptor.h" @@ -40,6 +40,9 @@ namespace mongo { + // What's the default version of our indices? + const int DefaultIndexVersionNumber = 1; + int removeFromSysIndexes(const char *ns, const char *idxName) { string system_indexes = cc().database()->name + ".system.indexes"; BSONObjBuilder b; @@ -79,11 +82,6 @@ namespace mongo { return -1; } - const IndexSpec& IndexDetails::getSpec() const { - SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex); - return NamespaceDetailsTransient::get_inlock( info.obj()["ns"].valuestr() ).getIndexSpec( this ); - } - /* delete this index. does NOT clean up the system catalog (system.indexes or system.namespaces) -- only NamespaceIndex. */ @@ -148,7 +146,7 @@ namespace mongo { for ( ; cursor && cursor->ok(); cursor->advance()) { const BSONObj index = cursor->current(); const BSONObj key = index.getObjectField("key"); - const string plugin = KeyPattern::findPluginName(key); + const string plugin = IndexNames::findPluginName(key); if (IndexNames::existedBefore24(plugin)) continue; @@ -245,7 +243,7 @@ namespace mongo { return false; } - string pluginName = KeyPattern::findPluginName( key ); + string pluginName = IndexNames::findPluginName( key ); if (pluginName.size()) { uassert(16734, str::stream() << "Unknown index plugin '" << pluginName << "' " << "in index "<< key, @@ -257,16 +255,7 @@ namespace mongo { { BSONObj o = io; - if (IndexNames::TEXT == pluginName || IndexNames::TEXT_INTERNAL == pluginName) { - StringData desc = cc().desc(); - if ( desc.find( "conn" ) == 0 ) { - // this is to make sure we only complain for users - // if you do get a text index created an a primary - // want it to index on the secondary as well - massert(16808, "text search not enabled", fts::isTextSearchEnabled() ); - } - o = fts::FTSSpec::fixSpec(o); - } + o = IndexLegacy::adjustIndexSpecObject(o); BSONObjBuilder b; int v = DefaultIndexVersionNumber; if( !o["v"].eoo() ) { @@ -300,19 +289,4 @@ namespace mongo { return true; } - - void IndexSpec::reset(const IndexDetails * details) { - _details = details; - reset(details->info); - } - - void IndexSpec::reset(const BSONObj& _info) { - info = _info; - keyPattern = info["key"].embeddedObjectUserCheck(); - if ( keyPattern.objsize() == 0 ) { - out() << info.toString() << endl; - verify(false); - } - _init(); - } } diff --git a/src/mongo/db/index.h b/src/mongo/db/index.h index 902f7afd72f..d9332c89b6f 100644 --- a/src/mongo/db/index.h +++ b/src/mongo/db/index.h @@ -23,7 +23,6 @@ #include #include "mongo/db/diskloc.h" -#include "mongo/db/indexkey.h" #include "mongo/db/jsobj.h" #include "mongo/db/key.h" #include "mongo/db/namespace.h" @@ -160,8 +159,6 @@ namespace mongo { */ void kill_idx(); - const IndexSpec& getSpec() const; - string toString() const { return info.obj().toString(); } diff --git a/src/mongo/db/index/2d_index_cursor.cpp b/src/mongo/db/index/2d_index_cursor.cpp index 48d7bf72c64..d5877585464 100644 --- a/src/mongo/db/index/2d_index_cursor.cpp +++ b/src/mongo/db/index/2d_index_cursor.cpp @@ -322,6 +322,7 @@ namespace mongo { scoped_ptr _cursor; scoped_ptr _frs; + // TODO: Turn into a KeyPattern object when FieldRangeVector takes one. BSONObj _keyPattern; BSONObj key() { return _cursor->currKey(); } diff --git a/src/mongo/db/index/btree_key_generator.cpp b/src/mongo/db/index/btree_key_generator.cpp index b4ca76de792..0efdbd7e96a 100644 --- a/src/mongo/db/index/btree_key_generator.cpp +++ b/src/mongo/db/index/btree_key_generator.cpp @@ -18,8 +18,9 @@ #include "mongo/util/mongoutils/str.h" namespace mongo { - // XXX: sigh this shouldnt' be here OR in indexkey.h - static const int ParallelArraysCode = 10088; + + // Used in scanandorder.cpp to inforatively error when we try to sort keys with parallel arrays. + const int BtreeKeyGenerator::ParallelArraysCode = 10088; BtreeKeyGenerator::BtreeKeyGenerator(vector fieldNames, vector fixed, bool isSparse) @@ -50,7 +51,7 @@ namespace mongo { static void assertParallelArrays( const char *first, const char *second ) { stringstream ss; ss << "cannot index parallel arrays [" << first << "] [" << second << "]"; - uasserted( ParallelArraysCode , ss.str() ); + uasserted( BtreeKeyGenerator::ParallelArraysCode , ss.str() ); } BtreeKeyGeneratorV0::BtreeKeyGeneratorV0(vector fieldNames, diff --git a/src/mongo/db/index/btree_key_generator.h b/src/mongo/db/index/btree_key_generator.h index da46e2ee816..985981d7c98 100644 --- a/src/mongo/db/index/btree_key_generator.h +++ b/src/mongo/db/index/btree_key_generator.h @@ -22,6 +22,10 @@ namespace mongo { + /** + * Internal class used by BtreeAccessMethod to generate keys for indexed documents. + * This class is meant to be kept under the index access layer. + */ class BtreeKeyGenerator { public: BtreeKeyGenerator(vector fieldNames, vector fixed, bool isSparse); @@ -29,6 +33,8 @@ namespace mongo { void getKeys(const BSONObj &obj, BSONObjSet *keys) const; + static const int ParallelArraysCode; + protected: // These are used by the getKeysImpl(s) below. vector _fieldNames; diff --git a/src/mongo/db/index/catalog_hack.h b/src/mongo/db/index/catalog_hack.h index 2b306b75462..c75d6f5a6d6 100644 --- a/src/mongo/db/index/catalog_hack.h +++ b/src/mongo/db/index/catalog_hack.h @@ -26,7 +26,6 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/s2_access_method.h" #include "mongo/db/index_names.h" -#include "mongo/db/keypattern.h" #include "mongo/db/pdfile.h" namespace mongo { @@ -36,8 +35,15 @@ namespace mongo { */ class CatalogHack { public: + + /** + * Older versions of MongoDB treated unknown index plugins as ascending Btree indices. + * We need to replicate this behavior. We use the version of the on-disk database to hint + * to us whether or not a given index was created as an actual instance of a special index, + * or if it was just treated as an increasing Btree index. + */ static bool shouldOverridePlugin(const BSONObj& keyPattern) { - string pluginName = KeyPattern::findPluginName(keyPattern); + string pluginName = IndexNames::findPluginName(keyPattern); bool known = IndexNames::isKnownName(pluginName); if (NULL == cc().database()) { @@ -73,11 +79,16 @@ namespace mongo { } } - static string findPluginName(const BSONObj& keyPattern) { + /** + * This differs from IndexNames::findPluginName in that returns the plugin name we *should* + * use, not the plugin name inside of the provided key pattern. To understand when these + * differ, see shouldOverridePlugin. + */ + static string getAccessMethodName(const BSONObj& keyPattern) { if (shouldOverridePlugin(keyPattern)) { return ""; } else { - return KeyPattern::findPluginName(keyPattern); + return IndexNames::findPluginName(keyPattern); } } @@ -87,11 +98,8 @@ namespace mongo { } static BtreeBasedAccessMethod* getBtreeBasedIndex(IndexDescriptor* desc) { - if (shouldOverridePlugin(desc->keyPattern())) { - return new BtreeAccessMethod(desc); - } + string type = getAccessMethodName(desc->keyPattern()); - string type = KeyPattern::findPluginName(desc->keyPattern()); if (IndexNames::HASHED == type) { return new HashAccessMethod(desc); } else if (IndexNames::GEO_2DSPHERE == type) { @@ -112,11 +120,8 @@ namespace mongo { } static IndexAccessMethod* getIndex(IndexDescriptor* desc) { - if (shouldOverridePlugin(desc->keyPattern())) { - return new BtreeAccessMethod(desc); - } + string type = getAccessMethodName(desc->keyPattern()); - string type = KeyPattern::findPluginName(desc->keyPattern()); if (IndexNames::HASHED == type) { return new HashAccessMethod(desc); } else if (IndexNames::GEO_2DSPHERE == type) { diff --git a/src/mongo/db/index/emulated_cursor.h b/src/mongo/db/index/emulated_cursor.h index e4f41631e98..ad3500f5afc 100644 --- a/src/mongo/db/index/emulated_cursor.h +++ b/src/mongo/db/index/emulated_cursor.h @@ -142,7 +142,7 @@ namespace mongo { EmulatedCursor(IndexDescriptor* descriptor, IndexAccessMethod* indexAccessMethod, const BSONObj& order, int numWanted, const BSONObj& keyPattern) : _descriptor(descriptor), _indexAccessMethod(indexAccessMethod), - _keyPattern(keyPattern), _pluginName(KeyPattern::findPluginName(keyPattern)) { + _keyPattern(keyPattern), _pluginName(IndexNames::findPluginName(keyPattern)) { IndexCursor *cursor; indexAccessMethod->newCursor(&cursor); diff --git a/src/mongo/db/index/hash_access_method.cpp b/src/mongo/db/index/hash_access_method.cpp index 13a966fecf0..5d6f671bea0 100644 --- a/src/mongo/db/index/hash_access_method.cpp +++ b/src/mongo/db/index/hash_access_method.cpp @@ -28,20 +28,6 @@ namespace mongo { return BSONElementHasher::hash64(e, seed); } - BSONObj HashAccessMethod::getMissingField(const IndexDetails& details) { - BSONObj infoObj = details.info.obj(); - int hashVersion = infoObj["hashVersion"].numberInt(); - HashSeed seed = infoObj["seed"].numberInt(); - - // Explicit null valued fields and missing fields are both represented in hashed indexes - // using the hash value of the null BSONElement. This is partly for historical reasons - // (hash of null was used in the initial release of hashed indexes and changing would alter - // the data format). Additionally, in certain places the hashed index code and the index - // bound calculation code assume null and missing are indexed identically. - BSONObj nullObj = BSON("" << BSONNULL); - return BSON("" << makeSingleKey(nullObj.firstElement(), seed, hashVersion)); - } - HashAccessMethod::HashAccessMethod(IndexDescriptor* descriptor) : BtreeBasedAccessMethod(descriptor) { @@ -69,9 +55,6 @@ namespace mongo { massert(16765, "error: no hashed index field", firstElt.str().compare(HASHED_INDEX_TYPE_IDENTIFIER) == 0); _hashedField = firstElt.fieldName(); - - BSONObj nullObj = BSON("" << BSONNULL); - _missingKey = BSON("" << makeSingleKey(nullObj.firstElement(), _seed, _hashVersion)); } Status HashAccessMethod::newCursor(IndexCursor** out) { @@ -80,16 +63,24 @@ namespace mongo { } void HashAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) { - const char* cstr = _hashedField.c_str(); + getKeysImpl(obj, _hashedField, _seed, _hashVersion, _descriptor->isSparse(), keys); + } + + // static + void HashAccessMethod::getKeysImpl(const BSONObj& obj, const string& hashedField, HashSeed seed, + int hashVersion, bool isSparse, BSONObjSet* keys) { + const char* cstr = hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array ); if (!fieldVal.eoo()) { - BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); - keys->insert( key ); - } else if (!_descriptor->isSparse()) { - keys->insert( _missingKey.copy() ); + BSONObj key = BSON( "" << makeSingleKey(fieldVal, seed, hashVersion)); + keys->insert(key); + } + else if (!isSparse) { + BSONObj nullObj = BSON("" << BSONNULL); + keys->insert(BSON("" << makeSingleKey(nullObj.firstElement(), seed, hashVersion))); } } diff --git a/src/mongo/db/index/hash_access_method.h b/src/mongo/db/index/hash_access_method.h index 19060cf3f96..0777f645159 100644 --- a/src/mongo/db/index/hash_access_method.h +++ b/src/mongo/db/index/hash_access_method.h @@ -43,15 +43,18 @@ namespace mongo { return Status::OK(); } - // Our missing field is different than the default missing field, this needs to be - // exposed in s/d_split.cpp. That's the only thing that calls this. - static BSONObj getMissingField(const IndexDetails& details); - /** * Hashing function used by both this class and the cursors we create. + * Exposed for testing and so mongo/db/index_legacy.cpp can use it. */ static long long int makeSingleKey(const BSONElement& e, HashSeed seed, int v); + /** + * Exposed externally for testing purposes. + */ + static void getKeysImpl(const BSONObj& obj, const string& hashedField, HashSeed seed, + int hashVersion, bool isSparse, BSONObjSet* keys); + private: virtual void getKeys(const BSONObj& obj, BSONObjSet* keys); diff --git a/src/mongo/db/index_legacy.cpp b/src/mongo/db/index_legacy.cpp new file mode 100644 index 00000000000..a47544af518 --- /dev/null +++ b/src/mongo/db/index_legacy.cpp @@ -0,0 +1,81 @@ +/** +* 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 . +*/ + +#include "mongo/db/index_legacy.h" + +#include "mongo/db/client.h" +#include "mongo/db/fts/fts_enabled.h" +#include "mongo/db/fts/fts_spec.h" +#include "mongo/db/index_names.h" +#include "mongo/db/index/catalog_hack.h" +#include "mongo/db/index/hash_access_method.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/namespace_details.h" + +namespace mongo { + + // static + BSONObj IndexLegacy::adjustIndexSpecObject(const BSONObj& obj) { + string pluginName = IndexNames::findPluginName(obj.getObjectField("key")); + + if (IndexNames::TEXT == pluginName || IndexNames::TEXT_INTERNAL == pluginName) { + StringData desc = cc().desc(); + if (desc.find("conn") == 0) { + // this is to make sure we only complain for users + // if you do get a text index created an a primary + // want it to index on the secondary as well + massert(16811, "text search not enabled", fts::isTextSearchEnabled() ); + } + return fts::FTSSpec::fixSpec(obj); + } + + return obj; + } + + // static + BSONObj IndexLegacy::getMissingField(const BSONObj& infoObj) { + if (IndexNames::HASHED == CatalogHack::getAccessMethodName(infoObj.getObjectField("key"))) { + int hashVersion = infoObj["hashVersion"].numberInt(); + HashSeed seed = infoObj["seed"].numberInt(); + + // Explicit null valued fields and missing fields are both represented in hashed indexes + // using the hash value of the null BSONElement. This is partly for historical reasons + // (hash of null was used in the initial release of hashed indexes and changing would + // alter the data format). Additionally, in certain places the hashed index code and + // the index bound calculation code assume null and missing are indexed identically. + BSONObj nullObj = BSON("" << BSONNULL); + return BSON("" << HashAccessMethod::makeSingleKey(nullObj.firstElement(), seed, + hashVersion)); + } + else { + BSONObjBuilder b; + b.appendNull(""); + return b.obj(); + } + } + + // static + void IndexLegacy::postBuildHook(NamespaceDetails* tableToIndex, const IndexDetails& idx) { + // If it's an FTS index, we want to set the power of 2 flag. + string pluginName = CatalogHack::getAccessMethodName(idx.keyPattern()); + if (IndexNames::TEXT == pluginName || IndexNames::TEXT_INTERNAL == pluginName) { + if (tableToIndex->setUserFlag(NamespaceDetails::Flag_UsePowerOf2Sizes)) { + tableToIndex->syncUserFlags(idx.parentNS()); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/index_legacy.h b/src/mongo/db/index_legacy.h new file mode 100644 index 00000000000..ffa880c929f --- /dev/null +++ b/src/mongo/db/index_legacy.h @@ -0,0 +1,67 @@ + +/** +* 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 . +*/ + +#pragma once + +#include "mongo/db/jsobj.h" + +namespace mongo { + + class IndexDetails; + class NamespaceDetails; + + /** + * There has been some behavior concerning indexed access patterns -- both pre and post-index + * construction -- that does not quite fit in the access pattern model implemented in + * index/index_access_pattern.h. Such behavior can't be changed in the current implementation of + * the code. + * + * We grouped such exception/legacy behavior here. + */ + class IndexLegacy { + public: + /** + * Adjust the provided index spec BSONObj depending on the type of index obj describes. + * + * This is a no-op unless the object describes a FTS index. To see what FTS does, look in + * FTSSpec::fixSpec in fts/fts_spec.cpp. + */ + static BSONObj adjustIndexSpecObject(const BSONObj& obj); + + /** + * Returns the BSONObj that is inserted into an index when the object is missing the keys + * the index is over. + * + * For every index *except hash*, this is the BSON equivalent of jstNULL. + * For the hash index, it's the hash of BSON("" << BSONNULL). + * + * s/d_split.cpp needs to know this. + * + * This is a significant leak of index functionality out of the index layer. + */ + static BSONObj getMissingField(const BSONObj& infoObj); + + /** + * Perform any post-build steps for this index. + * + * This is a no-op unless the index is a FTS index. In that case, we set the flag for using + * power of 2 sizes for space allocation. + */ + static void postBuildHook(NamespaceDetails* tableToIndex, const IndexDetails& idx); + }; + +} // namespace mongo diff --git a/src/mongo/db/index_names.cpp b/src/mongo/db/index_names.cpp index 6a9aac30bc3..873f11c0f41 100644 --- a/src/mongo/db/index_names.cpp +++ b/src/mongo/db/index_names.cpp @@ -16,6 +16,8 @@ #include "mongo/db/index_names.h" +#include "mongo/db/jsobj.h" + namespace mongo { const string IndexNames::GEO_2D = "2d"; const string IndexNames::GEO_HAYSTACK = "geoHaystack"; @@ -23,4 +25,18 @@ namespace mongo { const string IndexNames::TEXT = "text"; const string IndexNames::TEXT_INTERNAL = "_fts"; const string IndexNames::HASHED = "hashed"; + + // static + string IndexNames::findPluginName(const BSONObj& keyPattern) { + BSONObjIterator i(keyPattern); + + while (i.more()) { + BSONElement e = i.next(); + if (String != e.type()) { continue; } + return e.String(); + } + + return ""; + } + } // namespace mongo diff --git a/src/mongo/db/index_names.h b/src/mongo/db/index_names.h index c2eef144ad9..6b4cb8570d3 100644 --- a/src/mongo/db/index_names.h +++ b/src/mongo/db/index_names.h @@ -22,6 +22,8 @@ namespace mongo { using std::string; + class BSONObj; + /** * We use the string representation of index names all over the place, so we declare them all * once here. @@ -47,6 +49,12 @@ namespace mongo { || name == IndexNames::HASHED; } + /** + * Return the first string value in the provided object. For an index key pattern, + * a field with a non-string value indicates a "special" (not straight Btree) index. + */ + static string findPluginName(const BSONObj& keyPattern); + static bool isKnownName(const string& name) { return name.empty() || name == IndexNames::GEO_2D diff --git a/src/mongo/db/index_selection.cpp b/src/mongo/db/index_selection.cpp index 431d51f8e0a..523106d25cf 100644 --- a/src/mongo/db/index_selection.cpp +++ b/src/mongo/db/index_selection.cpp @@ -28,9 +28,10 @@ namespace mongo { const FieldRangeSet& queryConstraints, const BSONObj& order) { - string type = CatalogHack::findPluginName(keyPattern); + string type = CatalogHack::getAccessMethodName(keyPattern); BSONObj query = queryConstraints.originalQuery(); + // "" means it's a b-tree index, ascending or descending. if ("" == type) { // This is a quick first pass to determine the suitability of the index. It produces // some false positives (returns HELPFUL for some indexes which are not particularly). diff --git a/src/mongo/db/index_selection.h b/src/mongo/db/index_selection.h index e64978f638e..bc8c999da3d 100644 --- a/src/mongo/db/index_selection.h +++ b/src/mongo/db/index_selection.h @@ -16,13 +16,13 @@ #pragma once -#include "mongo/db/indexkey.h" // for IndexSuitability - namespace mongo { class BSONObj; class FieldRangeSet; + enum IndexSuitability { USELESS = 0 , HELPFUL = 1 , OPTIMAL = 2 }; + /** * This class is part of query optimization. For a given index (as uniquely described by * keyPattern) and a given query (queryConstraints) and given sort order (order), return how diff --git a/src/mongo/db/indexkey.cpp b/src/mongo/db/indexkey.cpp deleted file mode 100644 index 56de9511ec7..00000000000 --- a/src/mongo/db/indexkey.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// index_key.cpp - -/** -* Copyright (C) 2008 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 . -*/ - -#include "pch.h" -#include "namespace-inl.h" -#include "index.h" -#include "background.h" -#include "../util/stringutils.h" -#include "mongo/util/mongoutils/str.h" -#include "../util/text.h" -#include "mongo/db/client.h" -#include "mongo/db/database.h" -#include "mongo/db/index/catalog_hack.h" -#include "mongo/db/pdfile.h" -#include "mongo/db/queryutil.h" - -namespace mongo { - - /** old (<= v1.8) : 0 - 1 is new version - */ - const int DefaultIndexVersionNumber = 1; - - void IndexSpec::_init() { - verify( keyPattern.objsize() ); - - // some basics - _nFields = keyPattern.nFields(); - _sparse = info["sparse"].trueValue(); - uassert( 13529 , "sparse only works for single field keys" , ! _sparse || _nFields ); - - - { - // build _nullKey - - BSONObjBuilder b; - BSONObjIterator i( keyPattern ); - - while( i.more() ) { - BSONElement e = i.next(); - _fieldNames.push_back( e.fieldName() ); - _fixed.push_back( BSONElement() ); - b.appendNull( "" ); - } - _nullKey = b.obj(); - } - - { - // _nullElt - BSONObjBuilder b; - b.appendNull( "" ); - _nullObj = b.obj(); - _nullElt = _nullObj.firstElement(); - } - - { - // _undefinedElt - BSONObjBuilder b; - b.appendUndefined( "" ); - _undefinedObj = b.obj(); - _undefinedElt = _undefinedObj.firstElement(); - } - - _finishedInit = true; - } - - string IndexSpec::getTypeName() const { - return CatalogHack::findPluginName(_details->keyPattern()); - } - - string IndexSpec::toString() const { - stringstream s; - s << "IndexSpec @ " << hex << this << dec << ", " - << "Details @ " << hex << _details << dec << ", " - << "Type: " << getTypeName() << ", " - << "nFields: " << _nFields << ", " - << "KeyPattern: " << keyPattern << ", " - << "Info: " << info; - return s.str(); - } - - int IndexSpec::indexVersion() const { - if ( !info.hasField( "v" ) ) { - return DefaultIndexVersionNumber; - } - return IndexDetails::versionForIndexObj( info ); - } - -} diff --git a/src/mongo/db/indexkey.h b/src/mongo/db/indexkey.h deleted file mode 100644 index 7fed236db7e..00000000000 --- a/src/mongo/db/indexkey.h +++ /dev/null @@ -1,110 +0,0 @@ -// index_key.h - -/** -* Copyright (C) 2008 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 . -*/ - -#pragma once - -#include - -#include "mongo/db/diskloc.h" -#include "mongo/db/index_names.h" -#include "mongo/db/keypattern.h" -#include "mongo/db/jsobj.h" - -namespace mongo { - - extern const int DefaultIndexVersionNumber; - - const int ParallelArraysCode = 10088; - - class Cursor; - class IndexSpec; - class IndexDetails; - class FieldRangeSet; - - enum IndexSuitability { USELESS = 0 , HELPFUL = 1 , OPTIMAL = 2 }; - - /* precomputed details about an index, used for inserting keys on updates - stored/cached in NamespaceDetailsTransient, or can be used standalone - */ - class IndexSpec { - public: - BSONObj keyPattern; // e.g., { name : 1 } - BSONObj info; // this is the same as IndexDetails::info.obj() - - IndexSpec() - : _details(0) , _finishedInit(false) { - } - - explicit IndexSpec(const BSONObj& k, const BSONObj& m=BSONObj()) - : keyPattern(k) , info(m) , _details(0) , _finishedInit(false) { - _init(); - } - - /** - this is a DiscLoc of an IndexDetails info - should have a key field - */ - explicit IndexSpec(const DiskLoc& loc) { - reset(loc); - } - - void reset(const BSONObj& info); - void reset(const IndexDetails * details); // determines rules based on pdfile version - void reset(const DiskLoc& infoLoc) { - reset(infoLoc.obj()); - } - - string getTypeName() const; - - const IndexDetails * getDetails() const { - return _details; - } - - bool isSparse() const { return _sparse; } - - string toString() const; - - protected: - - int indexVersion() const; - - BSONSizeTracker _sizeTracker; - vector _fieldNames; - vector _fixed; - - BSONObj _nullKey; // a full key with all fields null - BSONObj _nullObj; // only used for _nullElt - BSONElement _nullElt; // jstNull - - BSONObj _undefinedObj; // only used for _undefinedElt - BSONElement _undefinedElt; // undefined - - int _nFields; // number of fields in the index - bool _sparse; // if the index is sparse - const IndexDetails * _details; - - void _init(); - - friend class KeyGeneratorV0; - friend class KeyGeneratorV1; - public: - bool _finishedInit; - }; - - -} // namespace mongo diff --git a/src/mongo/db/keypattern.cpp b/src/mongo/db/keypattern.cpp index 6c1bb6d67e6..03a29c4dc2c 100644 --- a/src/mongo/db/keypattern.cpp +++ b/src/mongo/db/keypattern.cpp @@ -51,18 +51,6 @@ namespace mongo { && i.next().eoo(); } - string KeyPattern::findPluginName(const BSONObj& keyPattern) { - BSONObjIterator i(keyPattern); - - while (i.more()) { - BSONElement e = i.next(); - if (String != e.type()) { continue; } - return e.String(); - } - - return ""; - } - BSONObj KeyPattern::extractSingleKey(const BSONObj& doc ) const { if ( _pattern.isEmpty() ) return BSONObj(); diff --git a/src/mongo/db/keypattern.h b/src/mongo/db/keypattern.h index da206e10b72..c499bae4d69 100644 --- a/src/mongo/db/keypattern.h +++ b/src/mongo/db/keypattern.h @@ -86,12 +86,6 @@ namespace mongo { */ static bool isIdKeyPattern(const BSONObj& pattern); - /** - * Return the first string value in the provided object. For an index key pattern, - * a field with a non-string value indicates a "special" (not straight Btree) index. - */ - static string findPluginName(const BSONObj& keyPattern); - /* Takes a BSONObj whose field names are a prefix of the fields in this keyPattern, and * outputs a new bound with MinKey values appended to match the fields in this keyPattern * (or MaxKey values for descending -1 fields). This is useful in sharding for diff --git a/src/mongo/db/namespace_details.cpp b/src/mongo/db/namespace_details.cpp index 2be3d0d59c5..e5da0d6feb6 100644 --- a/src/mongo/db/namespace_details.cpp +++ b/src/mongo/db/namespace_details.cpp @@ -681,14 +681,12 @@ namespace mongo { /* ------------------------------------------------------------------------- */ SimpleMutex NamespaceDetailsTransient::_qcMutex("qc"); - SimpleMutex NamespaceDetailsTransient::_isMutex("is"); NamespaceDetailsTransient::DMap NamespaceDetailsTransient::_nsdMap; void NamespaceDetailsTransient::reset() { Lock::assertWriteLocked(_ns); clearQueryCache(); _keysComputed = false; - _indexSpecs.clear(); } NamespaceDetailsTransient::CMap& NamespaceDetailsTransient::get_cmap_inlock(const string& ns) { diff --git a/src/mongo/db/namespace_details.h b/src/mongo/db/namespace_details.h index e53d3afec9f..2401afa6707 100644 --- a/src/mongo/db/namespace_details.h +++ b/src/mongo/db/namespace_details.h @@ -20,6 +20,7 @@ #include "mongo/db/d_concurrency.h" #include "mongo/db/diskloc.h" #include "mongo/db/index.h" +#include "mongo/db/index_names.h" #include "mongo/db/index_set.h" #include "mongo/db/jsobj.h" #include "mongo/db/mongommf.h" @@ -296,7 +297,7 @@ namespace mongo { void findIndexByType( const string& name , vector& matches ) { IndexIterator i = ii(); while ( i.more() ) { - if ( i.next().getSpec().getTypeName() == name ) + if ( IndexNames::findPluginName(i.next().keyPattern()) == name ) matches.push_back( i.pos() - 1 ); } } @@ -496,23 +497,6 @@ namespace mongo { return _indexedPaths; } - /* IndexSpec caching */ - private: - map _indexSpecs; - static SimpleMutex _isMutex; - public: - const IndexSpec& getIndexSpec( const IndexDetails * details ) { - IndexSpec& spec = _indexSpecs[details]; - if ( ! spec._finishedInit ) { - SimpleMutex::scoped_lock lk(_isMutex); - if ( ! spec._finishedInit ) { - spec.reset( details ); - verify( spec._finishedInit ); - } - } - return spec; - } - /* query cache (for query optimizer) ------------------------------------- */ private: int _qcWriteCount; diff --git a/src/mongo/db/pdfile.cpp b/src/mongo/db/pdfile.cpp index d592991a014..8331fc87006 100644 --- a/src/mongo/db/pdfile.cpp +++ b/src/mongo/db/pdfile.cpp @@ -45,6 +45,7 @@ _ disallow system* manipulations from the database. #include "mongo/db/db.h" #include "mongo/db/dbhelpers.h" #include "mongo/db/extsort.h" +#include "mongo/db/index_legacy.h" #include "mongo/db/index_names.h" #include "mongo/db/index_update.h" #include "mongo/db/index/catalog_hack.h" @@ -1531,13 +1532,7 @@ namespace mongo { tableToIndex->addIndex(tabletoidxns.c_str()); getDur().writingInt(tableToIndex->indexBuildsInProgress) -= 1; - // If it's an FTS index, we want to set the power of 2 flag. - string pluginName = KeyPattern::findPluginName(idx.keyPattern()); - if (IndexNames::TEXT == pluginName || IndexNames::TEXT_INTERNAL == pluginName) { - if (tableToIndex->setUserFlag(NamespaceDetails::Flag_UsePowerOf2Sizes)) { - tableToIndex->syncUserFlags(idx.parentNS()); - } - } + IndexLegacy::postBuildHook(tableToIndex, idx); } catch (...) { // Generally, this will be called as an exception from building the index bubbles up. diff --git a/src/mongo/db/query_optimizer_internal.cpp b/src/mongo/db/query_optimizer_internal.cpp index dcd7516b357..c94212c1deb 100644 --- a/src/mongo/db/query_optimizer_internal.cpp +++ b/src/mongo/db/query_optimizer_internal.cpp @@ -545,7 +545,7 @@ namespace mongo { int j = i.pos(); IndexDetails& ii = i.next(); BSONObj keyPattern = ii.keyPattern(); - string pluginName = CatalogHack::findPluginName(keyPattern); + string pluginName = CatalogHack::getAccessMethodName(keyPattern); if (special.has(pluginName) && (USELESS != IndexSelection::isSuitableFor(keyPattern, _qps.frsp().frsForIndex(d, j), _qps.order()))) { @@ -1497,8 +1497,7 @@ namespace mongo { while( i.more() ) { IndexDetails& ii = i.next(); if ( indexWorks( ii.keyPattern(), min.isEmpty() ? max : min, ret.first, ret.second ) ) { - if ( ii.getSpec().getTypeName().empty()) { - //if ( ii.getSpec().getType() == 0 ) { + if (CatalogHack::getAccessMethodName(ii.keyPattern()).empty()) { id = ⅈ keyPattern = ii.keyPattern(); break; diff --git a/src/mongo/db/query_plan.cpp b/src/mongo/db/query_plan.cpp index 6bb157369a6..de388f458c4 100644 --- a/src/mongo/db/query_plan.cpp +++ b/src/mongo/db/query_plan.cpp @@ -116,10 +116,10 @@ namespace mongo { // If the parsing or index indicates this is a special query, don't continue the processing if (!_special.empty() || - ( ("" != CatalogHack::findPluginName(_descriptor->keyPattern())) && (USELESS != + ( ("" != CatalogHack::getAccessMethodName(_descriptor->keyPattern())) && (USELESS != IndexSelection::isSuitableFor(_descriptor->keyPattern(), _frs, _order)))) { - _specialIndexName = CatalogHack::findPluginName(_descriptor->keyPattern()); + _specialIndexName = CatalogHack::getAccessMethodName(_descriptor->keyPattern()); if (_special.empty()) _special = _specialIndexName; massert( 13040 , (string)"no type for special: " + _special , "" != _specialIndexName); @@ -284,7 +284,7 @@ doneCheckOrder: _direction >= 0 ? 1 : -1 ) ); } - if ( !_index->getSpec().getTypeName().empty()) { //_index->getSpec().getType() ) { + if ( "" != CatalogHack::getAccessMethodName(_descriptor->keyPattern())) { return shared_ptr( BtreeCursor::make( _d, *_index, _frv->startKey(), diff --git a/src/mongo/db/query_plan.h b/src/mongo/db/query_plan.h index 3c6350cc180..fb950eb9465 100644 --- a/src/mongo/db/query_plan.h +++ b/src/mongo/db/query_plan.h @@ -18,7 +18,6 @@ #include "mongo/db/diskloc.h" #include "mongo/db/index/index_descriptor.h" -#include "mongo/db/indexkey.h" #include "mongo/db/jsobj.h" #include "mongo/db/matcher.h" #include "mongo/db/projection.h" diff --git a/src/mongo/db/queryutil.cpp b/src/mongo/db/queryutil.cpp index 6ac3433caf4..899c03d73eb 100644 --- a/src/mongo/db/queryutil.cpp +++ b/src/mongo/db/queryutil.cpp @@ -1540,6 +1540,12 @@ namespace mongo { bool ok = false; BSONObjSet keys; + + /** + * Key generation by design is behind the index interface. There is an exception here + * because $or uses key generation to dedup its results. When $or is fixed to not require + * this, key generation will be removed from here. + */ _keyGenerator->getKeys(obj, &keys); // TODO The representation of matching keys could potentially be optimized @@ -1564,6 +1570,7 @@ namespace mongo { verify( _direction >= 0 ); BSONObjCmp oc(_keyPattern); BSONObjSet keys(oc); + // See FieldRangeVector::matches for comment on key generation. _keyGenerator->getKeys(obj, &keys); for( BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i ) { if ( matchesKey( *i ) ) { diff --git a/src/mongo/db/queryutil.h b/src/mongo/db/queryutil.h index c53082c06af..a1eaf78bcf8 100644 --- a/src/mongo/db/queryutil.h +++ b/src/mongo/db/queryutil.h @@ -16,7 +16,6 @@ #pragma once #include "jsobj.h" -#include "indexkey.h" #include "mongo/db/index/btree_key_generator.h" namespace mongo { @@ -548,6 +547,7 @@ namespace mongo { vector _fieldNames; vector _fixed; + // See FieldRangeVector::matches for comment on key generation. scoped_ptr _keyGenerator; }; diff --git a/src/mongo/db/scanandorder.cpp b/src/mongo/db/scanandorder.cpp index d3ed2beffa2..d1eab6a7e87 100644 --- a/src/mongo/db/scanandorder.cpp +++ b/src/mongo/db/scanandorder.cpp @@ -22,6 +22,7 @@ #include "mongo/db/scanandorder.h" +#include "mongo/db/index/btree_key_generator.h" #include "mongo/db/matcher.h" #include "mongo/db/parsed_query.h" #include "mongo/util/mongoutils/str.h" @@ -37,7 +38,7 @@ namespace mongo { k = _order.getKeyFromObject(o); } catch (UserException &e) { - if ( e.getCode() == ParallelArraysCode ) { // cannot get keys for parallel arrays + if ( e.getCode() == BtreeKeyGenerator::ParallelArraysCode) { // cannot get keys for parallel arrays // fix lasterror text to be more accurate. uasserted( 15925, "cannot sort with keys that are parallel arrays" ); } diff --git a/src/mongo/db/scanandorder.h b/src/mongo/db/scanandorder.h index 4bcfb4e44f9..7038a8dae28 100644 --- a/src/mongo/db/scanandorder.h +++ b/src/mongo/db/scanandorder.h @@ -20,7 +20,7 @@ #pragma once -#include "mongo/db/indexkey.h" +#include "mongo/db/diskloc.h" #include "mongo/db/projection.h" #include "mongo/db/queryutil.h" diff --git a/src/mongo/dbtests/namespacetests.cpp b/src/mongo/dbtests/namespacetests.cpp index 82fe647cd87..3c944ad03f6 100644 --- a/src/mongo/dbtests/namespacetests.cpp +++ b/src/mongo/dbtests/namespacetests.cpp @@ -23,6 +23,7 @@ #include "../db/db.h" #include "../db/json.h" +#include "mongo/db/index_legacy.h" #include "mongo/db/index_selection.h" #include "mongo/db/index/btree_key_generator.h" #include "mongo/db/index/hash_access_method.h" @@ -1091,8 +1092,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$within:{$box:[[100,0],[120,100]]}}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1102,8 +1103,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$within:{$box:[[100,0],[120,100]]}}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "b" << "2d" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "b" << "2d" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1113,8 +1114,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$near:[100,0]}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1126,8 +1127,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{lat:4,lon:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1140,8 +1141,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$gt:4,$lt:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1151,8 +1152,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:[1,1]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1162,8 +1163,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:1}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1173,8 +1174,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1187,8 +1188,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{$and:[{a:{$near:[100,0]}}]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2d" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1202,9 +1203,9 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$near:[100,0]},$or:[{b:1}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "2d" ) ); + BSONObj spec( BSON( "a" << "2d" ) ); ASSERT_EQUALS( OPTIMAL, - IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); + IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1219,8 +1220,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$geoIntersects:{$geometry:{type:'Point'," "coordinates:[40,5]}}}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1231,8 +1232,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$geoIntersects:{$geometry:{type:'Point'," "coordinates:[40,5]}}}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "b" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "b" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1242,8 +1243,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$near:[100,0]}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1256,8 +1257,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{lat:4,lon:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1267,8 +1268,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$gt:4,$lt:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1278,8 +1279,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:[1,1]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1291,8 +1292,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:1}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1302,8 +1303,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1316,8 +1317,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{$and:[{a:{$near:[100,0]}}]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1331,9 +1332,9 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$near:[100,0]},$or:[{b:1}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "2dsphere" ) ); + BSONObj spec( BSON( "a" << "2dsphere" ) ); ASSERT_EQUALS( OPTIMAL, - IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); + IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1347,8 +1348,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:5}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1358,8 +1359,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$gt:4}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1369,8 +1370,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{a:{$in:[1,2]}}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1383,8 +1384,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{$and:[{a:5}]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1397,8 +1398,8 @@ namespace NamespaceTests { void run() { BSONObj query = fromjson( "{$and:[{a:5},{b:5}]}" ); FieldRangeSet frs( "n/a", query, true, true ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frs, BSONObj() ) ); } }; @@ -1412,8 +1413,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$or:[{a:5}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1428,8 +1429,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{z:5,$or:[{a:5}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1444,8 +1445,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$or:[{a:5},{b:5}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1460,8 +1461,8 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:5,$or:[{z:5}]}" ); OrRangeGenerator org( "n/a", query, true ); scoped_ptr frsp( org.topFrsp() ); - IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), + BSONObj spec( BSON( "a" << "hashed" ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1471,6 +1472,74 @@ namespace NamespaceTests { // GeoHaystack does not implement its own suitability(). See SERVER-8645. } // namespace IndexSpecSuitability + + namespace MissingFieldTests { + + /** A missing field is represented as null in a btree index. */ + class BtreeIndexMissingField { + public: + void run() { + BSONObj spec( BSON("key" << BSON( "a" << 1 ) )); + ASSERT_EQUALS(jstNULL, IndexLegacy::getMissingField(spec).firstElement().type()); + } + }; + + /** A missing field is represented as null in a 2d index. */ + class TwoDIndexMissingField { + public: + void run() { + BSONObj spec( BSON("key" << BSON( "a" << "2d" ) )); + ASSERT_EQUALS(jstNULL, IndexLegacy::getMissingField(spec).firstElement().type()); + } + }; + + /** A missing field is represented with the hash of null in a hashed index. */ + class HashedIndexMissingField { + public: + void run() { + BSONObj spec( BSON("key" << BSON( "a" << "hashed" ) )); + BSONObj nullObj = BSON( "a" << BSONNULL ); + + // Call getKeys on the nullObj. + BSONObjSet nullFieldKeySet; + HashAccessMethod::getKeysImpl(nullObj, "a", 0, 0, false, &nullFieldKeySet); + BSONElement nullFieldFromKey = nullFieldKeySet.begin()->firstElement(); + + ASSERT_EQUALS( HashAccessMethod::makeSingleKey( nullObj.firstElement(), 0, 0 ), + nullFieldFromKey.Long() ); + + BSONObj missingField = IndexLegacy::getMissingField(spec); + ASSERT_EQUALS( NumberLong, missingField.firstElement().type() ); + ASSERT_EQUALS( nullFieldFromKey, missingField.firstElement()); + } + }; + + /** + * A missing field is represented with the hash of null in a hashed index. This hash value + * depends on the hash seed. + */ + class HashedIndexMissingFieldAlternateSeed { + public: + void run() { + BSONObj spec( BSON("key" << BSON( "a" << "hashed" ) << "seed" << 0x5eed )); + BSONObj nullObj = BSON( "a" << BSONNULL ); + + BSONObjSet nullFieldKeySet; + HashAccessMethod::getKeysImpl(nullObj, "a", 0x5eed, 0, false, &nullFieldKeySet); + BSONElement nullFieldFromKey = nullFieldKeySet.begin()->firstElement(); + + ASSERT_EQUALS( HashAccessMethod::makeSingleKey( nullObj.firstElement(), 0x5eed, 0 ), + nullFieldFromKey.Long() ); + + // Ensure that getMissingField recognizes that the seed is different (and returns + // the right key). + BSONObj missingField = IndexLegacy::getMissingField(spec); + ASSERT_EQUALS( NumberLong, missingField.firstElement().type()); + ASSERT_EQUALS( nullFieldFromKey, missingField.firstElement()); + } + }; + + } // namespace MissingFieldTests namespace NamespaceDetailsTests { @@ -2370,6 +2439,10 @@ namespace NamespaceTests { add< NamespaceDetailsTests::Size >(); add< NamespaceDetailsTests::SetIndexIsMultikey >(); add< NamespaceDetailsTransientTests::ClearQueryCache >(); + add< MissingFieldTests::BtreeIndexMissingField >(); + add< MissingFieldTests::TwoDIndexMissingField >(); + add< MissingFieldTests::HashedIndexMissingField >(); + add< MissingFieldTests::HashedIndexMissingFieldAlternateSeed >(); } } myall; } // namespace NamespaceTests diff --git a/src/mongo/s/d_split.cpp b/src/mongo/s/d_split.cpp index 98e4847e3fa..ba302515fbf 100644 --- a/src/mongo/s/d_split.cpp +++ b/src/mongo/s/d_split.cpp @@ -31,7 +31,7 @@ #include "mongo/db/btreecursor.h" #include "mongo/db/clientcursor.h" #include "mongo/db/commands.h" -#include "mongo/db/index/hash_access_method.h" +#include "mongo/db/index_legacy.h" #include "mongo/db/instance.h" #include "mongo/db/jsobj.h" #include "mongo/s/chunk.h" // for static genID only @@ -133,16 +133,9 @@ namespace mongo { // Find the 'missingField' value used to represent a missing document field in a key of // this index. - // NOTE A local copy of 'missingField' is made because IndexSpec objects may be + // NOTE A local copy of 'missingField' is made because indices may be // invalidated during a db lock yield. - BSONObj missingFieldObj; - if (IndexNames::HASHED == KeyPattern::findPluginName(kp.toBSON())) { - missingFieldObj = HashAccessMethod::getMissingField(*idx); - } else { - BSONObjBuilder b; - b.appendNull(""); - missingFieldObj = b.obj(); - } + BSONObj missingFieldObj = IndexLegacy::getMissingField(idx->info.obj()); BSONElement missingField = missingFieldObj.firstElement(); // for now, the only check is that all shard keys are filled -- cgit v1.2.1