Documentation PostgreSQL 14.23 » Interfaces client » libpq -- Bibliothèque C » Exemples de programmes
Précédent Niveau supérieur Suivant
34.21. Construire des applications avec libpq Sommaire Chapitre 35. Large Objects

34.22. Exemples de programmes

Ces exemples (et d'autres) sont disponibles dans le répertoire src/test/examples de la distribution des sources.

Exemple 34.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, "%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 34.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, "%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 34.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;
 * SET standard_conforming_strings = ON;
 * 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, "%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;
}

AltStyle によって変換されたページ (->オリジナル) /