ThinSQLite++
A thin, safe and convenient modern C++ wrapper for SQLite API.
|
The following two objects and their methods comprise the essential elements of the ThinSQLite++ interface:
ThinSQLite++ has many APIs. However, most of the APIs are optional and very specialized and can be ignored by beginners. The core API is small, simple, and easy to learn. This article summarizes the core API.
The principal task of an SQL database engine is to evaluate SQL statements of SQL. To accomplish this, the developer needs two objects:
Strictly speaking, the prepared statement object is not required since the convenience wrapper interface, database::exec, can be used and this convenience wrapper encapsulates and hides the prepared statement object. Nevertheless, an understanding of prepared statements is needed to make full use of ThinSQLite++.
The database connection and prepared statement objects are controlled by a small set of interfaces listed below.
Here is a summary of what the core interfaces do:
This static method opens a connection to an SQLite database file and returns an std::unique_ptr to the database connection object. This is often the first ThinSQLite++ API call that an application makes and is a prerequisite for most other ThinSQLite++ APIs. This routine is the only way to construct the database connection object.
This static method converts SQL text into a prepared statement object and returns an std::unique_ptr to that object. This interface requires a database connection reference created by a prior call to database::open and a text string containing the SQL statement to be prepared. This API does not actually evaluate the SQL statement. It merely prepares the SQL statement for evaluation.
Think of each SQL statement as a small computer program. The purpose of statement::create is to compile that program into object code. The prepared statement is the object code. The statement::step interface then runs the object code to get a result.
This method is used to evaluate a prepared statement that has been previously created by the statement::create interface. The statement is evaluated up to the point where the first row of results are available. To advance to the second row of results, invoke statement::step again. Continue invoking statement::step until it returns false
indicating that the statement is complete. Statements that do not return results (ex: INSERT, UPDATE, or DELETE statements) run to completion on a single call to statement::step.
This template method returns a single column from the current row of a result set for a prepared statement that is being evaluated by statement::step. Each time statement::step stops with a new result set row, this method can be called multiple times to find the values of all columns in that row.
You indicate the desired return value type via a template parameter to statement::column_value. Possible types are:
char8_t
is supported by your compiler/library)An application will typically use database::open to create a single database connection during initialization. Note that database::open can be used to either open existing database files or to create and open new database files. While many applications use only a single database connection, there is no reason why an application cannot call database::open multiple times in order to open multiple database connections - either to the same database or to different databases. Sometimes a multi-threaded application will create separate database connections for each thread. Note that a single database connection can access two or more databases using the ATTACH SQL command, so it is not necessary to have a separate database connection for each database file.
To run an SQL statement, the application follows these steps:
The foregoing is all one really needs to know in order to use SQLite effectively. All the rest is optimization and detail.
A row_range is a convenience wrapper that exposes statement::step loop as STL forward range. It yields row objects which, in turn, model a random access container of cells. Using these wrappers you can handle data extraction as STL iteration, using range-for loops and STL algorithms.
The database::exec methods are convenience wrappers that parse multiple SQL statements and execute statement::create and statement::step loop with a single function call. An optional callback function passed into database::exec is used to process each row of the result set.
It is important to realize that neither row_range nor database::exec do anything that cannot be accomplished using the core methods. In fact, these wrappers are implemented purely in terms of the core routines.
In prior discussion, it was assumed that each SQL statement is prepared once, evaluated, then destroyed. However, SQLite allows the same prepared statement to be evaluated multiple times. This is accomplished using the following:
After a prepared statement has been evaluated by one or more calls to statement::step, it can be reset in order to be evaluated again by a call to statement::reset. Think of statement::reset as rewinding the prepared statement program back to the beginning. Using statement::reset on an existing prepared statement rather than creating a new prepared statement avoids unnecessary calls to statement::create. For many SQL statements, the time needed to run statement::create equals or exceeds the time needed by statement::step. So avoiding calls to statement::create can give a significant performance improvement.
As with any "destruction" action ensuring that statement::reset is called on all code paths before reuse can be tricky and best dealt with RAII. The auto_reset performs statement::reset on destruction and can greatly simply managing statement reuse.
It is not commonly useful to evaluate the exact same SQL statement more than once. More often, one wants to evaluate similar statements. For example, you might want to evaluate an INSERT statement multiple times with different values. Or you might want to evaluate the same query multiple times using a different key in the WHERE clause. To accommodate this, SQLite allows SQL statements to contain parameters which are "bound" to values prior to being evaluated. These values can later be changed and the same prepared statement can be evaluated a second time using the new values.
SQLite allows a parameter wherever a string literal, blob literal, numeric constant, or NULL is allowed in queries or data modification statements. (DQL or DML) (Parameters may not be used for column or table names, or as values for constraints or default values. (DDL)) A parameter takes one of the following forms:
?
?NNN
:AAA
$AAA
@AAA
In the examples above, NNN
is an integer value and AAA
is an identifier. A parameter initially has a value of NULL. Prior to calling statement::step for the first time or immediately after statement::reset, the application can invoke one of the statement::bind methods to attach values to the parameters. Each call to statement::bind overrides prior bindings on the same parameter.
An application is allowed to prepare multiple SQL statements in advance and evaluate them as needed. There is no arbitrary limit to the number of outstanding prepared statements. Some applications call statement::create multiple times at start-up to create all of the prepared statements they will ever need. Other applications keep a cache of the most recently used prepared statements and then reuse prepared statements out of the cache when available. Another approach is to only reuse prepared statements when they are inside of a loop.
The default configuration for SQLite works great for most applications. But sometimes developers want to tweak the setup to try to squeeze out a little more performance, or take advantage of some obscure feature.
The config interface is used to make global, process-wide configuration changes for SQLite. The config interface must be called before any database connections are created. The config interface allows the programmer to do things like:
After process-wide configuration is complete and database connections have been created, individual database connections can be configured using calls to database::limit and database::config.
ThinSQLite++ includes interfaces that can be used to extend SQLite functionality. Such routines include:
The database::create_collation methods is used to create new collating sequences for sorting text.
The database::create_function methods create new SQL functions - either scalar or aggregate. The new function implementation typically makes use of the context and value objects and the following additional interfaces:
The database::create_module can be used to create Virtual Table interfaces. More information can be found in Implementing Virtual Tables.
Shared libraries or DLLs can be used as loadable extensions to SQLite.
This article only mentions the most important and most commonly used ThinSQLite++ interfaces. The ThinSQLite++ library includes many other APIs implementing useful features that are not described here. Refer to the list of topics, content of the thinsqlitepp namespace or the list of classes for complete and authoritative information about all ThinSQLite++ interfaces.