| /** |
| * Copyright 2011-2015 Quickstep Technologies LLC. |
| * Copyright 2015-2016 Pivotal Software, 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. |
| **/ |
| |
| #ifndef QUICKSTEP_CATALOG_CATALOG_DATABASE_HPP_ |
| #define QUICKSTEP_CATALOG_CATALOG_DATABASE_HPP_ |
| |
| #include <exception> |
| #include <string> |
| #include <unordered_map> |
| |
| #include "catalog/Catalog.pb.h" |
| #include "catalog/CatalogDatabaseLite.hpp" |
| #include "catalog/CatalogRelation.hpp" |
| #include "catalog/CatalogTypedefs.hpp" |
| #include "storage/StorageConstants.hpp" |
| #include "threading/Mutex.hpp" |
| #include "threading/SharedMutex.hpp" |
| #include "threading/SpinSharedMutex.hpp" |
| #include "utility/Macros.hpp" |
| #include "utility/PtrVector.hpp" |
| #include "utility/StringUtil.hpp" |
| |
| #include "glog/logging.h" |
| |
| namespace quickstep { |
| |
| class Catalog; |
| class CatalogRelationSchema; |
| |
| /** \addtogroup Catalog |
| * @{ |
| */ |
| |
| /** |
| * @brief Exception thrown for a relation name collision. |
| **/ |
| class RelationNameCollision : public std::exception { |
| public: |
| /** |
| * @brief Constructor. |
| * |
| * @param db_name Name of the database in which the collision occurred. |
| * @param rel_name Name of the relation for which there was a collision. |
| **/ |
| RelationNameCollision(const std::string &db_name, const std::string &rel_name) |
| : message_("RelationNameCollision: database ") { |
| message_.append(db_name); |
| message_.append(" already has a relation "); |
| message_.append(rel_name); |
| } |
| |
| ~RelationNameCollision() throw() { |
| } |
| |
| virtual const char* what() const throw() { |
| return message_.c_str(); |
| } |
| |
| private: |
| std::string message_; |
| }; |
| |
| /** |
| * @brief Exception thrown when a relation with the specified name can't be |
| * found. |
| **/ |
| class RelationNameNotFound : public std::exception { |
| public: |
| /** |
| * @brief Constructor. |
| * |
| * @param db_name Name of the database in which the exception occurred. |
| * @param rel_name The relation name which could not be found. |
| **/ |
| RelationNameNotFound(const std::string &db_name, const std::string &rel_name) |
| : message_("RelationNameNotFound: database ") { |
| message_.append(db_name); |
| message_.append(" has no relation named "); |
| message_.append(rel_name); |
| } |
| |
| ~RelationNameNotFound() throw() { |
| } |
| |
| virtual const char* what() const throw() { |
| return message_.c_str(); |
| } |
| |
| private: |
| std::string message_; |
| }; |
| |
| /** |
| * @brief Exception thrown when a relation with the specified ID can't be found. |
| **/ |
| class RelationIdNotFound : public std::exception { |
| public: |
| /** |
| * @brief Constructor |
| * |
| * @param db_name Name of the database in which the exception occurred. |
| * @param id The relation ID which could not be found. |
| **/ |
| RelationIdNotFound(const std::string &db_name, const relation_id id) |
| : message_("RelationIdNotFound: database ") { |
| message_.append(db_name); |
| message_.append(" has no relation with ID "); |
| message_.append(std::to_string(id)); |
| } |
| |
| ~RelationIdNotFound() throw() { |
| } |
| |
| virtual const char* what() const throw() { |
| return message_.c_str(); |
| } |
| |
| private: |
| std::string message_; |
| }; |
| |
| /** |
| * @brief A single database in the catalog. |
| **/ |
| class CatalogDatabase : public CatalogDatabaseLite { |
| public: |
| typedef std::unordered_map<std::string, CatalogRelation*>::size_type size_type; |
| typedef PtrVector<CatalogRelation, true>::const_skip_iterator const_iterator; |
| |
| enum class Status { |
| kConsistent = 0, |
| kPendingBlockDeletions |
| }; |
| |
| /** |
| * @brief Create a new database. |
| * |
| * @param parent The catalog this database belongs to. |
| * @param name This database's name. |
| * @param id This database's ID (defaults to -1, which means invalid/unset). |
| **/ |
| CatalogDatabase(Catalog *parent, const std::string &name, const database_id id = -1) |
| : CatalogDatabaseLite(id), |
| parent_(parent), |
| name_(name), |
| status_(Status::kConsistent) { |
| } |
| |
| /** |
| * @brief Reconstruct a database from its serialized Protocol Buffer form. |
| * |
| * @param proto The Protocol Buffer serialization of a database, previously |
| * produced by getProto(). |
| **/ |
| explicit CatalogDatabase(const serialization::CatalogDatabase &proto); |
| |
| /** |
| * @brief Check whether a serialization::CatalogDatabase is fully-formed and |
| * all parts are valid. |
| * |
| * @param proto A serialized Protocol Buffer representation of a CatalogDatabase, |
| * originally generated by getProto(). |
| * @return Whether proto is fully-formed and valid. |
| **/ |
| static bool ProtoIsValid(const serialization::CatalogDatabase &proto); |
| |
| /** |
| * @brief Destructor which recursively destroys children. |
| **/ |
| ~CatalogDatabase() override { |
| } |
| |
| bool hasRelationWithId(const relation_id id) const override { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| return hasRelationWithIdUnsafe(id); |
| } |
| |
| const CatalogRelationSchema& getRelationSchemaById(const relation_id id) const override { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| DCHECK(hasRelationWithIdUnsafe(id)); |
| return rel_vec_[id]; |
| } |
| |
| /** |
| * @exception RelationIdNotFound No relation with the given ID exists. |
| **/ |
| void dropRelationById(const relation_id id) override; |
| |
| /** |
| * @brief Get the parent catalog. |
| * |
| * @return Parent catalog. |
| **/ |
| const Catalog& getParent() const { |
| return *DCHECK_NOTNULL(parent_); |
| } |
| |
| /** |
| * @brief Get a mutable pointer to the parent catalog. |
| * |
| * @return Parent catalog. |
| **/ |
| Catalog* getParentMutable() { |
| return parent_; |
| } |
| |
| /** |
| * @brief Get this database's name. |
| * |
| * @return This database's name. |
| **/ |
| const std::string& getName() const { |
| return name_; |
| } |
| |
| /** |
| * @brief Get this database's status. |
| * |
| * @return This database's status. |
| **/ |
| Status status() const { |
| SpinSharedMutexSharedLock<false> lock(status_mutex_); |
| return status_; |
| } |
| |
| /** |
| * @brief Whether this database is consistent. |
| * |
| * @return True if it is consistent. Otherwise, false. |
| **/ |
| bool isStatusConsistent() const { |
| SpinSharedMutexSharedLock<false> lock(status_mutex_); |
| return status_ == Status::kConsistent; |
| } |
| |
| /** |
| * @brief Set this database's status. |
| * |
| * @param status The status to set. |
| **/ |
| void setStatus(const Status status) { |
| SpinSharedMutexExclusiveLock<false> lock(status_mutex_); |
| status_ = status; |
| } |
| |
| /** |
| * @brief Check whether a relation with the given name exists. The search is case-insensitive. |
| * |
| * @param rel_name The name to check for. |
| * @return Whether the relation exists. |
| **/ |
| bool hasRelationWithName(const std::string &rel_name) const { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| return hasRelationWithNameUnsafe(rel_name); |
| } |
| |
| /** |
| * @brief Get a relation by name. The search is case-insensitive. |
| * |
| * @param rel_name The name to search for. |
| * @return The relation with the given name. NULL if the relation is not found. |
| **/ |
| const CatalogRelation* getRelationByName(const std::string &rel_name) const; |
| |
| /** |
| * @brief Get a mutable pointer to a relation by name. The search is case-insensitive. |
| * |
| * @param rel_name The name to search for. |
| * @return The relation with the given name. NULL if the relation is not found. |
| **/ |
| CatalogRelation* getRelationByNameMutable(const std::string &rel_name); |
| |
| /** |
| * @brief Get a relation by ID. |
| * |
| * @param id The id to search for. |
| * @return The relation with the given ID. |
| **/ |
| const CatalogRelation* getRelationById(const relation_id id) const { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| if (hasRelationWithIdUnsafe(id)) { |
| return &rel_vec_[id]; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| /** |
| * @brief Get a mutable pointer to a relation by ID. |
| * |
| * @param id The id to search for. |
| * @return The relation with the given ID. |
| **/ |
| CatalogRelation* getRelationByIdMutable(const relation_id id) { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| if (hasRelationWithIdUnsafe(id)) { |
| return &(rel_vec_[id]); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| /** |
| * @brief Add a new relation to the database. If the relation already has an |
| * ID and/or parent, it will be overwritten. |
| * |
| * @param new_rel The relation to be added. |
| * @exception RelationNameCollision A relation with the same name as new_rel |
| * is already present in the database. |
| * @return The id assigned to the relation. |
| **/ |
| relation_id addRelation(CatalogRelation *new_rel); |
| |
| /** |
| * @brief Drop (delete) a relation by name. |
| * |
| * @param rel_name The name of the relation to drop. |
| * @exception RelationNameNotFound No relation with the given name exists. |
| **/ |
| void dropRelationByName(const std::string &rel_name); |
| |
| /** |
| * @brief Serialize the database as Protocol Buffer. |
| * |
| * @return The Protocol Buffer representation of the database. |
| **/ |
| serialization::CatalogDatabase getProto() const; |
| |
| /** |
| * @brief Get the number of child relations. |
| * |
| * @return The number of child relations. |
| **/ |
| size_type size() const { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| return rel_map_.size(); |
| } |
| |
| /** |
| * @brief Get an iterator at the beginning of the child relations. |
| * |
| * @return An iterator on the first child relation. |
| **/ |
| const_iterator begin() const { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| return rel_vec_.begin_skip(); |
| } |
| |
| /** |
| * @brief Get an iterator at one-past-the-end of the child relations. |
| * |
| * @return An iterator one-past-the-end of the child relations. |
| **/ |
| const_iterator end() const { |
| SpinSharedMutexSharedLock<false> lock(relations_mutex_); |
| return rel_vec_.end_skip(); |
| } |
| |
| private: |
| /** |
| * @brief Set the parent Catalog of this database. Used by Catalog (a friend |
| * of this class) when adding a new database. |
| * |
| * @param parent The new parent for this CatalogDatabase. |
| **/ |
| void setParent(Catalog *parent) { |
| parent_ = parent; |
| } |
| |
| /** |
| * @brief Set the ID of this database. Used by Catalog (a friend of this |
| * class) when adding a new database. |
| * |
| * @param id The new ID for this CatalogDatabase. |
| **/ |
| void setID(const database_id id) { |
| id_ = id; |
| } |
| |
| /** |
| * @brief Check whether a relation_id is within the range of IDs contained |
| * in this CatalogDatabase. |
| * |
| * @param id The id to check. |
| * @return true if id is in range, false otherwise. |
| * |
| * @note The caller of this function should make it sure that it holds at |
| * least a shared lock before calling this function. |
| * |
| **/ |
| bool idInRange(const relation_id id) const { |
| return ((id >= 0) |
| && (static_cast<PtrVector<CatalogRelation>::size_type>(id) < rel_vec_.size())); |
| } |
| |
| /** |
| * @brief Check whether a relation with the given name exists. The search is case-insensitive. |
| * |
| * @note This method assumes that the caller has already acquired the |
| * necessary locks before invoking it. |
| * |
| * @param rel_name The name to check for. |
| * @return Whether the relation exists. |
| **/ |
| inline bool hasRelationWithNameUnsafe(const std::string &rel_name) const { |
| return (rel_map_.find(ToLower(rel_name)) != rel_map_.end()); |
| } |
| |
| /** |
| * @brief Check whether a relation with the given id exists. |
| * |
| * @note This method assumes that the caller has already acquired the |
| * necessary locks before invoking it. |
| * |
| * @param id The id to check for. |
| * @return Whether the relation exists. |
| **/ |
| inline bool hasRelationWithIdUnsafe(const relation_id id) const { |
| return (idInRange(id) && !rel_vec_.elementIsNull(id)); |
| } |
| |
| Catalog *parent_; |
| |
| // The database name. |
| const std::string name_; |
| |
| // Indicate the status of this database (i.e., consistent or not). |
| Status status_; |
| alignas(kCacheLineBytes) mutable SpinSharedMutex<false> status_mutex_; |
| |
| // A vector of relations. NULL if the relation has dropped from the database. |
| PtrVector<CatalogRelation, true> rel_vec_; |
| |
| /** |
| * @brief Map from relation name to the pointer to the corresponding relation. |
| * The relation name stored in the map is in lowercase characters. |
| */ |
| std::unordered_map<std::string, CatalogRelation*> rel_map_; |
| |
| // Concurrency protection for 'rel_vec_' and 'rel_map_'. |
| alignas(kCacheLineBytes) mutable SpinSharedMutex<false> relations_mutex_; |
| |
| friend class Catalog; |
| |
| DISALLOW_COPY_AND_ASSIGN(CatalogDatabase); |
| }; |
| |
| /** @} */ |
| |
| } // namespace quickstep |
| |
| #endif // QUICKSTEP_CATALOG_CATALOG_DATABASE_HPP_ |