SQLite includes a number of convenience functions that can be used to prepare, step, and finalize an SQL statement in one call. Most of these functions exist for historical reasons and, as the name says, convenience.
While they’re not fully deprecated, there are a number
of reasons why their use is not exactly encouraged. First off, understand
that there is nothing special under the hood. Both of these functions eventually call the same sqlite3_prepare_xxx(), sqlite3_step(), and sqlite3_finalize() calls that are available
in the public API. These functions are not faster, nor are they more
efficient.
Second, since the API doesn’t support the use of bound parameters, you’re forced to use string manipulations to build your SQL commands. That means these functions are slower to process and much more vulnerable to SQL injection attacks. This is particularly dangerous because all the convenience functions are designed to automatically process multiple SQL statements from a single command string. If input strings are not properly sanitized, this situation effectively gives anyone providing input data full access to the database engine, including the ability to delete data or drop whole tables.
These functions also tend to be a bit slower. All results are returned in a string representation, without any kind of type information. This can make it difficult to determine the type of a return value, and can lead to a lot of extra type conversions.
For all their disadvantages, there is still the simple
fact that these functions are very convenient. If you’re just trying to
throw together a quick and dirty snippet of code, these functions provide an
easy means of doing that. They’re also perfectly acceptable for DDL
commands, such as CREATE TABLE. For any
type of DML command, especially those
that involve values from unsanitized sources, I strongly recommend using the
normal prepare, step, and finalize routines. You’ll end up with safer code
and better performance.
The first function allows for fairly generic execution of any SQL command string.
int sqlite3_exec( sqlite3 *db, const char *sql,
callback_ptr, void *userData, char **errMsg )
Prepares and executes one or more SQL statements, calling
the optional callback for each result set row for each
statement. The first parameter is a valid database
connection. The second parameter is a UTF-8 encoded string
that consists of one or more SQL statements. The third
parameter is a pointer to a callback function. The
prototype of this function is given below. This function
pointer can be NULL. The fourth parameter is a user-data
pointer that will be passed to the callback. The value can
be whatever you want, including NULL. The fifth parameter
is a reference to a character pointer. If an error is
generated and this parameter is non-NULL, sqlite3_exec() will
allocate a string buffer and return it. If the passed-back
pointer is non-NULL, you are responsible for releasing the
buffer with sqlite3_free() once you are done with
it.
If the SQL string consists of multiple SQL statements separated by semicolons, each statement will be executed in turn.
If the call is successful and all
statements are processed without errors, SQLITE_OK will be
returned. Otherwise, just about any of the other return
codes are possible, since this one function runs through
the whole statement preparation and execution
process.
The sqlite3_exec()
function is reasonably all encompassing, and can be used to execute any SQL
statement. If you’re executing a table query and want to access the result
set, you will need to supply a function pointer that references a
user-defined callback. This callback will be called once for each row
returned. If you’re executing an SQL statement that does not normally return
any database value, there is no need to provide a callback function. The
success or failure of the SQL command will be indicated in the return
value.
The sqlite3_exec()
function makes any database results available through a user-defined callback function. As each
result row is computed, the callback is called to make the
row data available to your code. Essentially, each internal call to
sqlite3_step() that results in a return value of
SQLITE_ROW results in a
callback.
The format of the callback looks like this:
int user_defined_exec_callback( void *userData, int numCol,
char **colData, char **colName )
This function is not part of the
SQLite API. Rather, this shows the required format for a
user-defined sqlite3_exec() callback. The first
parameter is the user-data pointer passed in as the fourth
parameter to sqlite3_exec(). The second parameter
indicates how many columns exist in this row. The third
and fourth parameters both return an array of strings
(char pointers).
The third parameter holds the data values for this row,
while the forth parameter holds the column names. All
values are returned as strings. There is no type
information.
Normally, the callback should return a
zero value. If a nonzero value is returned, execution is
stopped and sqlite3_exec() will return SQLITE_ABORT.
The second, third, and fourth parameters act very
similar to the traditional C variables argc and argv (and an
extra argv) in main( int argc, char **argv ), the traditional start to
every C program. The column value and name arrays will always be the same
size for any given callback, but the specific size of the arrays and the
column names can change over the course of processing a multi-statement SQL
string. There is no need to release any of these values. Once your callback
function returns, sqlite3_exec() will
handle all the memory management.
If you’d prefer not to mess with a callback, you can use
sqlite3_get_table() to extract a
whole table at once. Be warned, however, that this can consume a large
amount of memory, and must be used carefully.
While you can technically call sqlite3_get_table() with any SQL command string, it is
specifically designed to work with SELECT
statements.
int sqlite3_get_table( sqlite3 *db, const char *sql, char ***result,
int *numRow, int *numCol, char **errMsg );
Prepares and executes an SQL command string, consisting of one or more SQL statements. The full contents of the result set(s) is returned in an array of UTF-8 strings.
The first parameter is a database
connection. The second parameter is a UTF-8 encoded SQL
command string that consists of one or more SQL
statements. The third parameter is a reference to a
one-dimensional array of strings (char pointers). The results of the query
are passed back through this reference. The fourth and
fifth parameters are integer references that pass back the
number of rows and the number of columns, respectively, in
the result array. The sixth and final parameter is a
reference to a character string, and is used to return any
error message.
The result array consists of (numCol * (numRow + 1))
entries. Entries zero through numCol -
1 hold the column names. Each
additional set of numCol entries holds one row worth of
data.
If the call is successful and all
statements are processed without errors, SQLITE_OK will be
returned. Otherwise, just about any of the other return
codes are possible, since this one function runs through
the whole statement preparation and execution
process.
void sqlite3_free_table( char **result )
Correctly frees the memory allocated by a successful
call to sqlite3_get_table(). Do not attempt to
free this memory yourself.
As indicated, you must release the result of a call to
sqlite3_get_table() with a call to
sqlite3_free_table(). This will
properly release the individual allocations used to build the result value.
As with sqlite3_exec(), you must call
sqlite3_free() on any errMsg value that is returned.
The result array is a one-dimensional array of character pointers. You must compute your own offsets into the array using the formula:
/* offset to access column C of row R of **result */ int offset = ((R + 1) * numCol) + C; char *value = result[offset];
The “+ 1” used to
compute the row offset is required to skip over the column names, which are
stored in the first row of the result. This assumes that the first row and
column would be accessed with an index of zero.
As a convenience function, there is nothing special
about sqlite3_get_table(). In fact, it is
just a wrapper around sqlite3_exec(). It
offers no additional performance benefits over the prepare, step, and
finalize interfaces. In fact, between all the type conversions inherent in
sqlite3_exec(), and all the memory
allocations, sqlite3_get_table() has
substantial overhead over other methods.
Since sqlite3_get_table() is a wrapper around sqlite3_exec(), it is possible to pass in an
SQL command string that consists of multiple SQL statements. In the case of
sqlite3_get_table(), this must be
done with care, however.
If more than one SELECT statement is passed in, there is no way to determine
where one result set ends and the next begins. All the resulting rows are
run together as one large result array. All of the statements must return
the same number of columns, or the whole sqlite3_get_table() command will fail. Additionally, only
the first statement will return any column names. To avoid these issues, it
is best to call sqlite3_get_table() with
single SQL commands.
There are a number of reasons why these convenience
functions may not be the best choice. Their use requires building an SQL
command statement using string manipulation functions, and that process
tends to be error prone. However, if you insist, your best bet is to use one
of SQLite’s built-in string-building functions: sqlite3_mprintf(),
sqlite3_vmprintf(), or sqlite3_snprintf(). See Appendix G for more details.