Saturday, March 28, 2015

More SQLite C API

In previous instalment we used sqlite3_open and sqlite3_prepare to connect to db file and execute select statement. Let us take a better look at it again. Those functions are legacy interface, we could use new sqlite3_open_v2 and sqlite3_prepare_v2 which are allowing us more control over execution and they are also recommended by SQLite development team. Let us look at sqlite3_open. This is from SQLite documentation:


In all three cases, the first argument is name of database file to be opened and the second one is handle:

typedef struct sqlite3 sqlite3;


We will need that handle to manipulate db and close it at the end. From comment we see that string with name of db file could be UTF-8 or UTF-16 encoded. What are flags? Here are definitions:


I will wisely skip last four, for now. If we specify SQLITE_OPEN_READWRITE we should be able to read and write, unless OS have marked file as read only, file must exist or error is returned. In order to simulate behavior of sqlite3_open we should use  SQLITE_OPEN_READWRITE |  SQLITE_OPEN_CREATE. The fourth argument of sqlite3_open_v2 function is name of the sqlit3_vfs object that defines the operating system interface that the new database connection should use. If we pass NULL as the fourth argument we get default sqlit3_vfs object.
If  sqlite3_open_v2 doesn't return SQLITE_OK, we have error and we should exit. We still may have proper db handle or NULL handle, calling sqlite3_close with NULL pointer is not problem.
So, we have something like this as template:


Call to sqlite3_errmsg should return string with description of message. We want to do resource management and to match  sqlite3_open with sqlite3_close. If we compile this and we do not have some.db file, after execution we should see descriptive error message.

./test
Failure to open database: unable to open database file


While passing to sqlite3_close NULL pointer is not problem, passing to it handle of already closed connection is problem.
Situation with sqlite3_prepare_v2 is similar to  sqlite3_open_v2, new API is preferable to legacy one and it should be used. Here both new and legacy functions are accepting the same number of arguments but behavior is different. Please check in documentation how they differ.


There is also UTF-16 version with about the same arguments. It prepares statement, compiles it to bytecode  and if successful returns pointer to compiled  sqlite3_stmt.


After statement is successfully prepared we iterate using sqlite3_step and at the end we finalize statement. In the case of sqlite3_prepare  returning error, we in general want to check is statement handle points to something and do cleanup if it does.
There is convenience wrapper around  prepare_v2-step-finalize, it is sqlite3_exec.


If we are doing insert, we do not really need callback, so we pass NULL pointer as the third and fourth argument.


If we don't have table jane in test.db exec will return error.

Exec failure: no such table: jane

or if we execute it more than once:

Exec failure: PRIMARY KEY must be unique

Error message is allocated using sqlite3_malloc and we have to free it using sqlite3_free.

No comments:

Post a Comment