Skip to content

Mark Statement::execute() and the fetch family [[nodiscard]] (#59)#60

Closed
mariuz wants to merge 1 commit into
asfernandes:mainfrom
mariuz:nodiscard
Closed

Mark Statement::execute() and the fetch family [[nodiscard]] (#59)#60
mariuz wants to merge 1 commit into
asfernandes:mainfrom
mariuz:nodiscard

Conversation

@mariuz

@mariuz mariuz commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #59, as offered there.

What

  • Statement::execute() and the cursor-movement family (fetchNext/fetchPrior/fetchFirst/fetchLast/fetchAbsolute/fetchRelative) are now [[nodiscard]], each with a C++20 reason message, so the silent first-row-loss shape

    stmt.execute(transaction);
    while (stmt.fetchNext())  // starts at row TWO

    becomes a compiler warning at the call site. The doxygen comments gain the missing @return lines and a note on the execute-fetches-first-row semantics.

  • The tests' intentional discards (DDL/DML executes, reads of a known-existing row, re-execution checks) are annotated with explicit (void) casts — 49 sites, found mechanically by compiling with the new header and fixing every warning, so the suite stays warning-free under -Wall//W4.

  • The README example gains a short note explaining why the loop is if (execute()) do … while (fetchNext()); and what the (void) idiom is for.

Why

While writing 43 small fb-cpp programs (hands-on companions to a Firebird architecture paper), three of them independently wrote the execute(); while (fetchNext()) shape and silently lost the first row — no error, just missing data. Details and field report in #59.

Verification

  • Every library and test translation unit compiles warning-free with the new attributes (g++ 13, -std=c++20 -Wall, Linux aarch64).
  • All changed files pass clang-format 20.1.8 (the version check-format.sh uses) byte-for-byte.
  • I could not link/run the Boost.Test suite locally (no vcpkg toolchain on this machine) — relying on CI for the run; the changes to test files are (void) casts only.

🤖 Generated with Claude Code

…ndes#59)

execute() on a SELECT opens the cursor and already fetches the first
row, so the natural-looking 'execute(); while (fetchNext())' loop
compiles and runs but silently drops row one.  Marking execute() and
the cursor-movement methods [[nodiscard]] (with a reason message)
turns that shape into a compiler warning at every call site.

Intentional discards - DDL/DML executes and reads of a known-existing
row - are annotated with an explicit (void) cast in the tests, and the
README example gains a short note about the idiom.

Closes asfernandes#59
Comment thread src/fb-cpp/Statement.h
/// @param transaction Transaction that will own the execution context.
/// @return `true` when execution yields a record.
///
[[nodiscard("execute() already fetches a SELECT's first row; looping on fetchNext() alone skips it")]]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you see, this is not needed for DDL and I do not want that (void) thing everywhere.

@mariuz mariuz closed this Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants