
On 9/5/25 22:23, Andrzej Krzemienski via Boost wrote:
czw., 4 wrz 2025 o 20:54 Klemens Morgenstern via Boost <
I do not see a clear resource management in the class `statement`. Is
`statement` associated with a resource that needs to be managed? The class has a comment "This transfers ownership to the resultset". Ownership of what? What is the full lifetime of this implied resource? Can execute() be called multiple times? If so, do you transfer the same ownership of something to multiple places?
The overload of execute qualified with && transfers ownership of the statement to the resultset, so you can do this:
conn.prepare("select * from x where y = %1").execute(42);
where as this, does not, so you can call executor multiple times:
auto s = conn.prepare("select * from x where y = %1"); s.execute(42); s.execute(43);
This is because I introduced multiple classes (statement, resultset, row) that all use the same type (sqlite3_stmt) underneath.
I don't think replicating the C APi here with a single class is helpful, especially given the required order of operations, hence the resultset & row. Therefore I split the functionality over multiple classes but they all share a single resource.
Because a prepared-statement is the way to do parameterized queries in sqlite, there'll be a lot of uses of a statement with a single execution, hence allowing the user not to keep the statement around, but just the resultset.
I fully support introducing multiple classes (statement, resultset, row). I am still struggling with understanding what kind of resource is being managed here. From your description it looks like there is a "shared state": created in function prepare and then consumed by class resultset and released by the last of all result sets and the statement object. Is this right?
Approaching this from a different angle, I would naively expect the following two pieces of code to do the same thing (they don't): // 1 void process_users(connection &conn) { statement st = conn.prepare("select * from users where name = ?"); resultset rs = st.execute({"allen"}); for (row const& r : rs) handle(r); } //2 void process_users(connection &conn) { resultset rs = conn.prepare("select * from users where name = ?").execute({"allen"}); for (row const& r : rs) handle(r); } And depending on what resultset is, the following two pieces of code also do the same thing or both break for the same reasons (they don't): //3 resultset get_users(connection & conn) { statement s = conn.prepare("SELECT * from users where name = ?"); resultset rs = s.execute({"allen"}); return rs; } void process_users(connection &conn) { resultset rs = get_users(conn); for (row const& r : rs) handle(r); } //4 resultset get_users(connection & conn) { resultset rs = conn.prepare("SELECT * from users where name = ?").execute({"allen"}); return rs } void process_users(connection &conn) { resultset rs = get_users(conn); for (row const& r : rs) handle(r); } Granted, 3 and 4 are documented to behave very differently: 3 is UB, 4 not.