-
Notifications
You must be signed in to change notification settings - Fork 569
Range for loop for statements #545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
d42e665
b51ba20
f889053
bcd2db8
83bb827
1a9abc8
e7d12d9
c996909
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| #include <SQLiteCpp/Utils.h> // SQLITECPP_PURE_FUNC | ||
|
|
||
| #include <cstdint> | ||
| #include <iterator> | ||
| #include <string> | ||
| #include <map> | ||
| #include <memory> | ||
|
|
@@ -660,6 +661,73 @@ class SQLITECPP_API Statement | |
| /// Shared pointer to SQLite Prepared Statement Object | ||
| using TStatementPtr = std::shared_ptr<sqlite3_stmt>; | ||
|
|
||
| /** | ||
| * @brief Input iterator over the rows of a prepared SELECT statement. | ||
| * | ||
| * Allows range-based for loops over query results: | ||
| * @code | ||
| * SQLite::Statement query(db, "SELECT id, name FROM test"); | ||
| * for (SQLite::Statement& row : query) | ||
| * { | ||
| * std::cout << row.getColumn(0).getInt() << "\n"; | ||
| * } | ||
| * @endcode | ||
| * | ||
| * Each increment calls executeStep() to advance to the next row. | ||
| * Dereferencing returns the Statement itself, giving access to getColumn(). | ||
| * | ||
| * @warning Only one active RowIterator per Statement is supported. | ||
| */ | ||
| struct RowIterator | ||
| { | ||
| using iterator_category = std::input_iterator_tag; | ||
| using value_type = Statement; | ||
| using difference_type = std::ptrdiff_t; | ||
| using pointer = Statement*; | ||
| using reference = Statement&; | ||
|
|
||
| Statement* mpStatement = nullptr; ///< Pointer to the iterated Statement, nullptr when done | ||
|
|
||
| /// Construct an end sentinel (no associated Statement). | ||
| RowIterator() = default; | ||
|
|
||
| /// Construct an iterator pointing to the current row of apStatement. | ||
| SQLITECPP_API explicit RowIterator(Statement* apStatement); | ||
|
|
||
| /// Advance to the next row. Becomes the end sentinel when no rows remain. | ||
| SQLITECPP_API RowIterator& operator++(); | ||
|
|
||
| /// Post-increment: advance to the next row, return a copy of the iterator before advancing. | ||
| SQLITECPP_API RowIterator operator++(int); | ||
|
|
||
| /// Return true when two iterators point to the same row. | ||
| SQLITECPP_API bool operator==(const RowIterator& aOther) const; | ||
|
|
||
| /// Return true when two iterators do not point to the same row. | ||
| SQLITECPP_API bool operator!=(const RowIterator& aOther) const; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR description calls this "full iterator traits." The traits are all here, but the interface isn't: there's no |
||
|
|
||
| /// Dereference to the Statement, giving access to getColumn(). | ||
| SQLITECPP_API Statement& operator*() const; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Return an iterator to the first row of the result set. | ||
| * | ||
| * Calls reset() then executeStep() so that iterating the same Statement | ||
| * a second time always starts from the beginning. | ||
| * Returns the end iterator immediately if the result set is empty. | ||
| * | ||
| * @note Bindings set before the loop are preserved across reset(). | ||
| * | ||
| * @throw SQLite::Exception in case of error | ||
| */ | ||
| RowIterator begin(); | ||
|
|
||
| /** | ||
| * @brief Return the end sentinel iterator (past the last row). | ||
| */ | ||
| RowIterator end(); | ||
|
|
||
| private: | ||
| /** | ||
| * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -355,6 +355,51 @@ std::string Statement::getExpandedSQL() const { | |
| #endif | ||
| } | ||
|
|
||
| Statement::RowIterator::RowIterator(Statement* apStatement): mpStatement(apStatement) | ||
| {} | ||
|
|
||
| Statement::RowIterator& Statement::RowIterator::operator++() | ||
| { | ||
| if (!mpStatement->executeStep()) | ||
| mpStatement = nullptr; | ||
| return *this; | ||
| } | ||
|
|
||
| Statement::RowIterator Statement::RowIterator::operator++(int) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth flagging: the copy this returns shares the one live cursor, so Caching the row would mean copying every column into the iterator, which is not worth it. The honest signal for a single-pass iterator is to return void from post-increment: void RowIterator::operator++(int) { ++*this; }That still satisfies |
||
| { | ||
| const RowIterator tmp(*this); | ||
| ++(*this); | ||
| return tmp; | ||
| } | ||
|
|
||
| bool Statement::RowIterator::operator==(const RowIterator& aOther) const | ||
| { | ||
| return mpStatement == aOther.mpStatement; | ||
| } | ||
|
|
||
| bool Statement::RowIterator::operator!=(const RowIterator& aOther) const | ||
| { | ||
| return !this->operator==(aOther); | ||
| } | ||
|
|
||
| Statement& Statement::RowIterator::operator*() const | ||
| { | ||
| return *mpStatement; | ||
| } | ||
|
|
||
| Statement::RowIterator Statement::begin() | ||
| { | ||
| reset(); | ||
| if (executeStep()) | ||
| return RowIterator { this }; | ||
| return RowIterator { nullptr }; | ||
| } | ||
|
|
||
| Statement::RowIterator Statement::end() | ||
| { | ||
| return RowIterator{ nullptr }; | ||
| } | ||
|
|
||
|
|
||
| // Prepare SQLite statement object and return shared pointer to this object | ||
| Statement::TStatementPtr Statement::prepareStatement() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation is fine, but the doc overstates it. It compares the
Statement*, not the row, so two live iterators over the same statement always compare equal regardless of position. That is correct for a single-pass stream iterator (it is howstd::istream_iteratorworks, and your@warningalready rules out two live iterators). Only the wording needs to match:/// Return true when both iterators refer to the same statement, or are both the end sentinel.