Ces exemples (et d'autres) sont disponibles dans le répertoire
src/test/examples de la distribution des sources.
Exemple 33.1. Premier exemple de programme pour libpq
/*
* src/test/examples/testlibpq.c
*
*
* testlibpq.c
*
* Teste la version C de libpq, la bibliothèque frontend de PostgreSQL.
*/
#include <stdio.h>
#include <stdlib.h>
#include "libpq-fe.h"
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
int nFields;
int i,
j;
/*
* Si l'utilisateur fournit un paramètre sur la ligne de commande,
* l'utiliser comme une chaîne conninfo ; sinon prendre par défaut
* dbname=postgres et utiliser les variables d'environnement ou les
* valeurs par défaut pour tous les autres paramètres de connexion.
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* Crée une connexion à la base de données */
conn = PQconnectdb(conninfo);
/* Vérifier que la connexion au backend a été faite avec succès */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* Initialise un search path sûr, pour qu'un utilisateur
malveillant ne puisse prendre le contrôle. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Il faut libérer PGresult avec PQclear dès que l'on en a plus besoin pour
* éviter les fuites de mémoire.
*/
PQclear(res);
/*
* Notre exemple inclut un curseur, pour lequel il faut que nous soyons dans
* un bloc de transaction. On pourrait tout faire dans un seul PQexec()
* d'un "select * from pg_database" mais c'est trop trivial pour faire
* un bon exemple.
*/
/* Démarre un bloc de transaction */
res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/*
* Récupère les lignes de pg_database, catalogue système des bases de
* données
*/
res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
res = PQexec(conn, "FETCH ALL in myportal");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/* affiche d'abord les noms des attributs */
nFields = PQnfields(res);
for (i = 0; i < nFields; i++)
printf("%-15s", PQfname(res, i));
printf("\n\n");
/* puis affiche les lignes */
for (i = 0; i < PQntuples(res); i++)
{
for (j = 0; j < nFields; j++)
printf("%-15s", PQgetvalue(res, i, j));
printf("\n");
}
PQclear(res);
/* ferme le portail... nous ne cherchons pas s'il y a des erreurs... */
res = PQexec(conn, "CLOSE myportal");
PQclear(res);
/* termine la transaction */
res = PQexec(conn, "END");
PQclear(res);
/* ferme la connexion à la base et nettoie */
PQfinish(conn);
return 0;
}
Exemple 33.2. Deuxième exemple de programme pour libpq
/*
* src/test/examples/testlibpq2.c
*
*
* testlibpq2.c
* Teste l'interface de notification asynchrone
*
* Démarrez ce programme, puis depuis psql dans une autre fenêtre faites
* NOTIFY TBL2;
* Répétez quatre fois pour terminer ce programme.
*
* Ou, si vous voulez vous faire plaisir, faites ceci :
* remplissez une base avec les commandes suivantes
* (issues de src/test/examples/testlibpq2.sql):
*
* CREATE SCHEMA TESTLIBPQ2;
* SET search_path = TESTLIBPQ2;
* CREATE TABLE TBL1 (i int4);
*
* CREATE TABLE TBL2 (i int4);
*
* CREATE RULE r1 AS ON INSERT TO TBL1 DO
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
*
* Démarrez ce programme, puis depuis psql faites quatre fois :
*
* INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "libpq-fe.h"
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
PGnotify *notify;
int nnotifies;
/*
* Si l'utilisateur fournit un paramètre sur la ligne de commande,
* l'utiliser comme une chaîne conninfo ; sinon prendre par défaut
* dbname=postgres et utiliser les variables d'environnement ou les
* pour tous les autres paramètres de connection.
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* Se connecte à la base */
conn = PQconnectdb(conninfo);
/* Vérifier que la connexion au backend a été faite avec succès */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* Initialise un search path sûr, pour qu'un utilisateur
malveillant ne puisse prendre le contrôle. */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Il faut libérer PGresult avec PQclear dès que l'on en a plus besoin pour
* éviter les fuites de mémoire.
*/
PQclear(res);
/*
* Lance une commande LISTEN pour démarrer des notifications depuis le
* NOTIFY.
*/
res = PQexec(conn, "LISTEN TBL2");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/* Quitte après avoir reçu quatre notifications. */
nnotifies = 0;
while (nnotifies < 4)
{
/*
* Dort jusqu'à ce que quelque chose arrive sur la connexion. Nous
* utilisons select(2) pour attendre une entrée, mais vous pouvez
* utiliser poll() ou des fonctions similaires.
*/
int sock;
fd_set input_mask;
sock = PQsocket(conn);
if (sock < 0)
break; /* ne devrait pas arriver */
FD_ZERO(&input_mask);
FD_SET(sock, &input_mask);
if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0)
{
fprintf(stderr, "select() failed: %s\n", strerror(errno));
exit_nicely(conn);
}
/* Cherche une entrée */
PQconsumeInput(conn);
while ((notify = PQnotifies(conn)) != NULL)
{
fprintf(stderr,
"ASYNC NOTIFY of '%s' received from backend PID %d\n",
notify->relname, notify->be_pid);
PQfreemem(notify);
nnotifies++;
PQconsumeInput(conn);
}
}
fprintf(stderr, "Done.\n");
/* ferme la connexion à la base et nettoie */
PQfinish(conn);
return 0;
}
Exemple 33.3. Troisième exemple de programme pour libpq
/*
* src/test/examples/testlibpq3.c
*
*
* testlibpq3.c
* Teste des paramètres délicats et des entrées-sorties binaires.
*
* Avant de lancer ceci, remplissez une base avec les commandes suivantes
* (fournies dans src/test/examples/testlibpq3.sql):
*
* CREATE SCHEMA testlibpq3;
* SET search_path = testlibpq3;
* CREATE TABLE test1 (i int4, t text, b bytea);
*
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
*
* La sortie attendue est :
*
* tuple 0: got
* i = (4 bytes) 1
* t = (11 bytes) 'joe's place'
* b = (5 bytes) \000\001\002\003\004
*
* tuple 0: got
* i = (4 bytes) 2
* t = (8 bytes) 'ho there'
* b = (5 bytes) \004\003\002\001\000
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include "libpq-fe.h"
/* for ntohl/htonl */
#include <netinet/in.h>
#include <arpa/inet.h>
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
/*
* Cette fonction affiche un résultat qui est la récupération
* au format binaire d'une table définie dans le commentaire ci-dessus.
* Nous l'avons extraite car la fonction main() l'utilise deux fois.
*/
static void
show_binary_results(PGresult *res)
{
int i,
j;
int i_fnum,
t_fnum,
b_fnum;
/*
* Utilise PQfnumber pour éviter de deviner l'ordre des champs
* dans le résultat
*/
i_fnum = PQfnumber(res, "i");
t_fnum = PQfnumber(res, "t");
b_fnum = PQfnumber(res, "b");
for (i = 0; i < PQntuples(res); i++)
{
char *iptr;
char *tptr;
char *bptr;
int blen;
int ival;
/*
* Récupère les valeurs des champs
* (on ignore la possibilité qu'ils soient NULL !)
*/
iptr = PQgetvalue(res, i, i_fnum);
tptr = PQgetvalue(res, i, t_fnum);
bptr = PQgetvalue(res, i, b_fnum);
/*
* La représentation binaire d'INT4 est dans l'ordre d'octets
* du réseau (network byte order), qu'il vaut mieux forcer à
* l'ordre local.
*/
ival = ntohl(*((uint32_t *) iptr));
/*
* La représentation binaire de TEXT est, hé bien, du texte,
* et puisque libpq a été assez sympa pour rajouter un octet zéro,
* cela marchera très bien en tant que chaîne C.
*
* La représentation binaire de BYTEA est un paquet d'octets,
* pouvant incorporer des nulls, donc nous devons faire attention à
* la longueur des champs.
*/
blen = PQgetlength(res, i, b_fnum);
printf("tuple %d: got\n", i);
printf(" i = (%d bytes) %d\n",
PQgetlength(res, i, i_fnum), ival);
printf(" t = (%d bytes) '%s'\n",
PQgetlength(res, i, t_fnum), tptr);
printf(" b = (%d bytes) ", blen);
for (j = 0; j < blen; j++)
printf("\\%03o", bptr[j]);
printf("\n\n");
}
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
const char *paramValues[1];
int paramLengths[1];
int paramFormats[1];
uint32_t binaryIntVal;
/*
* Si l'utilisateur fournit un paramètre sur la ligne de commande,
* l'utiliser comme une chaîne conninfo ; sinon prendre par défaut
* dbname=postgres et utiliser les variables d'environnement ou les
* valeurs par défaut pour tous les autres paramètres de connexion.
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* Crée une connexion à la base */
conn = PQconnectdb(conninfo);
/* Vérifie que la connexion à la base s'est bien déroulée */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/*
* Il faut libérer PGresult avec PQclear dès que l'on en a plus besoin pour
* éviter les fuites de mémoire.
*/
res = PQexec(conn, "SET search_path = testlibpq3");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/*
* Le sujet de ce programme est d'illustrer l'utilisation de PQexecParams()
* avec des paramètres délicats aussi bien que la transmission de
* données binaires.
*
* Ce premier exemple transmet les paramètres en tant que texte, mais
* reçoit les résultats en format binaire. Avec des paramètres
* un peu délicats il n'y a pas besoin de nettoyage fastidieux en
* terme de guillemets et d'échappement, même si les données sont du
* texte. Notez que nous ne faisons rien de spécial avec les guillemets
* dans la valeur du paramètre.
*/
/* Voici notre paramètre délicat */
paramValues[0] = "joe's place";
res = PQexecParams(conn,
"SELECT * FROM test1 WHERE t = $1",
1, /* un paramètre */
NULL, /* laissons le backend déduire le type */
paramValues,
NULL, /* pas besoin de la longueur des paramètres,
c'est du texte */
NULL, /* par défaut tous les paramètres sont du texte */
1); /* demande le résultat en binaire */
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
show_binary_results(res);
PQclear(res);
/*
* Dans ce second exemple, on transmet un paramètre entier sous
* forme binaire, et on récupère à nouveau les paramètres sous forme
* binaire.
*
* Bien que nous disions à PQexecParams que nous laissons le backend
* déduire le type du paramètre, nous forçons la décision en convertissant
* le symbole du paramètre dans le texte de la requête. C'est une bonne
* précaution quand on envoie des paramètres binaires.
*/
/* Convertit l'entier "2" dans l'ordre d'octets du réseau */
binaryIntVal = htonl((uint32_t) 2);
/* Met en place les tableaux de paramètres pour PQexecParams */
paramValues[0] = (char *) &binaryIntVal;
paramLengths[0] = sizeof(binaryIntVal);
paramFormats[0] = 1; /* binary */
res = PQexecParams(conn,
"SELECT * FROM test1 WHERE i = $1::int4",
1, /* un paramètre */
NULL, /* laissons le backend déduire le type
du paramètre */
paramValues,
paramLengths,
paramFormats,
1); /* demande des résultats binaires */
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
show_binary_results(res);
PQclear(res);
/* ferme la connexion et nettoie */
PQfinish(conn);
return 0;
}