samedi 14 juin 2008

libpqxx - Un tutorial

Aucun doute, libpqxx est une bibliothèque très bien foutue. C'est un wrapper C++ autour de la libpq, qui est l'interface C de Postgres.

Cependant, j'ai trouvé que la documentation était trop orientée sur la description des interfaces, et trop peu sur l'utilisation en elle-même. Voici donc un petit topo sur les transactors.

Les transactors, ce sont des objets qui gèrent l'ensemble de la transaction, le support du rollback, et les essais multiples en cas de problème de connexion. Typiquement, un transactor qui fait simple SELECT pourrait ressembler à cela:


class SelectObjects : public pqxx::transactor<pqxx::transaction<> >
{
public:
SelectObjects(std::vector & objects):
m_objects(objects)
{
}

void operator()(argument_type & T)
{
std::ostringstream sql;
sql << "select name from objecttype";
pqxx::result result = T.exec(sql.str());

pqxx::result::const_iterator it = result.begin();
pqxx::result::const_iterator end = result.end();
for(; it != end; ++it)
{
m_objects.push_back(it->at(0).as());
}
}

private:
std::vector & m_objects;
};


A noter, la structure de données qui sera remplie est passée par référence. En effet, le transactor sera copié au moins une fois, et potentiellement plusieurs si la connexion est interrompue et que la commande doit être relancée.

L'on appellera le code ainsi:


// dbConn est une pqxx::connection valide
std::vector objects;
dbConn.perform(SelectObjects(objects));


L'on pourra ensuite facilement vérifier, à l'aide de notre ami Boost, que les données ont été récupérées:


BOOST_FOREACH(const std::string & obj, objects)
{
std::cout << obj << std::endl;
}


Allez, une autre pour la route: imaginons que l'on veuille insérer une valeur dans la table.


class InsertObject : public pqxx::transactor<pqxx::transaction<> >
{
public:
InsertObject(const std::string & value):
m_value(value)
{
}

void operator()(argument_type & T)
{
std::ostringstream sql;
sql << "insert into objecttype values "
<< "(5, '" << T.esc(m_value) << "', 0.7)";
T.exec(sql.str());
}

void on_commit()
{
std::cout << "Success!" << std::endl;
}

private:
std::string m_value;
};


L'on remarquera le T.esc() qui permet d'échapper la chaine en bon SQL. Tant qu'à faire, autant éviter l'injection! La méthode "on_commit" sera appelée automatiquement s'il n'y a pas eu de souci pendant la transaction.

Voilà, en espérant que ces quelques fragments soient utiles un jour!

1 commentaire:

Anonyme a dit…

Cet article m'a été très utile, merci beaucoup.