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; }