Overview
Advantages | Disadvantages |
---|---|
The key features are:
|
The main disadvantages are:
|
Tutorial
/// Shaun Harker
/// Sample Program for SQLambda
/// 2015-05-18
#include <iostream>
#include <exception>
#include <cmath>
#include <cstdint>
#include "sqlambda.h"
using namespace sqlite;
int main ( void ) {
int64_t N = 1000000;
try {
// Open a database. Create if does not exist.
database db("dbfile.db");
// Create a table "user" in database
db . exec ( "create table if not exists test (name Integer, data Real);" );
// Begin a transaction
db . exec ( "begin;" );
// Create a prepared statement for insertion
statement prepped = db . prepare ( "insert into test (name, data) values (?, ?);" );
// Insert N rows into table "user"
for ( int64_t i = 0; i < N; ++ i )
prepped . bind ( i, std::sqrt((double)i) ) . exec ();
// Commit the transaction
db . exec ( "end;" );
// Create a prepared statement for selection
prepped = db . prepare ( "select * from test where data < 8.0;");
// Process the results using a lambda expression
prepped . forEach ( [] (int name, double data) {
std::cout << "Name: " << name << "\t Data: " << data << "\n";
});
}
catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
API
Our API consists of 2 classes with 4 public methods each. They are as follows:
|
|
A database
object wraps sqlite3
object which SQLite uses to represent database sessions. A statement
object wraps an sqlite3_stmt
object which SQLite uses to represent prepared statements. Both of these classes provide casting operators, so the user can get direct access to the objects and call the SQLite C API directly if needs be.
The database
and statement
classes use RAII (Resource Acquisition Is Initialization); the constructor of each class acquires its resource (database connection or prepared statement) and releases it when the deconstructor is called. Or nearly so. Actually, we use std::shared_ptr
to hold wrappers ; so if the database
(respectively, statement
) object is duplicated, reference counting will prevent one duplicate going out of scope from destroying an object still in scope.
The database
methods are straightforward to understand. The constructor and casting methods have already been explained. The method database::prepare
creates a statement
object. The database::exec
method is actually just shorthand for creating a statement
object and calling statement::exec
on it.
The statement
methods are only slightly harder to understand. Notice there is no public constructor; a statement
object must be created by a database
object. The casting method has already been explained. The statement::bind
method is used to bind variables to prepared statements. It takes a variadic template argument. This allows it to be called with any sequence of arguments of any types. It will attempt to bind the arguments it is given to the ?
slots that occur in the prepared statement. Any previous bindings will be forgotten. The statement::exec
function executes the prepared statement. If there is expected output (say, for instance, the prepared statement was a SELECT
statement), then this is a poor choice. Instead, statement::forEach
should be called with a callback function as an argument. A callback function can be a lambda function (hence, SQLambda!) which takes arguments corresponding to the columns in the returned rows and returns void (i.e. it operates purely through side effects).
Philosophy
The design philosophy behind SQLambda is to provide an efficient pipeline between SQLite database operations and modern-style C++ programming constructs for the most common use cases. In particular, we want to provide fast mechanisms for repeated inserts with prepared statements, and provide convenient processing of the rows returned by SELECT
statements.
No attempt is made to wrap the entire functionality of the SQLite C interface with C++. This decision is motivated by the desire to prevent needlessly reinventing the interface language. We simply want to allow modern idioms for common operations. For uncommon operations the well-documented C API provided by SQLite should suffice; for this reason we make sure our interface transparently exposes the underlying C API rather than attempting to invent our own abstractions.
See Also
There are many other C++ wrappers for SQLite available. I attempt to give a comparison below. Given the difficulty of surveying 15K lines of code it is likely I have misclassified at least one item. Further, some of these projects remain under active development and may gain new features. Finally, the choices of features and their categorization into good, ok, and bad are based on my personal bias, so it might not pay to take this too seriously.
Project Name | Creation Date | C++11 | Supports NULL | Prepared Statements | Encoding | Installation | SLOC |
---|---|---|---|---|---|---|---|
2006-01-23 |
NO |
NO |
YES |
UTF-8 only |
Many .h + .cpp |
~2850 |
|
2009-12-06 |
NO |
Write Only |
YES |
UTF-8/UTF-16 |
.h + .cpp |
~1200 |
|
2010-09-16 |
NO |
NO |
NO |
UTF-8 only |
Many .h + .cpp |
~3000 |
|
2011-06-24 |
NO |
YES |
YES |
UTF-8 only |
.h + .cpp |
~1800 |
|
2012-01-23 |
NO |
Write Only |
YES |
UTF-8 only |
2*(.h+.cpp) |
~3000 |
|
2012-03-30 |
NO |
YES |
YES |
UTF-8 only |
Many .h + .cpp |
~2000 |
|
2014-05-11 |
YES |
/w BOOST |
NO |
UTF-16 only |
Headers only |
~450 |
|
2014-08-23 |
YES |
Write Only |
YES |
UTF-8 only |
2*(.h + .cpp) |
~1300 |
|
SQLambda |
2015-05-18 |
YES |
NO |
YES |
UTF-8 only |
Header only |
~350 |