|
| 1 | +/** |
| 2 | + * @file RowExecutor.h |
| 3 | + * @ingroup SQLiteCpp |
| 4 | + * @brief TODO: |
| 5 | + * |
| 6 | + * Copyright (c) 2015 Shibao HONG ([email protected]) |
| 7 | + * Copyright (c) 2015-2021 Sebastien Rombauts ([email protected]) |
| 8 | + * |
| 9 | + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt |
| 10 | + * or copy at http://opensource.org/licenses/MIT) |
| 11 | + */ |
| 12 | +#pragma once |
| 13 | + |
| 14 | + //#include <SQLiteCpp/Database.h> |
| 15 | + #include <SQLiteCpp/Exception.h> |
| 16 | + |
| 17 | +#include <memory> |
| 18 | +#include <string> |
| 19 | +#include <map> |
| 20 | + |
| 21 | +// Forward declaration to avoid inclusion of <sqlite3.h> in a header |
| 22 | +struct sqlite3_stmt; |
| 23 | + |
| 24 | +namespace SQLite |
| 25 | +{ |
| 26 | + |
| 27 | +extern const int OK; ///< SQLITE_OK |
| 28 | + |
| 29 | + |
| 30 | +class RowExecutor |
| 31 | +{ |
| 32 | +public: |
| 33 | + /// Shared pointer to SQLite Prepared Statement Object |
| 34 | + using TStatementPtr = std::shared_ptr<sqlite3_stmt>; |
| 35 | + |
| 36 | + /// Weak pointer to SQLite Prepared Statement Object |
| 37 | + using TStatementWeakPtr = std::weak_ptr<sqlite3_stmt>; |
| 38 | + |
| 39 | + /// Shared pointer to SQLite RowExecutor |
| 40 | + using TRowPtr = std::shared_ptr<RowExecutor>; |
| 41 | + |
| 42 | + /// Weak pointer to SQLite RowExecutor |
| 43 | + using TRowWeakPtr = std::weak_ptr<RowExecutor>; |
| 44 | + |
| 45 | + /// Type to store columns names and indexes |
| 46 | + using TColumnsMap = std::map<std::string, int, std::less<>>; |
| 47 | + |
| 48 | + /// Reset the statement to make it ready for a new execution. Throws an exception on error. |
| 49 | + void reset(); |
| 50 | + |
| 51 | + /// Reset the statement. Returns the sqlite result code instead of throwing an exception on error. |
| 52 | + int tryReset() noexcept; |
| 53 | + |
| 54 | + /** |
| 55 | + * @brief Execute a step of the prepared query to fetch one row of results. |
| 56 | + * |
| 57 | + * While true is returned, a row of results is available, and can be accessed |
| 58 | + * through the getColumn() method |
| 59 | + * |
| 60 | + * @see exec() execute a one-step prepared statement with no expected result |
| 61 | + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. |
| 62 | + * @see Database::exec() is a shortcut to execute one or multiple statements without results |
| 63 | + * |
| 64 | + * @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it |
| 65 | + * then you have to call executeStep() again to fetch more rows until the query is finished |
| 66 | + * - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result |
| 67 | + * (case of a query with no result, or after N rows fetched successfully) |
| 68 | + * |
| 69 | + * @throw SQLite::Exception in case of error |
| 70 | + */ |
| 71 | + bool executeStep(); |
| 72 | + |
| 73 | + /** |
| 74 | + * @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. |
| 75 | + * |
| 76 | + * |
| 77 | + * |
| 78 | + * @see exec() execute a one-step prepared statement with no expected result |
| 79 | + * @see executeStep() execute a step of the prepared query to fetch one row of results |
| 80 | + * @see Database::exec() is a shortcut to execute one or multiple statements without results |
| 81 | + * |
| 82 | + * @return the sqlite result code. |
| 83 | + */ |
| 84 | + int tryExecuteStep() noexcept; |
| 85 | + |
| 86 | + /** |
| 87 | + * @brief Execute a one-step query with no expected result, and return the number of changes. |
| 88 | + * |
| 89 | + * This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : |
| 90 | + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" |
| 91 | + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" |
| 92 | + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" |
| 93 | + * |
| 94 | + * It is similar to Database::exec(), but using a precompiled statement, it adds : |
| 95 | + * - the ability to bind() arguments to it (best way to insert data), |
| 96 | + * - reusing it allows for better performances (efficient for multiple insertion). |
| 97 | + * |
| 98 | + * @see executeStep() execute a step of the prepared query to fetch one row of results |
| 99 | + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. |
| 100 | + * @see Database::exec() is a shortcut to execute one or multiple statements without results |
| 101 | + * |
| 102 | + * @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE) |
| 103 | + * |
| 104 | + * @throw SQLite::Exception in case of error, or if row of results are returned while they are not expected! |
| 105 | + */ |
| 106 | + int exec(); |
| 107 | + |
| 108 | + //////////////////////////////////////////////////////////////////////////// |
| 109 | + |
| 110 | + /** |
| 111 | + * @brief Execute a step of the prepared query to fetch one row of results. |
| 112 | + * |
| 113 | + * While true is returned, a row of results is available, and can be accessed |
| 114 | + * through the getColumn() method |
| 115 | + * |
| 116 | + * @see exec() execute a one-step prepared statement with no expected result |
| 117 | + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. |
| 118 | + * @see Database::exec() is a shortcut to execute one or multiple statements without results |
| 119 | + * |
| 120 | + * @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it |
| 121 | + * then you have to call executeStep() again to fetch more rows until the query is finished |
| 122 | + * - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result |
| 123 | + * (case of a query with no result, or after N rows fetched successfully) |
| 124 | + * |
| 125 | + * @throw SQLite::Exception in case of error |
| 126 | + */ |
| 127 | + const TColumnsMap& getColumnsNames() const; |
| 128 | + |
| 129 | + /// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). |
| 130 | + int getChanges() const noexcept; |
| 131 | + |
| 132 | + /// Return the number of columns in the result set returned by the prepared statement |
| 133 | + int getColumnCount() const |
| 134 | + { |
| 135 | + return mColumnCount; |
| 136 | + } |
| 137 | + /// true when a row has been fetched with executeStep() |
| 138 | + bool hasRow() const |
| 139 | + { |
| 140 | + return mbHasRow; |
| 141 | + } |
| 142 | + /// true when the last executeStep() had no more row to fetch |
| 143 | + bool isDone() const |
| 144 | + { |
| 145 | + return mbDone; |
| 146 | + } |
| 147 | + |
| 148 | + //////////////////////////////////////////////////////////////////////////// |
| 149 | + |
| 150 | + /// Return the numeric result code for the most recent failed API call (if any). |
| 151 | + int getErrorCode() const noexcept; |
| 152 | + |
| 153 | + /// Return the extended numeric result code for the most recent failed API call (if any). |
| 154 | + int getExtendedErrorCode() const noexcept; |
| 155 | + |
| 156 | + /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). |
| 157 | + const char* getErrorMsg() const noexcept; |
| 158 | + |
| 159 | +protected: |
| 160 | + /** |
| 161 | + * |
| 162 | + */ |
| 163 | + explicit RowExecutor(sqlite3* apSQLite, const std::string& aQuery); |
| 164 | + |
| 165 | + /** |
| 166 | + * @brief Return a std::shared_ptr with SQLite Statement Object. |
| 167 | + * |
| 168 | + * @return raw pointer to Statement Object |
| 169 | + */ |
| 170 | + TStatementPtr getStatement() const noexcept; |
| 171 | + |
| 172 | + /** |
| 173 | + * @brief Return a prepared SQLite Statement Object. |
| 174 | + * |
| 175 | + * Throw an exception if the statement object was not prepared. |
| 176 | + * @return raw pointer to Prepared Statement Object |
| 177 | + */ |
| 178 | + sqlite3_stmt* getPreparedStatement() const; |
| 179 | + |
| 180 | + /** |
| 181 | + * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message |
| 182 | + * |
| 183 | + * @param[in] aRet SQLite return code to test against the SQLITE_OK expected value |
| 184 | + */ |
| 185 | + void check(const int aRet) const |
| 186 | + { |
| 187 | + if (SQLite::OK != aRet) |
| 188 | + { |
| 189 | + throw SQLite::Exception(mpSQLite, aRet); |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception. |
| 195 | + */ |
| 196 | + void checkRow() const |
| 197 | + { |
| 198 | + if (false == mbHasRow) |
| 199 | + { |
| 200 | + throw SQLite::Exception("No row to get a column from. executeStep() was not called, or returned false."); |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + /** |
| 205 | + * @brief Check if there is a Column index is in the range of columns in the result. |
| 206 | + */ |
| 207 | + void checkIndex(const int aIndex) const |
| 208 | + { |
| 209 | + if ((aIndex < 0) || (aIndex >= mColumnCount)) |
| 210 | + { |
| 211 | + throw SQLite::Exception("Column index out of range."); |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | +private: |
| 216 | + /// Create prepared SQLite Statement Object |
| 217 | + void prepareStatement(const std::string& aQuery); |
| 218 | + |
| 219 | + /// Get column number and create map with columns names |
| 220 | + void createColumnInfo(); |
| 221 | + |
| 222 | + sqlite3* mpSQLite{}; //!< Pointer to SQLite Database Connection Handle |
| 223 | + TStatementPtr mpStatement{}; //!< Shared Pointer to the prepared SQLite Statement Object |
| 224 | + |
| 225 | + int mColumnCount{ 0 }; //!< Number of columns in the result of the prepared statement |
| 226 | + bool mbHasRow{ false }; //!< true when a row has been fetched with executeStep() |
| 227 | + bool mbDone{ false }; //!< true when the last executeStep() had no more row to fetch |
| 228 | + |
| 229 | + /// Map of columns index by name (mutable so getColumnIndex can be const) |
| 230 | + mutable TColumnsMap mColumnNames{}; |
| 231 | +}; |
| 232 | + |
| 233 | + |
| 234 | +} // namespace SQLite |
0 commit comments