Documentation PostgreSQL 18.4 » Interfaces client » libpq -- Bibliothèque C » Exemples de programmes

Précédent Niveau supérieur Suivant
32.22. Construire des applications avec libpq Sommaire Chapitre 33. Large Objects

32.23. Exemples de programmes #

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

Exemple 32.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 32.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/select.h>
#include <sys/time.h>
#include <sys/types.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;
 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 32.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 によって変換されたページ (->オリジナル) /