SQLite Forum

sqlite3_step after sqlite3_finalize: what is the expected behavior?
Login

sqlite3_step after sqlite3_finalize: what is the expected behavior?

(1.1) By Alain (McMartin) on 2021-09-18 13:24:01 edited from 1.0 [link] [source]

Up until check-in f7ab01f2, the following program would work:

#include <sqlite3.h>

#include <assert.h>
#include <string.h>
#include <stdio.h>


int main()
{
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc;

    rc = sqlite3_open(":memory:", &db);
    assert(rc == SQLITE_OK);

    const char* sql = "SELECT 0 WHERE 0;";
    rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
    assert(rc == SQLITE_OK);

    rc = sqlite3_finalize(stmt);
    assert(rc == SQLITE_OK);

    rc = sqlite3_step(stmt);
    assert(rc == SQLITE_MISUSE);

    rc = sqlite3_close(db);
    assert(rc == SQLITE_OK);

    printf("All good!\n");

    return 0;
}

Calling sqlite3_step on a finalized statement returned SQLITE_MISUSE, which seems sensible and matches the (current) documentation of sqlite3_step:

** [SQLITE_MISUSE] means that the this routine was called inappropriately.
** Perhaps it was called on a [prepared statement] that has
** already been [sqlite3_finalize | finalized] 

Then, in check-in 52a12e47, the default lookaside configuration was changed and that program crashes with a segmentation fault, which matches the (current) documentation of sqlite3_finalize:

**                  It is a grievous error for the application to try to use
** a prepared statement after it has been finalized.  Any use of a prepared
** statement after it has been finalized can result in undefined and
** undesirable behavior such as segfaults and heap corruption.

So what is the expected behavior? If the documentation of sqlite3_finalize holds the truth, then the documentation of sqlite3_step deserves an update to avoid confusion.

Also, is there any case where using a prepared statement that has been finalized doesn't crash?

(2.1) By Larry Brasfield (larrybr) on 2021-09-18 13:39:31 edited from 2.0 in reply to 1.0 [link] [source]

If the documentation of sqlite3_finalize holds the truth ...

It does. The phrase "can result in undefined behavior" includes the possibility of behavior not recognized as grievous. A better phrasing might be "results in undefined behavior" (which includes not seg-faulting), but that invites the same perplexity you have shared.

then the documentation of sqlite3_step deserves an update to avoid confusion.

I am not seeing it that way yet. What specific part of the doc you quoted for the SQLITE_MISUSE return do you believe to be false or confusing? If that return is seen, it means what is claimed. That logical (if-then) relation does not imply its converse, (which would be "If this routine is called inappropriately, then SQLITE_MISUSE will be returned.") Among the possible outcomes of an inappropriate call is a seg-fault, as is documented explicitly and is implicitly included in "undefined behavior".

At present, I am disinclined to further elaborate that documentation merely to make explicit such a result of elementary logic. Doing so would be in the same category as adding, to every positive assertion, something like: "This does not mean P, Q, R ... (without logical end)."

(3) By Alain (McMartin) on 2021-09-18 14:40:28 in reply to 2.1 [source]

Thanks a lot for the very quick answer!

After re-reading these pieces of documentation, I agree that they don't contradict each other, nor the previous or current behavior.

I guess I need to come up with a way of keeping track of which prepared statements have been finalized, in my own code.