Table des matières
Si vous n'avez pas encore installé postgresql, vous pouvez le faire en tapant les commandes suivantes:
$sudo urpmi postgresql
$sudo urpmi postgresql-devel
Urpmi durant cette installation a créé un utilisateur: postgres. Il est souhaitable de lui changer son mot de passe :
en tant que root tapez passwd postgres et fournissez le futur mot de passe de postgres.
Il faut ensuite installer la librairie libpq++.
Elle n'est pas fournie avec la distribution mandrake classique, par contre elle est fournie en contribution libre. Le plus simple pour cela est de rajouter la source de média logiciel "contrib" aux sources de vos médias logiciels.
comment s'y prendre ?
Lancer le gestionnaire de source de médias logiciels, ajouter une source de donnée avec comme adresse par exemple: ftp://ftp.club-internet.fr/pub/linux/Mandrake/current/i586/media/contrib/ et comme chemin relatif pour hdlist.cz : ./media_info/hdlist.cz
Il faut ensuite installer les paquets:
urpmi libpq++4 libpq++4-devel
.
Cette fois urpmi va les trouver les télécharger et les installer.
Certains fichiers .h qui viennent d'être installés sont à modifier:
Il est ainsi nécessaire de rajouter la ligne:
using namespace std; en début du fichier "pgconnection.h".
Le fichier pgconnection.h se trouve probablement dans le répertoire "/usr/include/pgsql/libpq++/".
Si vous ne le trouvez pas c'est que la libpq++4-devel n'a pas été installée.
Les .h contiennent aussi une directive de compilation DLLIMPORT qui ne passe pas à la compilation. Il faut donc l'enlever. Vim est votre ami et va se charger de cela tout seul:
$su
#cd /usr/include/pgsql/libpq++/
#vim -c "argdo %s/DLLIMPORT/ /g | wq" *.h
#exit
$
Explications: vous lancez vim sur tous les fichiers ".h" du répertoire courant en lui donnant deux commandes séparées par un"|".La première commande %s/DLLIMPORT/ /g remplace tous les DLLIMPORT du fichier par un espace. Et la deuxième commande: wq quitte aprés avoir sauvé.
Administrons Postgresql
Par défaut la sécurité de postgres est basée sur l'identification préalable d'un utilisateur dans le système. Nous souhaitons ici les identifier grâce à un mot de passe fourni à postgresql lors de la création de l'utilisateur. Il va donc nous falloir modifier le fichier pg_hba.conf.
#locate pg_hba.conf
Vous devriez obtenir le résultat suivant:
/usr/share/pgsql/pg_hba.conf.sample
/var/lib/pgsql/data/pg_hba.conf
Il faut maintenant l'éditer:
#vim /var/lib/pgsql/data/pg_hba.confModifications à opérer:
local all postgres ident sameuser #postgres lui n'a pas besoin de password s'il travaille sur sa base de donnée
local all all md5 #tous les utilisateurs locaux sont identifiés par un password md5
Il faut alors procéder à l'activation des changements:
#export PGDATA="/var/lib/pgsql/data"
#pg_ctl reload
Nous allons maintenant créer l'utilisateur "useressais" et sous cet utilisateur, nous créerons la base de donnée "essais".
Devenons postgres.
#su postgres
Créons l'utilisateur "useressai"
$createuser -d -P useressai
Création en tant que "useressai" de la base de données "essai"
$createdb -Uuseressai -P dbessai
Examinons maintenant le programme en c++ suivant:
fichier: essaisSql.cpp
/*
* * libpq++ programme de démo classe PgDatabase
* */
using namespace std;
#include <iostream>
#include <libpq++.h> //à consulter pour savoir ce que fait la classe
int main ()
{
PgDatabase maBase ("dbname=essai user=useressai password=p-5K3g9ù"); // connexion à la base
if (maBase.ConnectionBad ()) // La connexion a-t-elle échoué ?
{
cerr << "La connexion à la base a echoué." << endl
<< "Erreur: "<< maBase.ErrorMessage ()<< endl;
exit(1);//sortie avec le code d'erreur 1 du programme
}
else
{
string query="select * from bidon";
if (!maBase.ExecTuplesOk (query.c_str())) // on balance la requête et on attend le résultat
{
cerr<< "le SELECT a échoué"<<endl;
exit(2);//sortie avec le code d'erreur 2 du programme
}
else
{
for (int noTuple = 0; noTuple < maBase.Tuples (); noTuple++) // pour chaque ligne du résultat
{
cout<< maBase.GetValue (noTuple, 0)<< endl; // affichage du champ 0 de la ligne noTuple
}
return 0;
}
}//fin du else
}//fin du main
Pour compiler:
g++ -c psql.cpp -I/usr/include/pgsql/
le -I permet d'indiquer à g++ dans quel répertoire il peut trouver les entêtes (*.h) que l'on a inclus dans notre fichier source. La commande
sudo locate libpq++.hvous renseignera sur son emplacement.
Pour linker
g++ -o essais essaisSql.o -lpq++
l'option -l permet d'indiquer à g++ qu'il doit lier notre programme à la librairie partagée libpq++.so.
Attention pour la convention de nommage : classes et méthodes ont dans cette librairie leur première lettre en majuscule.
Le constructeur reçoit en paramètre la chaîne de connexion.
Lors de la construction, la connection au moteur tente de s'établir.
bool ConnectionBad(void)
renvoie l'état de la connexion
bool ExecTuplesOK(char * laRequete)
exécute la requête et renvoie le résultat de l'exécution.
int Tupples()
renvoie le nombre de lignes renvoyées par la requête
int Fields()
renvoie le nombre de colonnes renvoyées par la requête.
char* FieldName(int noChamp)
renvoie le nom du champ dont le numéro est passé en paramètre.
char * GetValue(noTuple,noChamp)
renvoie sous forme de vieille chaîne AZT (c) la valeur du champ indiqué dans la ligne indiquée.
D'autres méthodes sont disponibles
locate libpq++|grep docvous permet de touver la documentation de la classe. Ensuite un petit
konqueror /usr/share/doc/libpq++4-devel-4.0/libpq++.htmldevrait vous permettre de trouver votre bonheur pour apprendre par exemple à récupérer les messages d'erreurs du serveur postgresql.
Ecrire un programme qui lit des requêtes sql tapées par l'utilisateur et en affiche le résultat. Vous pouvez le faire indifféremment en mode console ou en mode graphique avec QT. Si vous le faîtes avec QT modifiez votre fichier qmake.conf (locate qmake.conf)de façon à rajouter le -lpq++ et le -I/usr/include/pgsql.
Ecrire un programme qui affiche le contenu de la table "films".
Vous avez remarqué que la classe PgDatabase utilise des vieux char * en paramètre. De plus, les valeurs issues de la base de donnée sont toutes fournies sous forme de char *. Il sera donc nécessaire d'opérer des conversions de type.Voici un programme qui réalise des conversions dont vous devez vous inspirer.
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
bool isDouble( const std::string & Str )
{
// créer un flux à partir de la chaîne donnée
std::istringstream iss( Str );
// créer un objet temporaire pour la conversion
double tmp;
// tenter la conversion et
// vérifier qu'il ne reste plus rien dans la chaîne
return ( iss >> tmp ) && ( iss.eof() );
}
bool isLong( const std::string & Str )
{
// créer un flux à partir de la chaîne donnée
std::istringstream iss( Str );
// créer un objet temporaire pour la conversion
long tmp;
// tenter la conversion et
// vérifier qu'il ne reste plus rien dans la chaîne
return ( iss >> tmp ) && ( iss.eof() );
}
long toLong(const std::string & Str )
{
if(isLong(Str))
{
// créer un flux à partir de la chaîne donnée
std::istringstream iss( Str );
// créer un objet temporaire pour la conversion
long tmp;
// faire la conversion et renvoyer un long
iss >> tmp;
return ( tmp);
}
else
return false;
}
double toDouble(const std::string & Str )
{
if(isDouble(Str))
{
// créer un flux à partir de la chaîne donnée
std::istringstream iss( Str );
// créer un objet temporaire pour la conversion
double tmp;
// faire la conversion et renvoyer le résultat
iss >> tmp;
return ( tmp);
}
else
return false;
}
int main()
{
string strChaineAParser;
long unEntier;
float unReel;
//transfo string vers entier
cout<<"Tapez votre entier : ";
getline(cin,strChaineAParser);
if(isLong(strChaineAParser))
{
unEntier=toLong(strChaineAParser);
cout<<"Vous avez tapé l'entier: "<<unEntier<<" 2 * "<<unEntier<<" ="<<2*unEntier<<endl;
}
//sinon
else
cout<<endl<<strChaineAParser<<" n'est pas un entier"<<endl;
//transfo string vers réel
cout<<"Tapez votre réel : ";
//je saisie une string
getline(cin,strChaineAParser);
//je prend ce qu'il y a dans le stringstream pour essayer de le mettre dans un réel
if(isDouble(strChaineAParser))
{
unReel=toDouble(strChaineAParser);
//si c'est un réel
cout<<"Vous avez tapé le réel: "<<unReel<<" 2.5 * "<<unReel<<" ="<<2.5*unReel<<endl;
}
//sinon
else
cout<<endl<<strChaineAParser<<" n'est pas un réel."<<endl;
//transfo string vers char *
//char* chaineAZT=(char*)malloc(strChaineAParser.length()+1);
//strcpy(chaineAZT,strChaineAParser.c_str());
cout<<endl<<strChaineAParser.c_str()<<endl;
//transfo char * vers string:
char * nom="dupond";
string strNom(nom);
cout<<strNom<<endl;
}
Essayons maintenant de développer un programme plus sérieux. Nous allons réfléchir grâce à cet exemple sur la viabilité des objets "métiers" en environnement multi-utilisateurs.
Nous povons nous appuyer sur une gestion de cd-théque.
Par définition, les instances des objets sont en mémoire. Les informations concernant les cds, leurs titres et les chanteurs correspondants sont dans la base de données.
Intuitivement, si nous avions à concevoir une classe chanteur, nous l'aurions créée avec un certain nombre de propriétés:
identifiant
nom
prénom
date de naissance
nom public
Cette façon de concevoir les classes va nous poser, comme nous allons le voir, plusieurs problèmes:
Premier souci, on ne peut pas forcément stocker toutes ces informations en mémoire.
Une fois que les informations sont en mémoire comment garantir qu'elles sont les mêmes que dans la base de données ?
Scénario catastrophe: Imaginons que plusieurs utilisateurs du programme l'exécutent en même temps. Il y a alors pas mal de versions de la cd-tèque qui sont stockées en mémoire, autant que de programmes en cours d'exécution.
Si un utilisateur modifie le titre d'un cd, cette modification n'apparaîtra pas chez les autres. Or en toute logique une modification effectuée chez l'un doit être répercutée chez l'autre.
Que de difficultés en vue...
Y a-t-il une solution ?
Bien sûr: Il suffit de ne stocker que les identifiants en mémoire et de travailler avec les informations de la base.
On prendra donc bien soin de ne pas créer les propriétés des objets. Mais alors comment connaître le prénom d'un chanteur?
Il suffit de créer un accesseur string prenom() qui grâce à une requête basée sur l'identifiant va chercher le prénom du chanteur dans la base de données.