Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions .claude/skills/sqlitecpp-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ description: >-
- [ ] Public API has Doxygen (`@brief`, `@param`, `@return`, `@throw`).
- [ ] Tests added under `tests/`.
- [ ] Build files updated (`CMakeLists.txt`, `meson.build`).
- [ ] `CHANGELOG.md` updated for user-facing changes.
- [ ] `CHANGELOG.md` updated for user-facing changes (in a **separate commit** after opening the PR).

## CHANGELOG conventions
Update `CHANGELOG.md` in the same PR that makes the change, not in a later batch. Add one line per
PR under the current unreleased version heading (`Version X.Y.Z - <year> ???`). Create that heading
if it does not exist yet.
Update `CHANGELOG.md` in the same PR that makes the change, but **in a separate commit** created after
the PR is opened so the PR number is known. Add one line per PR under the current unreleased
version heading (`Version X.Y.Z - <year> ???`). Create that heading if it does not exist yet.

- One bullet per PR: `- <description> (#NNN)`. The PR number is the last token, in parentheses.
- One bullet per PR: `- <description> (#NNN)`. The PR number is the **last token**, in parentheses.
- Write in the imperative mood, present tense: "Add", "Fix", "Update", "Remove". Not "Added",
"Fixes", or "Adding".
- Keep each entry to a single line that names the user-facing effect, not the internal mechanics.
Expand All @@ -32,6 +32,15 @@ if it does not exist yet.
Finalizing the version heading and tagging belong to the release process: see
[[sqlitecpp-release]].

**Workflow for CHANGELOG updates:**
1. Commit source code changes (tests, implementation, etc.) in one or more atomic commits
2. Push the branch and open the PR to obtain the PR number
3. Create a separate commit adding only the CHANGELOG entry with the PR number
4. Push the CHANGELOG commit to the same branch

This keeps commits atomic (CHANGELOG is separate from code) and allows the PR number to be
included in the CHANGELOG entry.

## Pull requests
- Open PRs with `gh pr create` against `master`.
- The maintainer wants a **short and tight** PR description: one or two sentences on what the PR
Expand All @@ -40,14 +49,17 @@ Finalizing the version heading and tagging belong to the release process: see

## Git commits and pushing
- Make **small, atomic commits** that each address a single logical change. Do not mix unrelated changes (e.g., bug fix + feature + formatting) in one commit.
- **CHANGELOG.md updates must be in a separate commit** from source code changes, created after the PR is opened so the PR number can be included.
- Each commit should be **complete and self-contained**: it must compile and pass tests independently.
- Before pushing a branch to the remote, **ask the user for explicit permission** stating the branch name and action (e.g., "Push branch `update-sqlite-3.52.2` to origin?"). Push only after receiving approval.

## Add a method
1. Declare in `include/SQLiteCpp/<Class>.h` with Doxygen.
2. Implement in `src/<Class>.cpp`.
3. Add tests in `tests/<Class>_test.cpp`.
4. Update `CHANGELOG.md`.
4. Commit the changes from steps 1-3.
5. Push the branch and open the PR.
6. Add CHANGELOG entry with the PR number in a separate commit.

## Add a class
1. Create `include/SQLiteCpp/NewClass.h` and `src/NewClass.cpp`.
Expand All @@ -56,3 +68,6 @@ Finalizing the version heading and tagging belong to the release process: see
4. Include in `SQLiteCpp.h` if public API.
5. Create `tests/NewClass_test.cpp`.
6. Add test to `CMakeLists.txt` and `meson.build`.
7. Commit all changes from steps 1-6.
8. Push the branch and open the PR.
9. Add CHANGELOG entry with the PR number in a separate commit.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,3 +313,4 @@ Version 3.4.0 - 2026 ???
- Fix Transaction destructor to catch all exceptions to avoid std::terminate (#559)
- Fix the Meson build when the SQLITECPP_DISABLE_STD_FILESYSTEM option is enabled (#560)
- Add Statement::RowIterator to support range-based for loops over query results (#181)
- Add unit tests for Statement::RowIterator to fix coverage regression (#562)
61 changes: 61 additions & 0 deletions tests/Statement_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,67 @@ TEST(Statement, rangeBasedForWithBind)
EXPECT_EQ(2, rowCount);
}

TEST(Statement, rowIteratorDirectUsage)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, val INTEGER)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (1, 10)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (2, 20)"));
EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (3, 30)"));

SQLite::Statement query(db, "SELECT id, val FROM test ORDER BY id");

// Test direct usage of begin() and end()
auto it = query.begin();
auto endIt = query.end();

// Test operator!= (already covered by range-based for, but explicit test for clarity)
int count = 0;
while (it != endIt)
{
++count;
EXPECT_EQ(count, (*it).getColumn(0).getInt());
++it;
}
EXPECT_EQ(3, count);

// Reset and test post-increment operator
it = query.begin();
count = 0;
while (it != endIt)
{
++count;
EXPECT_EQ(count, (*it).getColumn(0).getInt());
it++;
}
EXPECT_EQ(3, count);

// Test operator== with end iterator
it = query.end();
EXPECT_TRUE(it == endIt);
EXPECT_FALSE(it != endIt);

// Test operator== and operator!= with end iterator obtained from a different call
SQLite::Statement query2(db, "SELECT id, val FROM test ORDER BY id");
auto it2 = query2.end();
EXPECT_TRUE(it == it2);
EXPECT_FALSE(it != it2);

// Test operator!= with different statements
SQLite::Statement query3(db, "SELECT id, val FROM test ORDER BY id");
auto it3 = query3.begin();
auto endIt3 = query3.end();
EXPECT_TRUE(it3 != endIt3);
EXPECT_FALSE(it3 == endIt3);

// Advance to end and check equality
while (it3 != endIt3)
++it3;
EXPECT_TRUE(it3 == endIt3);
EXPECT_FALSE(it3 != endIt3);
}

#endif // C++11

TEST(Statement, getBindParameterCount)
Expand Down
Loading