Skip to content

Commit ea712b1

Browse files
committed
Split RowExecutor from Statement
1 parent dae2167 commit ea712b1

File tree

7 files changed

+441
-336
lines changed

7 files changed

+441
-336
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ build
44
example1
55
*.a
66

7+
.vs/
78
.vscode/
89
/SQLiteCpp.sln
910
*.ncb

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ set(SQLITECPP_SRC
105105
${PROJECT_SOURCE_DIR}/src/Column.cpp
106106
${PROJECT_SOURCE_DIR}/src/Database.cpp
107107
${PROJECT_SOURCE_DIR}/src/Exception.cpp
108+
${PROJECT_SOURCE_DIR}/src/RowExecutor.cpp
108109
${PROJECT_SOURCE_DIR}/src/Savepoint.cpp
109110
${PROJECT_SOURCE_DIR}/src/Statement.cpp
110111
${PROJECT_SOURCE_DIR}/src/Transaction.cpp
@@ -119,6 +120,7 @@ set(SQLITECPP_INC
119120
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Column.h
120121
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Database.h
121122
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Exception.h
123+
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/RowExecutor.h
122124
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Savepoint.h
123125
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Statement.h
124126
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Transaction.h

include/SQLiteCpp/Column.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ T Statement::getColumns()
283283
template<typename T, const int... Is>
284284
T Statement::getColumns(const std::integer_sequence<int, Is...>)
285285
{
286-
return T{Column(mpPreparedStatement, Is)...};
286+
return T{Column(getStatement(), Is)...};
287287
}
288288

289289
#endif

include/SQLiteCpp/RowExecutor.h

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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

Comments
 (0)