PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 15.10 » Interfaces client » ECPG -- SQL embarqué en C » Gérer les Connexions à la Base de Données

36.2. Gérer les Connexions à la Base de Données

Si des utilisateurs pour lesquels nous n'avons pas confiance ont accès à une base de données qui n'a pas adopté une méthode sécurisée d'usage des schemas, commencez chaque session en supprimant les schémas modifiables par tout le monde du paramètre search_path. Par exemple, ajoutez options=-c search_path= à options ou exécutez EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); tout de suite après la connexion. Cette considération n'est pas spécifique à ECPG ; elle s'applique à chaque interface permettant d'exécuter des commandes SQL arbitraires.

Cette section explique comment ouvrir, fermer, et changer de connexion à la base.

36.2.1. Se Connecter au Serveur de Base de Données

On se connecte à la base de données avec l'ordre suivant:

EXEC SQL CONNECT TO cible [AS nom-connexion] [USER nom-utilisateur];
    

La cible peut être spécifiée des façons suivantes:

  • nomdb[@nomhôte][:port]
  • tcp:postgresql://nomhôte[:port][/nomdb][?options]
  • unix:postgresql://nomhôte[:port][/nomdb][?options]
  • une chaine SQL littérale contenant une des formes précédentes
  • une référence à une variable caractère contenant une des formes précédentes (voyez les exemples)
  • DEFAULT

La cible de connexion DEFAULT initie une connexion à la base de données par défaut avec l'utilisateur par défaut. Il n'est pas nécessaire de préciser séparément un nom d'utilisateur ou un nom de connexion dans ce cas.

Si vous spécifiez la chaîne de connexion de façon littérale (c'est-à-dire, pas par une chaîne littérale ou une réference à une variable), alors les constituants de la chaîne suivent les règles de grammaire de SQL normal ; par exemple, le hostname doit être similaire à un ou plusieurs identifiants SQL séparés par des points, et ces identifiants seront insensibles à la casse à moins d'être entre guillemets. Les valeurs de n'importe quelles options doivent être des identifiants SQL, des entiers, ou des références de variables. Bien sûr, vous pouvez mettre presque n'importe quoi dans un identifiant SQL en le plaçant entre guillemets. En pratique, il y a probablement moins de risque d'erreur d'utiliser une chaîne littérale (entre simples guillemets) ou une référence de variable que d'écrire la chaîne de connexion directement.

Il y a aussi plusieurs façons de spécifier le nom de l'utilisateur :

  • nomutilisateur
  • nomutilisateur/motdepasse
  • nomutilisateur IDENTIFIED BY motdepasse
  • nomutilisateur USING motdepasse

Comme précédemment, les paramètres nomutilisateur et motdepasse peuvent être un identifiant SQL, une chaîne SQL littérale, ou une référence à une variable caractère.

Si la cible de connexion inclut des options, cela consiste en des spécifications motclé=valeur séparées par des esperluettes (&). Les mots clés autorisés sont les mêmes que ceux reconnus par libpq (voir Section 34.1.2). Les espaces sont ignorés avant tout mot-clé ou valeur, mais pas à l'intérieur ou après. Notez qu'il n'existe pas de moyens d'écrire & à l'intérieur d'une valeur.

Notez qu'en spécifiant une connexion socket (avec le préfixe unix:), le nom d'hôte doit être exactement localhost. Pour choisir un répertoire de socket autre que celui par défaut, écrivez le chemin complet du répertoire comme valeur de l'option host dans la section options de la cible.

Le nom-connexion est utilisé pour gérer plusieurs connexions dans un programme. Il peut être omis si le programme n'utilise qu'une connexion. La connexion la plus récemment ouverte devient la connexion courante, qui est utilisée par défaut quand un ordre SQL doit être exécuté (voyez plus bas dans ce chapitre).

Voici quelques exemples d'ordres CONNECT:

EXEC SQL CONNECT TO mabase@sql.mondomaine.com;

EXEC SQL CONNECT TO tcp:postgresql://sql.mondomaine.com/mabase AS maconnexion USER john;

EXEC SQL BEGIN DECLARE SECTION;
const char *cible = "mabase@sql.mondomaine.com";
const char *utilisateur = "john";
const char *motdepasse = "secret";
EXEC SQL END DECLARE SECTION;
 ...
EXEC SQL CONNECT TO :cible USER :utilisateur USING :motdepasse;
/* or EXEC SQL CONNECT TO :cible USER :utilisateur/:motdepasse; */
    

Le dernier exemple utilise la fonctionnalité dont on parlait précédemment sous le nom de références de variable. Vous verrez dans les sections finales comment des variables C peuvent être utilisées dans des ordres SQL quand vous les préfixez par deux-points.

Notez que le format de la cible de connexion n'est pas spécifié dans le standard SQL. Par conséquent si vous voulez développer des applications portables, vous pourriez vouloir utiliser quelque chose ressemblant au dernier exemple pour encapsuler la cible de connexion quelque part.

Si des utilisateurs non fiables ont accès à une base de données qui n'a pas adopté un modèle d'utilisation de schémas sécurisés, alors commencez chaque session en retirant les schémas accessibles à l'écriture publique du search_path. Par exemple, ajoutez options=-c search_path= aux options, ou lancez EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); après la connexion. Cette considération n'est pas spécifique à ECPG ; elle s'applique à toute interface pour exécuter des commandes SQL arbitraires.

36.2.2. Choisir une connexion

Les ordres des programmes SQL embarqué sont par défaut exécutés dans la connexion courante, c'est à dire la plus récemment ouverte. Si une application a besoin de gérer plusieurs connexions, alors il y a trois façons de le gérer.

La première solution est de choisir explicitement une connexion pour chaque ordre SQL, par exemple:

EXEC SQL AT nom-connexion SELECT ...;
    

Cette option est particulièrement appropriée si l'application a besoin d'alterner les accès à plusieurs connexions.

Si votre application utilise plusieurs threads d'exécution, ils ne peuvent pas utiliser une connexion simultanément. Vous devez soit contrôler explicitement l'accès à la connexion (en utilisant des mutexes), ou utiliser une connexion pour chaque thread.

La seconde option est d'exécuter un ordre pour changer de connexion courante. Cet ordre est:

EXEC SQL SET CONNECTION nom-connexion;
    

Cette option est particulièrement pratique si de nombreux ordres doivent être exécutés sur la même connexion.

Voici un programme exemple qui gère plusieurs connexions à base de données:

#include <stdio.h>

EXEC SQL BEGIN DECLARE SECTION;
    char nomdb[1024];
EXEC SQL END DECLARE SECTION;

int
main()
{
    EXEC SQL CONNECT TO basetest1 AS con1 USER utilisateurtest;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
    EXEC SQL CONNECT TO basetest2 AS con2 USER utilisateurtest;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
    EXEC SQL CONNECT TO basetest3 AS con3 USER utilisateurtest;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;

    /* Cette requête serait exécuté dans la dernière base ouverte "basetest3". */
    EXEC SQL SELECT current_database() INTO :nomdb;
    printf("courante=%s (devrait être basetest3)\n", nomdb);

    /* Utiliser "AT" pour exécuter une requête dans "basetest2" */
    EXEC SQL AT con2 SELECT current_database() INTO :nomdb;
    printf("courante=%s (devrait être basetest2)\n", nomdb);

    /* Basculer la connexion courante à "basetest1". */
    EXEC SQL SET CONNECTION con1;

    EXEC SQL SELECT current_database() INTO :nomdb;
    printf("courante=%s (devrait être basetest1)\n", nomdb);

    EXEC SQL DISCONNECT ALL;
    return 0;
}

Cet exemple devrait produire cette sortie :

courante=basetest3 (devrait être basetest3)
courante=basetest2 (devrait être basetest2)
courante=basetest1 (devrait être basetest1)

La troisième option est de déclarer un identifiant SQL lié à la connexion, par exemple :

EXEC SQL AT connection-name DECLARE statement-name STATEMENT;
EXEC SQL PREPARE statement-name FROM :dyn-string;

Une fois que vous avez lié l'identifiant SQL à la connexion, vous exécutez le SQL dynamique sans clause AT. Notez que cette option se comporte comme les directives préprocesseurs, ainsi le lien n'est actif que dans le fichier.

Voici un exemple de programme utilisant cette option :

#include <stdio.h>

EXEC SQL BEGIN DECLARE SECTION;
char dbname[128];
char *dyn_sql = "SELECT current_database()";
EXEC SQL END DECLARE SECTION;

int main(){
  EXEC SQL CONNECT TO postgres AS con1;
  EXEC SQL CONNECT TO testdb AS con2;
  EXEC SQL AT con1 DECLARE stmt STATEMENT;
  EXEC SQL PREPARE stmt FROM :dyn_sql;
  EXEC SQL EXECUTE stmt INTO :dbname;
  printf("%s\n", dbname);

  EXEC SQL DISCONNECT ALL;
  return 0;
}

Cet exemple produira cette sortie, même si la connexion par défaut est testdb :

postgres
    

36.2.3. Fermer une Connexion

Pour fermer une connexion, utilisez l'ordre suivant :

EXEC SQL DISCONNECT [connexion];
    

La connexion peut être spécifiée des façons suivantes:

  • nom-connexion
  • CURRENT
  • ALL

Si aucun nom de connexion n'est spécifié, la connexion courante est fermée.

C'est une bonne pratique qu'une application ferme toujours explicitement toute connexion qu'elle a ouverte.