PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 12.18 » Programmation serveur » Étendre SQL » Empaqueter des objets dans une extension

37.17. Empaqueter des objets dans une extension

Une extensions à PostgreSQL utile contient généralement plusieurs objets SQL. Par exemple, un nouveau type de données va nécessiter de nouvelles fonctions, de nouveaux opérateurs et probablement de nouvelles méthodes d'indexation. Il peut être utile de les grouper en un unique paquetage pour simplifier la gestion des bases de données. Avec PostgreSQL, ces paquetages sont appelés extension. Pour créer une extension, vous avez besoin au minimum d'un fichier de script qui contient les commandes SQL permettant de créer ses objets, et un fichier de contrôle qui rapporte quelques propriétés de base de cette extension. Si cette extension inclut du code C, elle sera aussi généralement accompagnée d'une bibliothèque dans lequel le code C aura été compilé. Une fois ces fichiers en votre possession, un simple appel à la commande CREATE EXTENSION vous permettra de charger ses objets dans la base de données.

Le principal avantage des extensions n'est toutefois pas de pouvoir de charger une grande quantité d'objets dans votre base de donnée. Les extensions permettent en effet surtout à PostgreSQL de comprendre que ces objets sont liés par cette extension. Vous pouvez par exemple supprimer tous ces objets avec une simple commande DROP EXTENSION. Il n'est ainsi pas nécessaire de maintenir un script de « désinstallation ». Plus utile encore, l'outil pg_dump saura reconnaître les objets appartenant à une extension et, plutôt que de les extraire individuellement, ajoutera simplement une commande CREATE EXTENSION à la sauvegarde. Ce mécanisme simplifie aussi la migration à une nouvelle version de l'extension qui peut contenir de nouveaux objets ou des objets différents de la version d'origine. Notez bien toutefois qu'il est nécessaire de disposer des fichiers de contrôles, de script, et autres pour permettre la restauration d'une telle sauvegarde dans une nouvelle base de donnée.

PostgreSQL ne vous laissera pas supprimer de manière individuelle les objets d'une extension sans supprimer l'extension tout entière. Aussi, bien que vous ayez la possibilité de modifier la définition d'un objet inclus dans une extension (par exemple via la commande CREATE OR REPLACE FUNCTION dans le cas d'une fonction), il faut garder en tête que cette modification ne sera pas sauvegardée par l'outil pg_dump. Une telle modification n'est en pratique raisonnable que si vous modifiez parallèlement le fichier de script de l'extension. Il existe toutefois des cas particuliers comme celui des tables qui contiennent des données de configuration (voir Section 37.17.3.) Dans les situations de production, il est généralement préférable de créer un script de mise à jour de l'extension pour réaliser les modifications sur les objets membres de l'extension.

Le script de l'extension peut mettre en place des droits sur les objets qui font partie de l'extension via les instructions GRANT et REVOKE. La configuration finale des droits pour chaque objet (si des droits sont à configurer) sera enregistrée dans le catalogue système pg_init_privs. Quand pg_dump est utilisé, la commande CREATE EXTENSION sera inclue dans la sauvegarde, suivi de la mise en place des instructions GRANT et REVOKE pour configurer les droits sur les objets, tels qu'ils étaient au moment où la sauvegarde a été faite.

PostgreSQL ne supporte pas l'exécution d'instructions CREATE POLICY et SECURITY LABEL par le script. Elles doivent être exécutées après la création de l'extension. Toutes les politiques RLS et les labels de sécurité placés sur les objets d'une extension seront inclus dans les sauvegardes créées par pg_dump.

Il existe aussi un mécanisme permettant de créer des scripts de mise à jour de la définition des objets SQL contenus dans une extension. Par exemple, si la version 1.1 d'une extension ajoute une fonction et change le corps d'une autre vis-à-vis de la version 1.0 d'origine, l'auteur de l'extension peut fournir un script de mise à jour qui effectue uniquement ces deux modifications. La commande ALTER EXTENSION UPDATE peut alors être utilisée pour appliquer ces changements et vérifier quelle version de l'extension est actuellement installée sur une base de donnée spécifiée.

Les catégories d'objets SQL qui peuvent être inclus dans une extension sont spécifiées dans la description de la commande ALTER EXTENSION. D'une manière générale, les objets qui sont communs à l'ensemble de la base ou du cluster, comme les bases de données, les rôles, les tablespaces ne peuvent être inclus dans une extension car une extension n'est référencée qu'à l'intérieur d'une base de donnée. À noter que rien n'empêche la création de fichier de script qui crée de tels objets, mais qu'ils ne seront alors pas considérés après leur création comme faisant partie de l'extension. À savoir en outre que bien que les tables puissent être incluses dans une extension, les objets annexes tels que les index ne sont pas automatiquement inclus dans l'extension et devront être explicitement mentionnés dans les fichiers de script.

Si un script d'extension créé n'importe quel objet temporaire (comme des tables temporaires), ces objets seront traités comme des membres de l'extension pour le reste de la session courante, mais seront automatiquement supprimés à la fin de la session, tout comme n'importe quel objet temporaire le serait également. C'est une exception à la règle que les objets membres d'une extension ne peuvent pas être supprimés sans supprimer l'intégralité de l'extension.

37.17.1. Fichiers des extensions

La commande CREATE EXTENSION repose sur un fichier de contrôle associé à chaque extension. Ce fichier doit avoir le même nom que l'extension suivi du suffixe .control, et doit être placé dans le sous-répertoire SHAREDIR/extension du répertoire d'installation. Il doit être accompagné d'au moins un fichier de script SQL dont le nom doit répondre à la syntaxe extension--version.sql (par exemple, foo--1.0.sql pour la version 1.0 de l'extension foo). Par défaut, les fichiers de script sont eux-aussi situés dans le répertoire SHAREDIR/extension. Le fichier de contrôle peut toutefois spécifier un répertoire différent pour chaque fichier de script.

Le format du fichier de contrôle d'une extension est le même que pour le fichier postgresql.conf, à savoir une liste d'affectation nom_paramètre = valeur avec un maximum d'une affectation par ligne. Les lignes vides et les commentaires introduits par # sont eux-aussi autorisés. Prenez garde à placer entre guillemets les valeurs qui ne sont ni des nombres ni des mots isolés.

Un fichier de contrôle peut définir les paramètres suivants :

directory (string)

Le répertoire qui inclut les scripts SQL de l'extension. Si un chemin relatif est spécifié, le sous-répertoire SHAREDIR du répertoire d'installation sera choisi comme base. Le comportement par défaut de ce paramètre revient à le définir tel que directory = 'extension'.

default_version (string)

La version par défaut de l'extension, qui sera installée si aucune version n'est spécifiée avec la commande CREATE EXTENSION. Ainsi, bien que ce paramètre puisse ne pas être précisé, il reste recommandé de le définir pour éviter que la commande CREATE EXTENSION ne provoque une erreur en l'absence de l'option VERSION.

comment (string)

Un commentaire de type chaîne de caractère au sujet de l'extension. Le commentaire est appliqué à la création de l'extension, mais pas pendant les mises à jour de cette extension (car cela pourrait écraser des commentaires ajoutés par l'utilisateur). Une alternative consiste à utiliser la commande COMMENT dans le script de l'extension.

encoding (string)

L'encodage des caractères utilisé par les fichiers de script. Ce paramètre doit être spécifié si les fichiers de script contiennent des caractères non ASCII. Le comportement par défaut en l'absence de ce paramètre consiste à utiliser l'encodage de la base de donnée.

module_pathname (string)

La valeur de ce paramètre sera utilisée pour toute référence à MODULE_PATHNAME dans les fichiers de script. Si ce paramètre n'est pas défini, la substitution ne sera pas effectuée. La valeur $libdir/nom_de_bibliothèque lui est usuellement attribuée et dans ce cas, MODULE_PATHNAME est utilisé dans la commande CREATE FUNCTION concernant les fonctions en langage C, de manière à ne pas mentionner « en dur » le nom de la bibliothèque partagée.

requires (string)

Une liste de noms d'extension dont dépend cette extension, comme par exemple requires = 'foo, bar'. Ces extensions doivent être installées avant que l'extension puisse être installée.

superuser (boolean)

Si ce paramètre est à true (il s'agit de la valeur par défaut), seuls les superutilisateurs pourront créer cet extension ou la mettre à jour. Si ce paramètre est à false, seuls les droits nécessaires seront requis pour installer ou mettre à jour l'extension.

relocatable (boolean)

Une extension est dite « déplaçable » (relocatable) s'il est possible de déplacer les objets qu'elle contient dans un schéma différent de celui attribué initialement par l'extension. La valeur par défaut est à false, ce qui signifie que l'extension n'est pas déplaçable. Voir Section 37.17.2 pour des informations complémentaires.

schema (string)

Ce paramètre ne peut être spécifié que pour les extensions non déplaçables. Il permet de forcer l'extension à charger ses objets dans le schéma spécifié et aucun autre. Le paramètre schema est uniquement consulté lors de la création initiale de l'extension, pas pendant ses mises à jour. Voir Section 37.17.2 pour plus d'informations.

En complément au fichier de contrôle extension.control, une extension peut disposer de fichiers de contrôle secondaires pour chaque version dont le nommage correspond à extension--version.control. Ces fichiers doivent se trouver dans le répertoire des fichiers de script de l'extension. Les fichiers de contrôle secondaires suivent le même format que le fichier de contrôle principal. Tout paramètre spécifié dans un fichier de contrôle secondaire surcharge la valeur spécifiée dans le fichier de contrôle principal concernant les installations ou mises à jour à la version considérée. Cependant, il n'est pas possible de spécifier les paramètres directory et default_version dans un fichier de contrôle secondaire.

Un fichier de script SQL d'une extension peut contenir toute commande SQL, à l'exception des commandes de contrôle de transaction (BEGIN, COMMIT, etc), et des commandes qui ne peuvent être exécutées au sein d'un bloc transactionnel (comme la commande VACUUM). Cette contrainte est liée au fait que les fichiers de script sont implicitement exécutés dans une transaction.

Les scripts SQL d'une extension peuvent aussi contenir des lignes commençant par \echo, qui seront ignorées (traitées comme des commentaires) par le mécanisme d'extension. Ceci est souvent utilisé pour renvoyer une erreur si le script est passé à psql plutôt qu'exécuter par CREATE EXTENSION (voir un script d'exemple dans Section 37.17.7). Sans cela, les utilisateurs pourraient charger accidentellement le contenu de l'extension sous la forme d'objets « autonomes » plutôt que faisant partie d'une extension, ce qui est assez pénible à corriger.

Bien que les fichiers de script puissent contenir n'importe quel caractère autorisé par l'encodage spécifié, les fichiers de contrôle ne peuvent contenir que des caractères ASCII non formatés. En effet, PostgreSQL ne peut pas déterminer l'encodage utilisé par les fichiers de contrôle. Dans la pratique, cela ne pose problème que dans le cas où vous voudriez utiliser des caractères non ASCII dans le commentaire de l'extension. Dans ce cas de figure, il est recommandé de ne pas utiliser le paramètre comment du fichier de contrôle pour définir ce commentaire, mais plutôt la commande COMMENT ON EXTENSION dans un fichier de script.

37.17.2. Possibilités concernant le déplacement des extensions

Les utilisateurs souhaitent souvent charger les objets d'une extension dans un schéma différent de celui imposé par l'auteur. Trois niveaux de déplacement sont supportés :

  • Une extension supportant complétement le déplacement peut être déplacé dans un autre schéma à tout moment, y compris après son chargement dans une base de donnée. Initialement, tous les objets de l'extension installée appartiennent à un premier schéma (excepté les objets qui n'appartiennent à aucun schéma comme les langages procéduraux). L'opération de déplacement peut alors être réalisée avec la commande ALTER EXTENSION SET SCHEMA, qui renomme automatiquement tous les objets de l'extension pour être intégrés dans le nouveau schéma. Le déplacement ne sera toutefois fonctionnel que si l'extension ne contient aucune référence de l'appartenance d'un de ses objets à un schéma. Dans ce cadre, il est alors possible de spécifier qu'une extension supporte complétement le déplacement en initialisant relocatable = true dans son fichier de contrôle.

  • Une extension peut être déplaçable durant l'installation et ne plus l'être par la suite. Un exemple courant est celui du fichier de script de l'extension qui doit référencer un schéma cible de manière explicite pour des fonctions SQL, par exemple en définissant la propriété search_path. Pour de telles extensions, il faut définir relocatable = false dans son fichier de contrôle, et utiliser @extschema@ pour référencer le schéma cible dans le fichier de script. Toutes les occurences de cette chaîne dans le fichier de script seront remplacées par le nom du schéma choisi avant son exécution. Le nom du schéma choisi peut être fixé par l'option SCHEMA de la commande CREATE EXTENSION.

  • Si l'extension ne permet pas du tout le déplacement, il faut définir relocatable = false dans le fichier de contrôle, mais aussi définir schema comme étant le nom du schéma cible. Cette précaution permettra d'empêcher l'usage de l'option SCHEMA de la commande CREATE EXTENSION, à moins que cette option ne référence la même valeur que celle spécifiée dans le fichier de contrôle. Ce choix est à priori nécessaire si l'extension contient des références à des noms de schéma qui ne peuvent être remplacés par @extschema@. À noter que même si son usage reste relativement limité dans ce cas de figure puisque le nom du schéma est alors fixé dans le fichier de contrôle, le mécanisme de substitution de @extschema@ reste toujours opérationnel.

Dans tous les cas, le fichier de script sera exécuté avec comme valeur de search_path le schéma cible. Cela signifie que la commande CREATE EXTENSION réalisera l'équivalent de la commande suivante :

SET LOCAL search_path TO @extschema@, pg_temp;
    

Cela permettra aux objets du fichier de script d'être créés dans le schéma cible. Le fichier de script peut toutefois modifier la valeur de search_path si nécessaire, mais cela n'est généralement pas le comportement souhaité. La variable search_path retrouvera sa valeur initiale à la fin de l'exécution de la commande CREATE EXTENSION.

Le schéma cible est déterminé par le paramètre schema dans le fichier de contrôle s'il est précisé, sinon par l'option SCHEMA de la commande CREATE EXTENSION si elle est spécifiée, sinon par le schéma de création par défaut actuel (le premier rencontré en suivant le chemin de recherche search_path de l'appelant). Quand le paramètre schema du fichier de contrôle est utilisé, le schéma cible sera créé s'il n'existe pas encore. Dans les autres cas, il devra exister au préalable.

Si des extensions requises sont définies par requires dans le fichier de contrôle, leur schéma cible est ajouté à la valeur initiale de search_path, d'après le schéma cible de la nouvelle extension. Cela permet à leurs objets d'être visibles dans le fichier de script de l'extension installée.

Pour des raisons de sécurité, pg_temp est ajouté automatiquement à la fin de search_path dans tous les cas.

Une extension peut contenir des objets répartis dans plusieurs schémas. Il est alors conseillé de regrouper dans un unique schéma l'ensemble des objets destinés à un usage externe à l'extension, qui sera alors le schéma cible de l'extension. Une telle organisation est compatible avec la définition par défaut de search_path pour la création d'extensions qui en seront dépendantes.

37.17.3. Tables de configuration des extensions

Certaines extensions incluent des tables de configuration, contenant des données qui peuvent être ajoutées ou changées par l'utilisateur après l'installation de l'extension. Normalement, si la table fait partie de l'extension, ni la définition de la table, ni son contenu ne sera sauvegardé par pg_dump. Mais ce comportement n'est pas celui attendu pour une table de configuration. Les données modifiées par un utilisateur nécessitent d'être sauvegardées, ou l'extension aura un comportement différent après rechargement.

Pour résoudre ce problème, un fichier de script d'extension peut marquer une table ou une séquence comme étant une relation de configuration, ce qui indiquera à pg_dump d'inclure le contenu de la table ou de la séquence (et non sa définition) dans la sauvegarde. Pour cela, il s'agit d'appeler la fonction pg_extension_config_dump(regclass, text) après avoir créé la table ou la séquence, par exemple

CREATE TABLE my_config (key text, value text);
CREATE SEQUENCE my_config_seq;

SELECT pg_catalog.pg_extension_config_dump('my_config', '');
SELECT pg_catalog.pg_extension_config_dump('my_config_seq', '');
    

Cette fonction permet de marquer autant de tables ou de séquences que nécessaire. Les séquences associées avec des colonnes de type serial ou bigserial peuvent être marquées ainsi.

Si le second argument de pg_extension_config_dump est une chaîne vide, le contenu entier de la table sera sauvegardé par l'application pg_dump. Cela n'est correct que si la table était initialement vide après l'installation du script. Si un mélange de données initiales et de données ajoutées par l'utilisateur est présent dans la table, le second argument de pg_extension_config_dump permet de spécifier une condition WHERE qui sélectionne les données à sauvegarder. Par exemple, vous pourriez faire

CREATE TABLE my_config (key text, value text, standard_entry boolean);

SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
    

et vous assurer que la valeur de standard_entry soit true uniquement lorsque les lignes ont été créées par le script de l'extension.

Pour les séquences, le deuxième argument de pg_extension_config_dump n'a pas d'effet.

Des situations plus compliquées, comme des données initiales qui peuvent être modifiées par l'utilisateur, peuvent être prises en charge en créant des triggers sur la table de configuration pour s'assurer que les lignes ont été marquées correctement.

Vous pouvez modifier la condition du filtre associé avec une table de configuration en appelant de nouveau pg_extension_config_dump. (Ceci serait typiquement utile dans un script de mise à jour d'extension.) La seule façon de marquer une table est de la dissocier de l'extension avec la commande ALTER EXTENSION ... DROP TABLE.

Notez que les relations de clés étrangères entre ces tables dicteront l'ordre dans lequel les tables seront sauvegardées par pg_dump. Plus spécifiquement, pg_dump tentera de sauvegarder en premier la table référencé, puis la table référante. Comme les relations de clés étrangères sont configurées lors du CREATE EXTENSION (avant que les données ne soient chargées dans les tables), les dépendances circulaires ne sont pas gérées. Quand des dépendances circulaires existent, les données seront toujours sauvegardées mais ne seront pas restaurables directement. Une intervention de l'utilisateur sera nécessaire.

Les séquences associées avec des colonnes de type serial ou bigserial doivent être directement marquées pour sauvegarder leur état. Marquer la relation parent n'est pas suffisant pour ça.

37.17.4. Mise à jour d'extension

Un des avantages du mécanisme d'extension est de proposer un moyen simple de gérer la mise à jour des commandes SQL qui définissent les objets de l'extension. Cela est rendu possible par l'association d'un nom ou d'un numéro de version à chaque nouvelle version du script d'installation de l'extension. En complément, si vous voulez qu'un utilisateur soit capable de mettre à jour sa base de données dynamiquement d'une version à une autre, vous pouvez fournir des scripts de mise à jour qui feront les modifications nécessaires. Les scripts de mise à jour ont un nom qui correspond au format extension--ancienne_version--nouvelle_version.sql (par exemple, foo--1.0--1.1.sql contient les commandes pour modifier la version 1.0 de l'extension foo en la version 1.1).

En admettant qu'un tel script de mise à jour soit disponible, la commande ALTER EXTENSION UPDATE mettra à jour une extension installée vers la nouvelle version spécifiée. Le script de mise à jour est exécuté dans le même environnement que celui que la commande CREATE EXTENSION fournit pour l'installation de scripts : en particulier, la variable search_path est définie de la même façon et tout nouvel objet créé par le script est automatiquement ajouté à l'extension. De plus, si le script choisit de supprimer des objets membres de l'extension, ils sont automatiquement dissociés de l'extension.

Si une extension a un fichier de contrôle secondaire, les paramètres de contrôle qui sont utilisés par un script de mise à jour sont ceux définis par le script de la version cible.

Le mécanisme de mise à jour peut être utilisé pour résoudre un cas particulier important : convertir une collection éparse d'objets en une extension. Avant que le mécanisme d'extension ne soit introduit à PostgreSQL (dans la version 9.1), de nombreuses personnes écrivaient des modules d'extension qui créaient simplement un assortiment d'objets non empaquetés. Etant donné une base de donnée existante contenant de tels objets, comment convertir ces objets en des extensions proprement empaquetées ? Les supprimer puis exécuter la commande CREATE EXTENSION est une première méthode, mais elle n'est pas envisageable lorsque les objets ont des dépendances (par exemple, s'il y a des colonnes de table dont le type de données appartient à une extension). Le moyen proposé pour résoudre ce problème est de créer une extension vide, d'utiliser la commande ALTER EXTENSION ADD pour lier chaque objet pré-existant à l'extension, et finalement créer les nouveaux objets présents dans la nouvelle extension mais absents de celle non empaquetée. La commande CREATE EXTENSION prend en charge cette fonction avec son option FROM old_version, qui permet de ne pas charger le script d'installation par défaut pour la version ciblée, mais celui nommé extension--old_version--target_version.sql. Le choix de la valeur de old_version relève de la responsabilité de l'auteur de l'extension, même si unpackaged est souvent rencontré. Il est aussi possible de multiplier les valeurs de old_version pour prendre en compte une mise à jour depuis différentes anciennes versions.

La commande ALTER EXTENSION peut exécuter des mises à jour en séquence pour réussir une mise à jour. Par exemple, si seuls les fichiers foo--1.0--1.1.sql et foo--1.1--2.0.sql sont disponibles, la commande ALTER EXTENSION les exécutera séquentiellement si une mise à jour vers la version 2.0 est demandée alors que la version 1.0 est installée.

PostgreSQL ne suppose rien au sujet des noms de version. Par exemple, il ne sait pas si 1.1 suit 1.0. Il effectue juste une correspondance entre les noms de version et suit un chemin qui nécessite d'appliquer le moins de fichier de script possible. Un nom de version peut en réalité être toute chaîne qui ne contiendrait pas -- ou qui ne commencerait ou ne finirait pas par -.

Il peut parfois être utile de fournir des scripts de retour en arrière, comme par exemple foo--1.1--1.0.sql pour autoriser d'inverser les modifications effectuées par la mise à jour en version 1.1. Si vous procédez ainsi, ayez conscience de la possibilité laissée à PostgreSQL d'exécuter un tel script de retour en arrière s'il permet d'atteindre la version cible d'une mise à jour en un nombre réduit d'étapes. La cause du risque se trouve dans les scripts de mise à jour optimisés permettant de passer plusieurs versions en un seul script. La longueur du chemin commençant par un retour en arrière suivi d'un script optimisé pourrait être inférieure à la longueur du chemin qui monterait de version une par une. Si le script de retour en arrière supprime un objet irremplaçable, les conséquences pourraient en être facheuses.

Pour vérifier que vous ne serez pas confronté à des chemins de mise à jour inattendus, utilisez cette commande :

SELECT * FROM pg_extension_update_paths('extension_name');
    

Cette commande permet d'afficher chaque paire de noms de version connues pour l'extension spécifiée, ainsi que le chemin de mise à jour qui serait suivi depuis la version de départ jusque la version cible, ou la valeur NULL si aucun chemin valable n'est disponible. Le chemin est affiché sous une forme textuelle avec des séparateurs --. Vous pouvez utiliser regexp_split_to_array(path,'--') si vous préférez le format tableau.

37.17.5. Installer des extensions en utilisant des scripts de mise à jour

Une extension qui a existé un certain temps existera probabement dans plusieurs version, pour lesquelles l'auteur devra écrire des scripts de mise à jour. Par exemple, si vous avez sorti une extension foo dans les versions 1.0, 1.1, et 1.2, il devrait exister les scripts de mise à jour foo--1.0--1.1.sql et foo--1.1--1.2.sql. Avant PostgreSQL 10, il était nécessaire de créer également de nouveaux fichiers de scripts foo--1.1.sql et foo--1.2.sql qui construisent directement les nouvelles versions de l'extension, ou sinon les nouvelles version ne pourraient pas être installées directement, mais uniquement en installant 1.0 puis en effectuant les mises à jour. C'était fastidieux et source de doublons, mais c'est maintenant inutile car CREATE EXTENSION peut suivre les chaînes de mise à jour automatiquement. Par exemple, si seuls les fichiers de script foo--1.0.sql, foo--1.0--1.1.sql, et foo--1.1--1.2.sql sont disponibles, alors une demande d'installation de la version 1.2 pourra être effectuée en lançant ces trois scripts les uns à la suite des autres. Le traitement est le même que si vous aviez d'abord installé 1.0 puis mis à jour vers 1.2. (Comme pour ALTER EXTENSION UPDATE, si de multiples chemins sont disponibles alors le plus court sera choisi.) Arranger les fichiers de script d'une extension de cette façon peut réduire la quantité nécessaire d'effort de maintenance à fournir pour produires de petites mises à jour.

Si vous utilisez des fichiers de contrôle secondaires (spécifique à la version) avec une extension maintenant de cette façon, gardez à l'esprit que chaque version nécessite un fichier de contrôle même s'il n'y a pas de script d'installation spécifique pour cette version, car ce fichier de contrôle déterminera comment une mise à jour implicite vers cette version est effectuée. Par exemple, si foo--1.0.control spécifie requires = 'bar' mais que l'autre fichier de contrôle de foo ne le spécifie pas, la dépendance sur l'extension bar sera supprimée lors de la mise à jour de 1.0 vers une autre version.

37.17.6. Considérations de sécurité pour les extensions

Les extensions largement distribuées devraient assumer peu sur la base qu'elles occupent. De ce fait, il est adéquat d'écrire des fonctions fournies par une extension dans un style sécurisé qui ne peut pas être compromis par des attaques basées sur le search_path.

Une extension qui dispose de la propriété superuser configurée à true doit aussi considérer les risques de sécurité pour les actions effectuées par ses scripts d'installation et de mise à jour. Il n'est pas particulièrement compliqué pour un utilisateur mal intentionné de créer des objets chevaux de Troie qui compromettront une exécution ultérieure d'un script d'extension mal écrit, permettant à son utilisateur de gagner les droits d'un superutilisateur.

Des conseils sur l'écriture de fonctions sécurisées sont donnés dans Section 37.17.6.1 ci-dessous, et d'autres conseils, sur l'écriture de scripts d'installation sécurisés, sont donnés dans Section 37.17.6.2.

37.17.6.1. Considérations de sécurité pour les fonctions d'extensions

Les fonctions en langage SQL et PL fournies par les extensions peuvent être l'objet d'attaques basées sur le search_path quand elles sont exécutées car l'analyse de ces fonctions survient lors de leur exécution et non pas lors de leur création.

La page de référence de CREATE FUNCTION contient des conseils sur la bonne écriture de fonctions SECURITY DEFINER. Il est conseillé d'appliquer ces techniques pour toute fonction fournie par une extension car la fonction pourrait être appelée par un utilisateur avec des droits importants.

Si vous ne pouvez pas configurer le search_path pour contenir seulement les schémas sécurisés, supposez que chaque nom non qualifié pourrait désigner un objet défini par un utilisateur mal intentionné. Faites attention aux requêtes qui pourraient dépendre implicitement d'un search_path ; par exemple, IN et CASE expression WHEN sélectionnent toujours un opérateur utilisant le chemin de recherche. À la place, utilisez OPERATOR(schema.=) ANY et CASE WHEN expression.

Une extension standard ne devrait généralement pas supposer qu'elle a été installée dans un schéma sécurisé, ce qui signifie que même les références à ses propres objets en qualifiant leur nom de celui du schéma ne sont pas entièrement sans risque. Par exemple, si l'extension a défini une fonction monschema.mafonction(bigint), alors un appel tel que monschema.mafonction(42) pourrait être capturée par une fonction hostile monschema.mafonction(integer). Faites attention que les types de données de la fonction et les paramètres de l'opérateur correspondent exactement aux types d'argument déclarés, en utilisant des conversions explicites si nécessaire.

37.17.6.2. Considérations de sécurité pour les scripts d'extension

Un script d'installation ou de mise à jour d'extension devrait être écrit pour se garder contre les attaques se basant sur le schéma, survenant lors de l'exécution du script. Si la référence d'un objet dans le script peut se faire en résolvant un autre objet que celui voulu par l'auteur de script, une compromission peut survenir immédiatement ou ultérieurement quand l'object mal défini est utilisé.

Les commandes DDL telles que CREATE FUNCTION et CREATE OPERATOR CLASS sont généralement sécurisées, mais il convient de faire attention à toute commande ayant une expression standard comme composant. Par exemple, CREATE VIEW nécessite d'être validé, comme une expression DEFAULT dans CREATE FUNCTION.

Quelque fois, un script d'extension peut avoir besoin d'exécuter un SQL, par exemple pour réaliser des ajustements sur le catalogue qui ne seraient pas possibles via une DDL. Faites bien attention d'exécuter de telles commandes avec un search_path sécurisé ; ne faites pas confiance au chemin fourni par CREATE/ALTER EXTENSION comme étant sécurisé. Une meilleure approche est de configurer temporairement search_path à 'pg_catalog, pg_temp' et d'insérer explicitement des références au schéma d'installation de l'expression si nécessaire. (Cette pratique peut être utile pour créer des vues.) Des exemples sont disponibles dans les modules contrib de la distribution des sources de PostgreSQL.

Les références entre extensions sont très difficiles à sécuriser complètement, en partie à cause de l'incertitude sur le schéma d'installation de l'autre extension. Ce risque est réduit si les deux extensions sont installées dans le même schéma parce que, dans ce cas, un objet hostile ne peut pas être placé avant l'extension référencée dans le search_path d'installation. Néanmoins, aucun mécanisme n'existe actuellement pour forcer cela.

37.17.7. Exemples d'extensions

Ci-après, un exemple complet d'une extension écrite uniquement en SQL, un type composite de deux éléments qui peut stocker n'importe quelle valeur dans chaque emplacement, qui sont nommés « k » et « v ». Les valeurs non textuelles sont automatiquement changées en texte avant stockage.

Le fichier de script pair--1.0.sql ressemble à ceci:

-- se plaint si le script est exécuté directement dans psql, plutôt que via CREATE EXTENSION
\echo Use "CREATE EXTENSION pair" to load this file. \quit

CREATE TYPE pair AS ( k text, v text );

CREATE pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';

CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, FUNCTION = pair);

-- "SET search_path" is easy to get right, but qualified names perform better.
CREATE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;

CREATE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
               $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';

    

Le fichier de contrôle pair.control ressemble à ceci:

# extension pair
comment = 'Un type de donnees representant un couple clef/valeur'
default_version = '1.0'
# n'est pas déplaçable à cause de l'utilisation de @extschema@
relocatable = false
    

Si vous avez besoin d'un fichier d'installation pour installer ces deux fichiers dans le bon répertoire, vous pouvez utiliser le fichier Makefile qui suit :

EXTENSION = pair
DATA = pair--1.0.sql

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
    

Ce fichier d'installation s'appuye sur PGXS, qui est décrit dans Section 37.18. La commande make install va installer les fichiers de contrôle et de script dans le répertoire adéquat tel qu'indiqué par pg_config.

Une fois les fichiers installés, utilisez la commande CREATE EXTENSION pour charger les objets dans une base de donnée.