// 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 <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.
*/

#pragma once

#include <vector>

#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* txn);

    bool ok() const;

    // ---- accessors -----

    bool haveAnyIndexes() const;
    int numIndexesTotal(OperationContext* txn) const;
    int numIndexesReady(OperationContext* txn) const;
    int numIndexesInProgress(OperationContext* txn) const {
        return numIndexesTotal(txn) - numIndexesReady(txn);
    }

    /**
     * this is in "alive" until the Collection goes away
     * in which case everything from this tree has to go away
     */

    bool haveIdIndex(OperationContext* txn) 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* txn) const;

    /**
     * Find index by name.  The index name uniquely identifies an index.
     *
     * @return null if cannot find
     */
    IndexDescriptor* findIndexByName(OperationContext* txn,
                                     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* txn,
        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* txn,
                                 const BSONObj& key,
                                 bool includeUnfinishedIndexes,
                                 std::vector<IndexDescriptor*>* 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* txn,
                                               const BSONObj& shardKey,
                                               bool requireSingleKey) const;

    void findIndexByType(OperationContext* txn,
                         const std::string& type,
                         std::vector<IndexDescriptor*>& 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* txn, 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* txn,
                      const IndexCatalog* cat,
                      bool includeUnfinishedIndexes);

        void _advance();

        bool _includeUnfinishedIndexes;

        OperationContext* const _txn;
        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* txn, bool includeUnfinishedIndexes) const {
        return IndexIterator(txn, 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<BSONObj> createIndexOnEmptyCollection(OperationContext* txn, BSONObj spec);

    StatusWith<BSONObj> prepareSpecForCreate(OperationContext* txn, const BSONObj& original) const;

    Status dropAllIndexes(OperationContext* txn, bool includingIdIndex);

    Status dropIndex(OperationContext* txn, IndexDescriptor* desc);

    /**
     * will drop all incompleted indexes and return specs
     * after this, the indexes can be rebuilt
     */
    std::vector<BSONObj> getAndClearUnfinishedIndexes(OperationContext* txn);


    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* txn, 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* txn, 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* txn, 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* _txn;
    };

    // ----- 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* txn,
                        const std::vector<BsonRecord>& 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* txn,
                       const BSONObj& obj,
                       const RecordId& loc,
                       bool noWarn,
                       int64_t* keysDeletedOut);

    // ------- temp internal -------

    std::string getAccessMethodName(OperationContext* txn, const BSONObj& keyPattern) {
        return _getAccessMethodName(txn, keyPattern);
    }

    Status _upgradeDatabaseMinorVersionIfNeeded(OperationContext* txn,
                                                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* txn,
                                           const IndexDescriptor* desc,
                                           InsertDeleteOptions* options);

private:
    static const BSONObj _idObj;  // { _id : 1 }

    bool _shouldOverridePlugin(OperationContext* txn, 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* txn, const BSONObj& keyPattern) const;

    void _checkMagic() const;

    Status _indexFilteredRecords(OperationContext* txn,
                                 IndexCatalogEntry* index,
                                 const std::vector<BsonRecord>& bsonRecords,
                                 int64_t* keysInsertedOut);

    Status _indexRecords(OperationContext* txn,
                         IndexCatalogEntry* index,
                         const std::vector<BsonRecord>& bsonRecords,
                         int64_t* keysInsertedOut);

    Status _unindexRecord(OperationContext* txn,
                          IndexCatalogEntry* index,
                          const BSONObj& obj,
                          const RecordId& loc,
                          bool logIfError,
                          int64_t* keysDeletedOut);

    /**
     * this does no sanity checks
     */
    Status _dropIndex(OperationContext* txn, IndexCatalogEntry* entry);

    // just does disk hanges
    // doesn't change memory state, etc...
    void _deleteIndexFromDisk(OperationContext* txn,
                              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* txn,
                                                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<BSONObj> _fixIndexSpec(OperationContext* txn,
                                             Collection* collection,
                                             const BSONObj& spec);

    Status _isSpecOk(OperationContext* txn, const BSONObj& spec) const;

    Status _doesSpecConflictWithExisting(OperationContext* txn, 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<BSONObj> _unfinishedIndexes;
};
}
