// index_catalog.h
/**
* Copyright (C) 2013-2014 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 .
*
* 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.
*/
#pragma once
#include
#include "mongo/db/catalog/index_catalog_entry.h"
#include "mongo/db/index/multikey_paths.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/record_id.h"
#include "mongo/db/server_options.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/platform/unordered_map.h"
namespace mongo {
class Client;
class Collection;
class IndexDescriptor;
class IndexAccessMethod;
struct InsertDeleteOptions;
/**
* how many: 1 per Collection
* lifecycle: attached to a Collection
*/
class IndexCatalog {
public:
IndexCatalog(Collection* collection);
~IndexCatalog();
// must be called before used
Status init(OperationContext* opCtx);
bool ok() const;
// ---- accessors -----
bool haveAnyIndexes() const;
int numIndexesTotal(OperationContext* opCtx) const;
int numIndexesReady(OperationContext* opCtx) const;
int numIndexesInProgress(OperationContext* opCtx) const {
return numIndexesTotal(opCtx) - numIndexesReady(opCtx);
}
/**
* this is in "alive" until the Collection goes away
* in which case everything from this tree has to go away
*/
bool haveIdIndex(OperationContext* opCtx) const;
/**
* Returns the spec for the id index to create by default for this collection.
*/
BSONObj getDefaultIdIndexSpec(
ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion) const;
IndexDescriptor* findIdIndex(OperationContext* opCtx) const;
/**
* Find index by name. The index name uniquely identifies an index.
*
* @return null if cannot find
*/
IndexDescriptor* findIndexByName(OperationContext* opCtx,
StringData name,
bool includeUnfinishedIndexes = false) const;
/**
* Find index by matching key pattern and collation spec. The key pattern and collation spec
* uniquely identify an index.
*
* Collation is specified as a normalized collation spec as returned by
* CollationInterface::getSpec. An empty object indicates the simple collation.
*
* @return null if cannot find index, otherwise the index with a matching key pattern and
* collation.
*/
IndexDescriptor* findIndexByKeyPatternAndCollationSpec(
OperationContext* opCtx,
const BSONObj& key,
const BSONObj& collationSpec,
bool includeUnfinishedIndexes = false) const;
/**
* Find indexes with a matching key pattern, putting them into the vector 'matches'. The key
* pattern alone does not uniquely identify an index.
*
* Consider using 'findIndexByName' if expecting to match one index.
*/
void findIndexesByKeyPattern(OperationContext* opCtx,
const BSONObj& key,
bool includeUnfinishedIndexes,
std::vector* matches) const;
/**
* Returns an index suitable for shard key range scans.
*
* This index:
* - must be prefixed by 'shardKey', and
* - must not be a partial index.
* - must have the simple collation.
*
* If the parameter 'requireSingleKey' is true, then this index additionally must not be
* multi-key.
*
* If no such index exists, returns NULL.
*/
IndexDescriptor* findShardKeyPrefixedIndex(OperationContext* opCtx,
const BSONObj& shardKey,
bool requireSingleKey) const;
void findIndexByType(OperationContext* opCtx,
const std::string& type,
std::vector& matches,
bool includeUnfinishedIndexes = false) const;
/**
* Reload the index definition for 'oldDesc' from the CollectionCatalogEntry. 'oldDesc'
* must be a ready index that is already registered with the index catalog. Returns an
* unowned pointer to the descriptor for the new index definition.
*
* Use this method to notify the IndexCatalog that the spec for this index has changed.
*
* It is invalid to dereference 'oldDesc' after calling this method. This method broadcasts
* an invalidateAll() on the cursor manager to notify other users of the IndexCatalog that
* this descriptor is now invalid.
*/
const IndexDescriptor* refreshEntry(OperationContext* opCtx, const IndexDescriptor* oldDesc);
// never returns NULL
const IndexCatalogEntry* getEntry(const IndexDescriptor* desc) const;
IndexAccessMethod* getIndex(const IndexDescriptor* desc);
const IndexAccessMethod* getIndex(const IndexDescriptor* desc) const;
/**
* Returns a not-ok Status if there are any unfinished index builds. No new indexes should
* be built when in this state.
*/
Status checkUnfinished() const;
class IndexIterator {
public:
bool more();
IndexDescriptor* next();
// returns the access method for the last return IndexDescriptor
IndexAccessMethod* accessMethod(const IndexDescriptor* desc);
// returns the IndexCatalogEntry for the last return IndexDescriptor
IndexCatalogEntry* catalogEntry(const IndexDescriptor* desc);
private:
IndexIterator(OperationContext* opCtx,
const IndexCatalog* cat,
bool includeUnfinishedIndexes);
void _advance();
bool _includeUnfinishedIndexes;
OperationContext* const _opCtx;
const IndexCatalog* _catalog;
IndexCatalogEntryContainer::const_iterator _iterator;
bool _start; // only true before we've called next() or more()
IndexCatalogEntry* _prev;
IndexCatalogEntry* _next;
friend class IndexCatalog;
};
IndexIterator getIndexIterator(OperationContext* opCtx, bool includeUnfinishedIndexes) const {
return IndexIterator(opCtx, this, includeUnfinishedIndexes);
};
// ---- index set modifiers ------
/**
* Call this only on an empty collection from inside a WriteUnitOfWork. Index creation on an
* empty collection can be rolled back as part of a larger WUOW. Returns the full specification
* of the created index, as it is stored in this index catalog.
*/
StatusWith createIndexOnEmptyCollection(OperationContext* opCtx, BSONObj spec);
StatusWith prepareSpecForCreate(OperationContext* opCtx,
const BSONObj& original) const;
/**
* Drops all indexes in the index catalog, optionally dropping the id index depending on the
* 'includingIdIndex' parameter value. If the 'droppedIndexes' parameter is not null,
* it is filled with the names and index info of the dropped indexes.
*/
Status dropAllIndexes(OperationContext* opCtx,
bool includingIdIndex,
std::map* droppedIndexes = nullptr);
Status dropIndex(OperationContext* opCtx, IndexDescriptor* desc);
/**
* will drop all incompleted indexes and return specs
* after this, the indexes can be rebuilt
*/
std::vector getAndClearUnfinishedIndexes(OperationContext* opCtx);
struct IndexKillCriteria {
std::string ns;
std::string name;
BSONObj key;
};
// ---- modify single index
/**
* Returns true if the index 'idx' is multikey, and returns false otherwise.
*/
bool isMultikey(OperationContext* opCtx, const IndexDescriptor* idx);
/**
* Returns the path components that cause the index 'idx' to be multikey if the index supports
* path-level multikey tracking, and returns an empty vector if path-level multikey tracking
* isn't supported.
*
* If the index supports path-level multikey tracking but isn't multikey, then this function
* returns a vector with size equal to the number of elements in the index key pattern where
* each element in the vector is an empty set.
*/
MultikeyPaths getMultikeyPaths(OperationContext* opCtx, const IndexDescriptor* idx);
// --- these probably become private?
/**
* disk creation order
* 1) system.indexes entry
* 2) collection's NamespaceDetails
* a) info + head
* b) _indexBuildsInProgress++
* 3) indexes entry in .ns file
* 4) system.namespaces entry for index ns
*/
class IndexBuildBlock {
MONGO_DISALLOW_COPYING(IndexBuildBlock);
public:
IndexBuildBlock(OperationContext* opCtx, Collection* collection, const BSONObj& spec);
~IndexBuildBlock();
Status init();
void success();
/**
* index build failed, clean up meta data
*/
void fail();
IndexCatalogEntry* getEntry() {
return _entry;
}
private:
Collection* const _collection;
IndexCatalog* const _catalog;
const std::string _ns;
BSONObj _spec;
std::string _indexName;
std::string _indexNamespace;
IndexCatalogEntry* _entry;
bool _inProgress;
OperationContext* _opCtx;
};
// ----- data modifiers ------
/**
* When 'keysInsertedOut' is not null, it will be set to the number of index keys inserted by
* this operation.
*
* This method may throw.
*/
Status indexRecords(OperationContext* opCtx,
const std::vector& bsonRecords,
int64_t* keysInsertedOut);
/**
* When 'keysDeletedOut' is not null, it will be set to the number of index keys removed by
* this operation.
*/
void unindexRecord(OperationContext* opCtx,
const BSONObj& obj,
const RecordId& loc,
bool noWarn,
int64_t* keysDeletedOut);
// ------- temp internal -------
std::string getAccessMethodName(OperationContext* opCtx, const BSONObj& keyPattern) {
return _getAccessMethodName(opCtx, keyPattern);
}
Status _upgradeDatabaseMinorVersionIfNeeded(OperationContext* opCtx,
const std::string& newPluginName);
// public static helpers
static BSONObj fixIndexKey(const BSONObj& key);
/**
* Fills out 'options' in order to indicate whether to allow dups or relax
* index constraints, as needed by replication.
*/
static void prepareInsertDeleteOptions(OperationContext* opCtx,
const IndexDescriptor* desc,
InsertDeleteOptions* options);
private:
static const BSONObj _idObj; // { _id : 1 }
bool _shouldOverridePlugin(OperationContext* opCtx, const BSONObj& keyPattern) const;
/**
* 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.
*/
std::string _getAccessMethodName(OperationContext* opCtx, const BSONObj& keyPattern) const;
void _checkMagic() const;
Status _indexFilteredRecords(OperationContext* opCtx,
IndexCatalogEntry* index,
const std::vector& bsonRecords,
int64_t* keysInsertedOut);
Status _indexRecords(OperationContext* opCtx,
IndexCatalogEntry* index,
const std::vector& bsonRecords,
int64_t* keysInsertedOut);
Status _unindexRecord(OperationContext* opCtx,
IndexCatalogEntry* index,
const BSONObj& obj,
const RecordId& loc,
bool logIfError,
int64_t* keysDeletedOut);
/**
* this does no sanity checks
*/
Status _dropIndex(OperationContext* opCtx, IndexCatalogEntry* entry);
// just does disk hanges
// doesn't change memory state, etc...
void _deleteIndexFromDisk(OperationContext* opCtx,
const std::string& indexName,
const std::string& indexNamespace);
// descriptor ownership passes to _setupInMemoryStructures
// initFromDisk: Avoids registering a change to undo this operation when set to true.
// You must set this flag if calling this function outside of a UnitOfWork.
IndexCatalogEntry* _setupInMemoryStructures(OperationContext* opCtx,
IndexDescriptor* descriptor,
bool initFromDisk);
// Apply a set of transformations to the user-provided index object 'spec' to make it
// conform to the standard for insertion. This function adds the 'v' field if it didn't
// exist, removes the '_id' field if it exists, applies plugin-level transformations if
// appropriate, etc.
static StatusWith _fixIndexSpec(OperationContext* opCtx,
Collection* collection,
const BSONObj& spec);
Status _isSpecOk(OperationContext* opCtx, const BSONObj& spec) const;
Status _doesSpecConflictWithExisting(OperationContext* opCtx, const BSONObj& spec) const;
int _magic;
Collection* const _collection;
const int _maxNumIndexesAllowed;
IndexCatalogEntryContainer _entries;
// These are the index specs of indexes that were "leftover".
// "Leftover" means they were unfinished when a mongod shut down.
// Certain operations are prohibited until someone fixes.
// Retrieve by calling getAndClearUnfinishedIndexes().
std::vector _unfinishedIndexes;
};
}