Skip to content

Commit

Permalink
Split RowExecutor from Statement
Browse files Browse the repository at this point in the history
  • Loading branch information
Kacperos155 committed Jul 24, 2022
1 parent dae2167 commit ea712b1
Show file tree
Hide file tree
Showing 7 changed files with 441 additions and 336 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build
example1
*.a

.vs/
.vscode/
/SQLiteCpp.sln
*.ncb
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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/RowExecutor.cpp
${PROJECT_SOURCE_DIR}/src/Savepoint.cpp
${PROJECT_SOURCE_DIR}/src/Statement.cpp
${PROJECT_SOURCE_DIR}/src/Transaction.cpp
Expand All @@ -119,6 +120,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/RowExecutor.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Savepoint.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Statement.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Transaction.h
Expand Down
2 changes: 1 addition & 1 deletion include/SQLiteCpp/Column.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ T Statement::getColumns()
template<typename T, const int... Is>
T Statement::getColumns(const std::integer_sequence<int, Is...>)
{
return T{Column(mpPreparedStatement, Is)...};
return T{Column(getStatement(), Is)...};
}

#endif
Expand Down
234 changes: 234 additions & 0 deletions include/SQLiteCpp/RowExecutor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/**
* @file RowExecutor.h
* @ingroup SQLiteCpp
* @brief TODO:
*
* Copyright (c) 2015 Shibao HONG ([email protected])
* Copyright (c) 2015-2021 Sebastien Rombauts ([email protected])
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#pragma once

//#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Exception.h>

#include <memory>
#include <string>
#include <map>

// Forward declaration to avoid inclusion of <sqlite3.h> in a header
struct sqlite3_stmt;

namespace SQLite
{

extern const int OK; ///< SQLITE_OK


class RowExecutor
{
public:
/// Shared pointer to SQLite Prepared Statement Object
using TStatementPtr = std::shared_ptr<sqlite3_stmt>;

/// Weak pointer to SQLite Prepared Statement Object
using TStatementWeakPtr = std::weak_ptr<sqlite3_stmt>;

/// Shared pointer to SQLite RowExecutor
using TRowPtr = std::shared_ptr<RowExecutor>;

/// Weak pointer to SQLite RowExecutor
using TRowWeakPtr = std::weak_ptr<RowExecutor>;

/// Type to store columns names and indexes
using TColumnsMap = std::map<std::string, int, std::less<>>;

/// Reset the statement to make it ready for a new execution. Throws an exception on error.
void reset();

/// Reset the statement. Returns the sqlite result code instead of throwing an exception on error.
int tryReset() noexcept;

/**
* @brief Execute a step of the prepared query to fetch one row of results.
*
* While true is returned, a row of results is available, and can be accessed
* through the getColumn() method
*
* @see exec() execute a one-step prepared statement with no expected result
* @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
* @see Database::exec() is a shortcut to execute one or multiple statements without results
*
* @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it
* then you have to call executeStep() again to fetch more rows until the query is finished
* - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result
* (case of a query with no result, or after N rows fetched successfully)
*
* @throw SQLite::Exception in case of error
*/
bool executeStep();

/**
* @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
*
*
*
* @see exec() execute a one-step prepared statement with no expected result
* @see executeStep() execute a step of the prepared query to fetch one row of results
* @see Database::exec() is a shortcut to execute one or multiple statements without results
*
* @return the sqlite result code.
*/
int tryExecuteStep() noexcept;

/**
* @brief Execute a one-step query with no expected result, and return the number of changes.
*
* This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" :
* - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP"
* - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE"
* - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK"
*
* It is similar to Database::exec(), but using a precompiled statement, it adds :
* - the ability to bind() arguments to it (best way to insert data),
* - reusing it allows for better performances (efficient for multiple insertion).
*
* @see executeStep() execute a step of the prepared query to fetch one row of results
* @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
* @see Database::exec() is a shortcut to execute one or multiple statements without results
*
* @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE)
*
* @throw SQLite::Exception in case of error, or if row of results are returned while they are not expected!
*/
int exec();

////////////////////////////////////////////////////////////////////////////

/**
* @brief Execute a step of the prepared query to fetch one row of results.
*
* While true is returned, a row of results is available, and can be accessed
* through the getColumn() method
*
* @see exec() execute a one-step prepared statement with no expected result
* @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code.
* @see Database::exec() is a shortcut to execute one or multiple statements without results
*
* @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it
* then you have to call executeStep() again to fetch more rows until the query is finished
* - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result
* (case of a query with no result, or after N rows fetched successfully)
*
* @throw SQLite::Exception in case of error
*/
const TColumnsMap& getColumnsNames() const;

/// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
int getChanges() const noexcept;

/// Return the number of columns in the result set returned by the prepared statement
int getColumnCount() const
{
return mColumnCount;
}
/// true when a row has been fetched with executeStep()
bool hasRow() const
{
return mbHasRow;
}
/// true when the last executeStep() had no more row to fetch
bool isDone() const
{
return mbDone;
}

////////////////////////////////////////////////////////////////////////////

/// Return the numeric result code for the most recent failed API call (if any).
int getErrorCode() const noexcept;

/// Return the extended numeric result code for the most recent failed API call (if any).
int getExtendedErrorCode() const noexcept;

/// Return UTF-8 encoded English language explanation of the most recent failed API call (if any).
const char* getErrorMsg() const noexcept;

protected:
/**
*
*/
explicit RowExecutor(sqlite3* apSQLite, const std::string& aQuery);

/**
* @brief Return a std::shared_ptr with SQLite Statement Object.
*
* @return raw pointer to Statement Object
*/
TStatementPtr getStatement() const noexcept;

/**
* @brief Return a prepared SQLite Statement Object.
*
* Throw an exception if the statement object was not prepared.
* @return raw pointer to Prepared Statement Object
*/
sqlite3_stmt* getPreparedStatement() const;

/**
* @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message
*
* @param[in] aRet SQLite return code to test against the SQLITE_OK expected value
*/
void check(const int aRet) const
{
if (SQLite::OK != aRet)
{
throw SQLite::Exception(mpSQLite, aRet);
}
}

/**
* @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception.
*/
void checkRow() const
{
if (false == mbHasRow)
{
throw SQLite::Exception("No row to get a column from. executeStep() was not called, or returned false.");
}
}

/**
* @brief Check if there is a Column index is in the range of columns in the result.
*/
void checkIndex(const int aIndex) const
{
if ((aIndex < 0) || (aIndex >= mColumnCount))
{
throw SQLite::Exception("Column index out of range.");
}
}

private:
/// Create prepared SQLite Statement Object
void prepareStatement(const std::string& aQuery);

/// Get column number and create map with columns names
void createColumnInfo();

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

/// Map of columns index by name (mutable so getColumnIndex can be const)
mutable TColumnsMap mColumnNames{};
};


} // namespace SQLite
Loading

0 comments on commit ea712b1

Please sign in to comment.