To work with libpqxx, you need to be aware of at least the following
classes. These are all in a dedicated namespace,
pqxx
:
connection
represents a connection from your
program to a PostgreSQL back-end server.
Naturally your program may open multiple connections, simultaneous or
not, to any number of databases.
work
, actually an alias for the
transaction
class template, represents a
transaction being executed in the context of a
connection
. This is a unit of work that must be
completed as a whole by the database backend. If execution fails,
everything you did within that transaction
is
"rolled back" as if it had never happened.
[1]
You may execute any number of transactions on a single
connection
, but only one at a time.
A result
is a container holding the resulting data
coming out of a query or command executed in a
transaction
. It behaves just like a standard C++
container as found in the STL, although you can't modify its
contents. (You can assign or copy it cheaply though; it's basically
a smart pointer). The rows in the result in turn behave like
non-modifiable containers as well.
I would also like to use this opportunity to plug the
esc
function, which you should use whenever you want
to include a variable as a string in your SQL (eg. insert it into a text
field in a table). See below.
There are other classes that may be of interest to you, but which you don't necessarily need to be aware of for writing programs. You will probably want to look them up at some point when it becomes necessary to figure out complex compiler warnings.
Some of the classes you may become interested in fairly quickly are:
broken_connection
is an exception type which
libpqxx throws if it loses its connection to the back-end.
sql_error
is an exception type which indicates
that a query did get to the back-end, but returned a failure.
in_doubt_error
is an exception class to indicate a
rare and difficult error condition. Explaining this is a bit
painful, since it sort of contradicts some of what we said above.
In theory, a "transaction" is an integral, atomic unit of work that either fails or succeeds. This is a massively useful and beautiful idea, but in practice there is nothing that cannot be broken. There is a tiny risk that, like Schroedinger's Cat, the transaction can end up in an indeterminate state. This happens when the connection to the backend is lost just when you were waiting for confirmation after completing your transaction. Your transaction may have succeeded, or it may have failed, or it may even still be running. Without a working connection, there is just no way for the server to tell the client code on your side!
When this happens, libpqxx throws an
in_doubt_error
on the client side, which you may
want to catch
as a special case. If you do, make
sure that all alarm go off. And whatever you do, do not retry the
transaction when this happens, because it may already have completed.
See also the description for robusttransaction
,
which sacrifices some performance to minimize this risk, as well as
transactors.
nontransaction
is just like a regular
transaction
as far your code is concerned (except
that it's not a template). The big difference is that where a
transaction
opens a back-end transaction to keep
your view and modifications of the database atomic, a
nontransaction
does nothing
to maintain integrity. It just passes your queries directly to the
database, with no enclosing transaction.
The other transaction classes are derived from
dbtransaction
, an abstract class that expresses
the presence of a real backend transaction. But
nontransaction
is not, since it does not open
"a transaction on the DB."
A nontransaction
may give you better performance
if your transaction does not modify the database. But if it does
make changes in the database, it may have unforeseen implications
if you expect certain levels of consistency in the database --
especially if other processes or threads may be modifying the
database at the same time. And, in cases where you do make changes
to the database, a regular transaction is often faster because some
work on the server needs to be done only once, rather than once per
statement.
You'll probably only want to use nontransaction
in the following cases:
If you only want to read from the database, not modify it, and you know that the data is not going to be updated while your program is running.
If you are interested in always getting the very latest information out of your database, even if that means that you may not get a temporally consistent view of the database.
When making changes to the database schema; some operations may not be supported by the backend inside backend transactions.
robusttransaction
(a template just like
transaction
) is essentially the same as a
transaction
, ie. an atomic unit of work. The
difference is that it works harder to prevent
in_doubt_error
s from happening. This comes at the
cost of some performance, which is one reason why this behaviour
isn't the default. And since it's a little more complicated than a
regular transaction, it could actually fail more
than a normal transaction. It just tries to give you more certainty
about whether it failed.
Use this class instead of plain old transaction
if
you get in_doubt_error
s in your application -- but
only after thoroughly inspecting your code for
bugs that might be causing the problem, or you'll only be hiding
glitches that should be fixed instead.
transaction_base
defines the common public
interface for the transaction
-like classes:
nontransaction
,
robusttransaction
, and
transaction
itself. To look up methods available
in these classes, see the definition (or documentation) for
transaction_base
instead.
You'll usually want to refer to the default
transaction
type as work
, an
alias that means "transaction
with default
(read-committed) isolation level."
transactor<>
provides a framework that makes
it easier for you to write correct, robust code to be executed in a
transaction. You write your transaction code as something that can
be called like a function: a lambda, a regular function, or an object
with a operator()
(also known as a functor).
The code which you put inside your transactor starts a transaction, does what it needs to do, then commits the transaction. It may also open its own connection first.
You pass your transactor to a perform
function,
which executes it. Pretty straightforward, and not much different
from just calling your code yourself. The difference is in what
happens if there's an error while executing. If the error is of a
transient kind, e.g. because of a deadlock or a failed connection,
the perform
function calls your transactor again.
This does mean that you must write your transactor as a transaction "on both sides." It's not enough to encapsulate its interaction with the database in a transaction. You must also make sure that it does not change your application's state on the client side until the transaction commits. It is this structure, not the framework code, which makes transactors useful. It's what lets libpqxx ensure that your transaction either completes, exactly once, or does nothing at all.
notification_receiver
is an instance of the
Observer design pattern. Any code connected to a backend may flag an
event using the PostgreSQL NOTIFY
command.
Frontends connected to that same backend database may be listening
for an event of that name to occur.
A frontend using libpqxx does this by registering an observer object
derived from notification_receiver
, which libpqxx
will invoke to handle the event when it occurs.
Something every programmer using notification receivers in PostgreSQL should know, by the way, is that notifications are not delivered to your program while it is inside a back-end transaction. Nor are they sent until the notifying process has committed any transaction it may be in. This makes sense from a correctness point of view (otherwise your processing of the event might get rolled back just because the transaction you're in happens to fail), but may be a little confusing to the unaware programmer.
row
refers to a row of data within a
result
object. It acts as a container of
field
s.
field
refers to one field within a
row
. A field
contains one
retrieved value such as a number or a string, and can be read into
variables of various types such as int
s or
string
s.
Finally, there are also some functions that you may want to know about,
that live alone in the pqxx
namespace without being
part of any class:
esc
, found in the connection and transaction
classes, is the canonical way to use strings in your SQL. This
function escapes any quotes, backslashes, and other characters in
your input string that would otherwise cause syntax errors (or worse,
security holes) in your queries when included directly.
Use these functions whenever you want to have a variable string in your SQL queries. If you don't, you'll be left with annoying bugs or possibly even security holes in your program. Use this function, use this function, use this function. Please. Make me proud.
to_string
knows how to render many types of
values as strings. The libpqxx code uses this internally to convert
values being sent to the backend. The function does not localise its
output.
from_string
is the inverse of
to_string
. Functions such as
field::to()
use this to convert the incoming
field data from the postgres text format to a C++ type in your client
application.
[1]
Actually libpqxx provides three classes doing this at various
levels of reliability, called nontransaction
,
transaction
, and
robusttransaction
for different levels of
reliability: respectively none, standard, and best.