From 81d0fe56f87f88df3fcab75fa5767089e7db8c74 Mon Sep 17 00:00:00 2001 From: Kacperos155 Date: Sun, 24 Jul 2022 21:33:58 +0200 Subject: [PATCH] Add RowIterator and Row class fundation --- CMakeLists.txt | 2 ++ include/SQLiteCpp/Column.h | 7 ++-- include/SQLiteCpp/Row.h | 56 ++++++++++++++++++++++++++++++ include/SQLiteCpp/RowExecutor.h | 36 +++++++++++++++----- include/SQLiteCpp/Statement.h | 60 +++++++++++++++++++++++++++++++-- src/Column.cpp | 2 +- src/Row.cpp | 41 ++++++++++++++++++++++ src/RowExecutor.cpp | 12 +++---- src/Statement.cpp | 47 ++++++++++++++++++++++++-- 9 files changed, 239 insertions(+), 24 deletions(-) create mode 100644 include/SQLiteCpp/Row.h create mode 100644 src/Row.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c4e244f..34a4b7c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ set(SQLITECPP_SRC ${PROJECT_SOURCE_DIR}/src/Column.cpp ${PROJECT_SOURCE_DIR}/src/Database.cpp ${PROJECT_SOURCE_DIR}/src/Exception.cpp + ${PROJECT_SOURCE_DIR}/src/Row.cpp ${PROJECT_SOURCE_DIR}/src/RowExecutor.cpp ${PROJECT_SOURCE_DIR}/src/Savepoint.cpp ${PROJECT_SOURCE_DIR}/src/Statement.cpp @@ -120,6 +121,7 @@ set(SQLITECPP_INC ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Column.h ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Database.h ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Exception.h + ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Row.h ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/RowExecutor.h ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Savepoint.h ${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Statement.h diff --git a/include/SQLiteCpp/Column.h b/include/SQLiteCpp/Column.h index 77cab679..2c88f4fb 100644 --- a/include/SQLiteCpp/Column.h +++ b/include/SQLiteCpp/Column.h @@ -54,7 +54,7 @@ class Column * @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object. * @param[in] aIndex Index of the column in the row of result, starting at 0 */ - explicit Column(const Statement::TStatementPtr& aStmtPtr, int aIndex); + explicit Column(const RowExecutor::TStatementPtr& aStmtPtr, int aIndex); // default destructor: the finalization will be done by the destructor of the last shared pointer // default copy constructor and assignment operator are perfectly suited : @@ -252,7 +252,7 @@ class Column } private: - Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object + RowExecutor::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object int mIndex; ///< Index of the column in the row of result, starting at 0 }; @@ -283,9 +283,10 @@ T Statement::getColumns() template T Statement::getColumns(const std::integer_sequence) { - return T{Column(getStatement(), Is)...}; + return T{ Column(getStatement(), Is)... }; } #endif + } // namespace SQLite diff --git a/include/SQLiteCpp/Row.h b/include/SQLiteCpp/Row.h new file mode 100644 index 00000000..40f204a1 --- /dev/null +++ b/include/SQLiteCpp/Row.h @@ -0,0 +1,56 @@ +/** + * @file Row.h + * @ingroup SQLiteCpp + * @brief TODO: + * + * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) + * Copyright (c) 2015-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include + +#include + +namespace SQLite +{ + + +class Row +{ +public: + Row(RowExecutor::TStatementWeakPtr apRow, std::size_t aID); + + std::size_t getRowNumber() const + { + return ID; + } + + /** + * @brief Test if the column value is NULL + * + * @param[in] aIndex Index of the column, starting at 0 + * + * @return true if the column value is NULL + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + bool isColumnNull(const int aIndex) const; + + /** + * @brief Return a pointer to the text value (NULL terminated string) of the column. + * + * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), + * thus you must copy it before using it beyond its scope (to a std::string for instance). + */ + const char* getText(uint32_t aColumnID) const noexcept; + +private: + RowExecutor::TStatementWeakPtr mpRow; + std::size_t ID; +}; + +} // namespace SQLite diff --git a/include/SQLiteCpp/RowExecutor.h b/include/SQLiteCpp/RowExecutor.h index d3ca632d..683ed239 100644 --- a/include/SQLiteCpp/RowExecutor.h +++ b/include/SQLiteCpp/RowExecutor.h @@ -1,7 +1,7 @@ /** * @file RowExecutor.h * @ingroup SQLiteCpp - * @brief TODO: + * @brief Step executor for SQLite prepared Statement Object * * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) * Copyright (c) 2015-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) @@ -130,17 +130,17 @@ class RowExecutor int getChanges() const noexcept; /// Return the number of columns in the result set returned by the prepared statement - int getColumnCount() const + int getColumnCount() const noexcept { return mColumnCount; } /// true when a row has been fetched with executeStep() - bool hasRow() const + bool hasRow() const noexcept { return mbHasRow; } /// true when the last executeStep() had no more row to fetch - bool isDone() const + bool isDone() const noexcept { return mbDone; } @@ -167,7 +167,10 @@ class RowExecutor * * @return raw pointer to Statement Object */ - TStatementPtr getStatement() const noexcept; + TStatementPtr getStatement() const noexcept + { + return mpStatement; + } /** * @brief Return a prepared SQLite Statement Object. @@ -176,6 +179,19 @@ class RowExecutor * @return raw pointer to Prepared Statement Object */ sqlite3_stmt* getPreparedStatement() const; + + /** + * @brief Return a prepared SQLite Statement Object. + * + * Throw an exception if the statement object was not prepared. + * @return raw pointer to Prepared Statement Object + */ + TRowWeakPtr getExecutorWeakPtr() const + { + return mpRowExecutor; + } + + //////////////////////////////////////////////////////////////////////////// /** * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message @@ -222,9 +238,13 @@ class RowExecutor sqlite3* mpSQLite{}; //!< Pointer to SQLite Database Connection Handle TStatementPtr mpStatement{}; //!< Shared Pointer to the prepared SQLite Statement Object - int mColumnCount{ 0 }; //!< Number of columns in the result of the prepared statement - bool mbHasRow{ false }; //!< true when a row has been fetched with executeStep() - bool mbDone{ false }; //!< true when the last executeStep() had no more row to fetch + /// Shared Pointer to this object. + /// Allows RowIterator to execute next step + TRowPtr mpRowExecutor{}; + + int mColumnCount = 0; //!< Number of columns in the result of the prepared statement + bool mbHasRow = false; //!< true when a row has been fetched with executeStep() + bool mbDone = false; //!< true when the last executeStep() had no more row to fetch /// Map of columns index by name (mutable so getColumnIndex can be const) mutable TColumnsMap mColumnNames{}; diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index cd145e16..0f14af36 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -1,7 +1,7 @@ /** * @file Statement.h * @ingroup SQLiteCpp - * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. + * @brief A prepared SQLite Statement Object binder and Column getter. * * Copyright (c) 2012-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) * @@ -541,8 +541,64 @@ class Statement : public RowExecutor /// Return the number of bind parameters in the statement int getBindParameterCount() const noexcept; + //////////////////////////////////////////////////////////////////////////// + + class RowIterator + { + public: + using iterator_category = std::input_iterator_tag; + using value_type = Row; + using reference = const Row&; + using pointer = const Row*; + using difference_type = std::ptrdiff_t; + + RowIterator() = default; + RowIterator(TStatementWeakPtr apStatement, TRowWeakPtr apRow, uint16_t aID) : + mpStatement(apStatement), mpRow(apRow), mID(aID), mRow(apStatement, aID) {} + + reference operator*() const + { + return mRow; + } + pointer operator->() const noexcept + { + return &mRow; + } + + reference operator++() noexcept + { + mRow = Row(mpStatement, ++mID); + advance(); + return mRow; + } + value_type operator++(int) + { + Row copy{ mRow }; + mRow = Row(mpStatement, ++mID); + advance(); + return copy; + } + + bool operator==(const RowIterator& aIt) const; + bool operator!=(const RowIterator& aIt) const + { + return !(*this == aIt); + } + + private: + void advance() noexcept; + + TStatementWeakPtr mpStatement{}; + TRowWeakPtr mpRow{}; + uint16_t mID{}; + Row mRow{ mpStatement, mID }; + }; + + RowIterator begin(); + RowIterator end(); + private: - std::string mQuery; //!< UTF-8 SQL Query + std::string mQuery; //!< UTF-8 SQL Query, }; diff --git a/src/Column.cpp b/src/Column.cpp index f5dc0d98..d1d15a7e 100644 --- a/src/Column.cpp +++ b/src/Column.cpp @@ -26,7 +26,7 @@ const int Null = SQLITE_NULL; // Encapsulation of a Column in a row of the result pointed by the prepared Statement. -Column::Column(const Statement::TStatementPtr& aStmtPtr, int aIndex) : +Column::Column(const RowExecutor::TStatementPtr& aStmtPtr, int aIndex) : mStmtPtr(aStmtPtr), mIndex(aIndex) { diff --git a/src/Row.cpp b/src/Row.cpp new file mode 100644 index 00000000..be2265b3 --- /dev/null +++ b/src/Row.cpp @@ -0,0 +1,41 @@ +/** + * @file Row.cpp + * @ingroup SQLiteCpp + * @brief TODO: + * + * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) + * Copyright (c) 2015-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include + +#include + +#include + +namespace SQLite +{ + + + Row::Row(RowExecutor::TStatementWeakPtr apRow, std::size_t aID) : + mpRow(apRow), ID(aID) + { + } + + bool Row::isColumnNull(const int aIndex) const + { + return false; + } + + const char* Row::getText(uint32_t aColumnID) const noexcept + { + auto statement = mpRow.lock(); + + + auto pText = reinterpret_cast(sqlite3_column_text(statement.get(), aColumnID)); + return (pText ? pText : ""); + } + +} // namespace SQLite diff --git a/src/RowExecutor.cpp b/src/RowExecutor.cpp index 1c0b757e..9c053e98 100644 --- a/src/RowExecutor.cpp +++ b/src/RowExecutor.cpp @@ -1,7 +1,7 @@ /** * @file RowExecutor.cpp * @ingroup SQLiteCpp - * @brief TODO: + * @brief Step executor for SQLite prepared Statement Object * * Copyright (c) 2015 Shibao HONG (shibaohong@outlook.com) * Copyright (c) 2015-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) @@ -24,6 +24,10 @@ namespace SQLite { prepareStatement(aQuery); createColumnInfo(); + + mpRowExecutor.swap(TRowPtr(this, [](const RowExecutor* const) { + //empty destructor to make shared_ptr without ownership + })); } void SQLite::RowExecutor::prepareStatement(const std::string& aQuery) @@ -165,12 +169,6 @@ namespace SQLite return sqlite3_errmsg(mpSQLite); } - // Return std::shared_ptr with SQLite statement object - RowExecutor::TStatementPtr RowExecutor::getStatement() const noexcept - { - return mpStatement; - } - // Return prepered SQLite statement object or throw sqlite3_stmt* RowExecutor::getPreparedStatement() const { diff --git a/src/Statement.cpp b/src/Statement.cpp index 5ee8cf88..038b128f 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -1,7 +1,7 @@ /** * @file Statement.cpp * @ingroup SQLiteCpp - * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. + * @brief A prepared SQLite Statement Object binder and Column getter. * * Copyright (c) 2012-2021 Sebastien Rombauts (sebastien.rombauts@gmail.com) * @@ -22,8 +22,7 @@ namespace SQLite Statement::Statement(const Database& aDatabase, const std::string& aQuery) : - RowExecutor(aDatabase.getHandle(), aQuery), - mQuery(aQuery) + RowExecutor(aDatabase.getHandle(), aQuery), mQuery(aQuery) {} // Clears away all the bindings of a prepared statement (can be associated with #reset() above). @@ -212,4 +211,46 @@ std::string Statement::getExpandedSQL() const { return expandedString; } +Statement::RowIterator Statement::begin() +{ + reset(); + tryExecuteStep(); + return Statement::RowIterator(getStatement(), getExecutorWeakPtr(), 0); +} + +Statement::RowIterator SQLite::Statement::end() +{ + return Statement::RowIterator(); +} + +void SQLite::Statement::RowIterator::advance() noexcept +{ + if (mpRow.expired()) + return; + + auto statement = mpRow.lock(); + statement->tryExecuteStep(); + + if (statement->isDone()) + { + mpRow.reset(); + return; + } +} + +bool SQLite::Statement::RowIterator::operator==(const RowIterator& aIt) const +{ + auto left = mpRow.lock(); + auto right = aIt.mpRow.lock(); + + if (!left && !right) + return true; + + if (left != right) + return false; + + return mID == aIt.mID; +} + + } // namespace SQLite