229 lines
7.7 KiB
Plaintext
229 lines
7.7 KiB
Plaintext
pbe::Database
|
|
=============
|
|
|
|
pbe::Database is a C++ SQL database API. It is implemented as a wrapper around
|
|
PostgreSQL's libpq C client library. Its objective is:
|
|
|
|
- To support a "modern" C++ style with a concise convenient syntax.
|
|
- To avoid the need to construct SQL queries in strings as far as possible.
|
|
- To perform as much compile-time type checking as possible.
|
|
- To have minimal overhead compared to raw libpq.
|
|
|
|
The source code is somewhat commented, and you should consider the comments in
|
|
the source to be more more definitive, comprehensive and up-to-date than this
|
|
file.
|
|
|
|
|
|
Usage
|
|
-----
|
|
|
|
Here's a very quick summary:
|
|
|
|
#include <Database.hh>
|
|
using namespace pbe;
|
|
...
|
|
Database db("dbname=foo, username=blah");
|
|
Query<string,int> insert_thing(db,"insert into things(name,num) values ($1,$2)");
|
|
SingletonQuery<int, string> count_things(db,"select sum(num) from things where name=$1");
|
|
Transaction t(db);
|
|
insert_thing("table",1);
|
|
insert_thing("chair",4);
|
|
int n = count_things("bed");
|
|
t.commit();
|
|
|
|
Now some more details:
|
|
|
|
|
|
* 1. Connect to the database server.
|
|
|
|
The pbe::Database object represents a connection to the database server. Its
|
|
constructor takes a string which defines the connection parameters in the
|
|
standard PostgreSQL format. See the PostgreSQL documentation for details.
|
|
|
|
|
|
* 2. Declare queries.
|
|
|
|
Queries are declared before use. The query constructor takes a reference to the
|
|
database connection and the query's SQL string. Template parameters specify the
|
|
C++ types of the parameters. For example, in this example:
|
|
|
|
Query<string,int> insert_thing(db,"insert into things(name,num) values ($1,$2)");
|
|
|
|
PostgreSQL is given a query string containing two placeholders for parameters, $1
|
|
and $2. Their C++ types are string and int respectively. Compile-time type
|
|
checking ensures that when the query is used the C++ parameters have (or are
|
|
convertible to) these types. A mapping is built in from C++ types to PostgreSQL
|
|
types. At present the mapping is as follows. It is fairly straightforward to
|
|
add more types.
|
|
|
|
C++ Type PostgreSQL type
|
|
-------- ---------------
|
|
std::string TEXT
|
|
int INT4
|
|
time_t TIMESTAMP WITH TIME ZONE
|
|
float FLOAT4
|
|
|
|
The geneeral type 'Query', as shown above, should be used for
|
|
- Queries that return multi-row, multi-column data.
|
|
- Queries that reutrn no data (e.g. update, delete, create table etc.)
|
|
|
|
The following variants are available for other cases. They have the benefit of
|
|
additional type checking and the like.
|
|
|
|
ColumnQuery<ResultType, Param1Type,...>
|
|
- Use this for a query that returns a single column of results, and give the C++
|
|
type for the result as the first template parameter.
|
|
|
|
SingletonQuery<ResultType, Param1Type,...>
|
|
- Use this for a query that returns a single value (one row, one column), e.g.
|
|
"count(*)" or "select sum(...)". The type of the result value is given as the
|
|
first template parameter.
|
|
|
|
OptQuery<ResultType, Param1Type,...>
|
|
- Use this fora query that returns either a single value as above or no results
|
|
at all. Once again the result value is given as the first template parameter.
|
|
|
|
One option for query declaration is to define a subclass of pbe::Database that
|
|
contains them. The existence of the SQL database can then be hidden from the use
|
|
of the class:
|
|
|
|
class ThingList: public Database {
|
|
public:
|
|
Query<string,int> add_thing;
|
|
SingletonQuery<int, string> count_things;
|
|
Query<string> delete_thing;
|
|
|
|
ThingList():
|
|
Database("dbname=things"),
|
|
add_thing(*this, "insert into things(name,num) values ($1,$2)"),
|
|
count_things(*this, "select sum(num) from things where name=$1"),
|
|
delete_thing(*this, "delete from things where name=$1")
|
|
{}
|
|
};
|
|
|
|
...
|
|
ThingList tl;
|
|
tl.add_thing("pen",25);
|
|
tl.add_thing("pencil",50);
|
|
tl.delete_thing("desk");
|
|
int num_lamps = tl.count_things("lamp");
|
|
|
|
|
|
* 3. Use a transaction.
|
|
|
|
To group a series of queries in a transaction, declare a Transaction object.
|
|
This takes a reference to the Database object as a constructor parameter. A
|
|
BEGIN statement is executed when the Transaction object is constructed. After
|
|
running your queries, call the Transaction's commit() method to execute a
|
|
COMMIT statement.
|
|
|
|
If something goes wrong and an exception is thrown, the Transaction object's
|
|
destructor is called as it goes out of scope. At this point a ROLLBACK statement
|
|
is executed.
|
|
|
|
It is probably best to used additional { } to bind the Transaction tightly to the
|
|
queries it is protecting, i.e.
|
|
|
|
...
|
|
{
|
|
Transaction t(db);
|
|
insert_thing(...);
|
|
...more queries...
|
|
t.commit();
|
|
}
|
|
...
|
|
|
|
|
|
* 4. Run the queries.
|
|
|
|
The Query objects are functors, i.e. they are objects that you can use as
|
|
functions. (This is achieved by defining operator().) For example:
|
|
|
|
insert_thing("table",1);
|
|
|
|
The parameters are passed to PostgreSQL using libpq's binary parameter format.
|
|
|
|
The query strings are passed to PostgreSQL the first time that they are used,
|
|
where they are stored as "prepared statements" for further uses. The prepared
|
|
statements are freed ("deallocated") when the Query object goes out of scope.
|
|
|
|
If for some reason you don't want to use prepared statements then you can use the
|
|
runonce() method:
|
|
|
|
insert_thing.runonce("table",1);
|
|
|
|
This should have exactly the same behaviour as the normal case.
|
|
|
|
(Note that although PostgreSQL only supports prepared statements and binary
|
|
parameters for SELECT, UPDATE, INSERT and DELETE statements, you don't need to
|
|
worry as this library detects other statements and treats them specially.)
|
|
|
|
|
|
* 5. Get the results.
|
|
|
|
The result of a general Query can be assigned to a Result object:
|
|
|
|
Query<int> q(db, "select a,b from t where c=$1");
|
|
Result r = q(99);
|
|
|
|
The size of the result is available in the rows and cols members. Individual
|
|
elements can be accessed using the get() method, which takes a row number and
|
|
either a column number or a column name, and the expected type as a template
|
|
parameter. Type checking is performed; if you want to avoid this small overhead,
|
|
use get_nocheck(). There is also an is_null method:
|
|
|
|
string s = r.get<string>(0,0);
|
|
int a = r.get<int>(1,"b");
|
|
if (!r.is_null(2,0)) {
|
|
...
|
|
}
|
|
|
|
The result of a ColumnQuery is a ColumnResult. ColumnResult takes the result
|
|
type as a template parameter, and you access elements using ():
|
|
|
|
ColumnQuery<string> q(db, "select name from t");
|
|
ColumnResult<string> r = q();
|
|
string s = r(1);
|
|
|
|
It is also a standard-library-compatible random-access container:
|
|
|
|
typedef ColumnResult<string> col_t;
|
|
col_t r = q();
|
|
for ( col_t::const_iterator i = r.begin();
|
|
i != r.end(); ++i ) {
|
|
cout << *i << "\n";
|
|
}
|
|
|
|
SingletonResult is used for SingletonQueries. It is convertible to the result
|
|
type itself, so you don't normally need to declare it; the exception would be if
|
|
you needed tu use is_null() first.
|
|
|
|
SingletonQuery<int, name> count_things(db,"select sum(num) from things where name=$1");
|
|
int n = count_things("bed");
|
|
|
|
OptResult stores the result of an OptQuery. The presence of a value can be
|
|
tested by calling the empty() method.
|
|
|
|
|
|
Instalation / Compilation
|
|
-------------------------
|
|
|
|
pbe::Database is part of libpbe, and you can build the library just by running
|
|
"make" at the top level. But libpbe probably contains lots of stuff that you
|
|
don't want, so it may be easiest just to copy the Database files, and the few
|
|
other files that they depend on, into your own build environment.
|
|
|
|
There is one significant external dependency which is Boost. Version 1.33.1
|
|
works; I can't comment on other versions.
|
|
|
|
|
|
License
|
|
-------
|
|
|
|
This is all licensed under the GPL. I'm aware that this is different from
|
|
PostgreSQL itself; if you would like to see this use a different license, let me
|
|
know.
|
|
|
|
|
|
|