/*------------------------------------------------------------------------------* * Architecture & Implementation of DBMS * *------------------------------------------------------------------------------* * Copyright 2022 Databases and Information Systems Group TU Dortmund * * Visit us at * * http://dbis.cs.tu-dortmund.de/cms/en/home/ * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * * * * Authors: * * Maximilian Berens * * Roland Kühn * * Jan Mühlig * *------------------------------------------------------------------------------* */ #pragma once #include "metadata.h" #include "timestamp.h" #include #include #include #include #include #include #include namespace beedb::concurrency { /** * Isolation levels with more (serializable) or * less (read uncommitted) isolation. */ enum IsolationLevel : std::uint8_t { Serializable = 0, // RepeatableRead = 1, // ReadCommitted = 2, // ReadUncommitted = 3 }; /** * A read set item contains two pointers (in form of record identifiers): * - A pointer to the "original" record placed in the table space * - A pointer to the read version (which may be the same as the "original"). * The first one is needed to compare read set items with the write set items * of other transactions. */ class ReadSetItem { public: ReadSetItem(const storage::RecordIdentifier in_place_record_identifier, const storage::RecordIdentifier read_record_identifier) : _in_place_record_identifier(in_place_record_identifier), _read_record_identifier(read_record_identifier) { } ReadSetItem(ReadSetItem &&) = default; ~ReadSetItem() = default; /** * @return Pointer to the record in the table space. */ [[nodiscard]] storage::RecordIdentifier in_place_record_identifier() const { return _read_record_identifier; } /** * @return Pointer to the really read record (which may * be in table or time travel space). */ [[nodiscard]] storage::RecordIdentifier read_record_identifier() const { return _read_record_identifier; } private: // Pointer to the record in the table space. storage::RecordIdentifier _in_place_record_identifier; // Pointer to the really read record. storage::RecordIdentifier _read_record_identifier; }; /** * The scan set indicates that a transaction read a set * of tuples (select * from ... or aggregation for example). * A scan can miss a record that is inserted/updated/deleted * by a concurrent transaction. Therefore the scan has to be * validated at commit time. */ class ScanSetItem { public: ScanSetItem() = default; explicit ScanSetItem(std::unique_ptr &&predicate) : _predicate(std::move(predicate)) { } explicit ScanSetItem(const table::Table &table) : _table(std::make_optional(std::ref(table))) { } ScanSetItem(ScanSetItem &&) = default; ~ScanSetItem() = default; /** * @return Table that was scanned. */ [[nodiscard]] std::optional> table() const { return _table; } /** * Update table that was scanned. * @param table Table */ void table(const table::Table &table) { _table = std::make_optional(std::ref(table)); } /** * @return Predicate that was used for scanning. */ [[nodiscard]] const std::unique_ptr &predicate() const { return _predicate; } /** * Update predicate that was used for scanning. * @param predicate Predicate that was used for scanning. */ void predicate(std::unique_ptr &&predicate) { _predicate = std::move(predicate); } private: // Table, needed for re-execution. std::optional> _table = std::nullopt; // Predicates, needed for re-execution. std::unique_ptr _predicate{nullptr}; }; /** * A write set item contains two pointers (in form of record identifiers): * - A pointer to the "original" record placed in the table space * - A pointer to the updated/deleted version. * Also the type of modification (inserted/updated/removed) is stored in * the write set. */ class WriteSetItem { public: enum ModificationType { Inserted, Updated, Deleted }; WriteSetItem(const table::Table::id_t table_id, const storage::RecordIdentifier in_place_record_identifier, const storage::RecordIdentifier old_version_record_identifier, const ModificationType modification_type, const storage::Page::offset_t size_written) : _table_id(table_id), _in_place_record_identifier(in_place_record_identifier), _old_version_record_identifier(old_version_record_identifier), _type(modification_type), _written_size(size_written) { } WriteSetItem(const table::Table::id_t table_id, const storage::RecordIdentifier record_identifier, const storage::Page::offset_t size_written) : WriteSetItem(table_id, record_identifier, record_identifier, ModificationType::Inserted, size_written) { } WriteSetItem(WriteSetItem &&) = default; WriteSetItem(const WriteSetItem &) = default; ~WriteSetItem() = default; /** * @return The id of the table this write belongs to. */ [[nodiscard]] table::Table::id_t table_id() const { return _table_id; } /** * @return Pointer to the record that was really written. */ [[nodiscard]] storage::RecordIdentifier in_place_record_identifier() const { return _in_place_record_identifier; } /** * @return Pointer to the record that was overwritten (for updates * and deletes). */ [[nodiscard]] storage::RecordIdentifier old_version_record_identifier() const { return _old_version_record_identifier; } bool operator==(const ModificationType type) const { return _type == type; } /** * @return Number of bytes written. */ [[nodiscard]] storage::Page::offset_t written_size() const { return _written_size; } private: // Id of the table, this write belongs to. table::Table::id_t _table_id; // The record identifier of the record that was updated in place. storage::RecordIdentifier _in_place_record_identifier; // The record identifier of the record that was versioned. storage::RecordIdentifier _old_version_record_identifier; // Type of the update. ModificationType _type; // Size of the written data (inclusively metadata). storage::Page::offset_t _written_size; }; /** * The transaction stores the begin and commit timestamp of the transaction, * as well as the read and write set. */ class Transaction { public: explicit Transaction(const IsolationLevel isolation_level, const timestamp begin) : _isolation_level(isolation_level), _begin_timestamp(begin), _commit_timestamp(timestamp::make_infinity()) { } ~Transaction() = default; /** * @return Isolation level of this transaction. */ [[maybe_unused]] [[nodiscard]] IsolationLevel isolation_level() const { return _isolation_level; } /** * Update the commit timestamp of this transaction. * @param commit_timestamp Timestamp this transaction commits. */ void commit_timestamp(const timestamp commit_timestamp) { _commit_timestamp = commit_timestamp; } /** * @return Timestamp this transaction was started. */ [[nodiscard]] timestamp begin_timestamp() const { return _begin_timestamp; } /** * @return Timestamp this transaction was committed. */ [[nodiscard]] timestamp commit_timestamp() const { return _commit_timestamp; } /** * Adds an item to the read set. * @param read_set_item Item that was read by this transaction. */ void add_to_read_set(ReadSetItem &&read_set_item) { _read_set.emplace_back(std::move(read_set_item)); } /** * Adds an item to the write set. * @param write_set_item Item that was written by this transaction. */ void add_to_write_set(WriteSetItem &&write_set_item) { _write_set.emplace_back(std::move(write_set_item)); } /** * Adds an item to the scan set. * @param scan_set_item Item that was scanned by this transaction. */ void add_to_scan_set(ScanSetItem *scan_set_item) { _scan_set.emplace_back(scan_set_item); } /** * @return Read set of this transaction. */ [[nodiscard]] const std::vector &read_set() const { return _read_set; } /** * @return Write set of this transaction. */ [[nodiscard]] const std::vector &write_set() const { return _write_set; } /** * @return Scan set of this transaction. */ [[nodiscard]] const std::vector &scan_set() const { return _scan_set; } private: // Isolation level of the transaction. const IsolationLevel _isolation_level; // Timestamp this transaction was created. const timestamp _begin_timestamp; // Timestamp this transaction committed. timestamp _commit_timestamp; // Items that were read by this transaction. std::vector _read_set; // Items that were written by this transaction. std::vector _write_set; // Items that were scanned by this transaction. std::vector _scan_set; }; } // namespace beedb::concurrency