<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Opensource on Carnet de découvertes</title><link>https://fljd.in/tags/opensource/</link><description>Recent content in Opensource on Carnet de découvertes</description><generator>Hugo -- gohugo.io</generator><language>fr</language><managingEditor>Florent Jardin</managingEditor><lastBuildDate>Wed, 08 Oct 2025 16:00:00 +0200</lastBuildDate><atom:link href="https://fljd.in/tags/opensource/index.xml" rel="self" type="application/rss+xml"/><item><title>Sauvegardes personnelles avec Restic</title><link>https://fljd.in/2025/10/08/sauvegardes-personnelles-avec-restic/</link><pubDate>Wed, 08 Oct 2025 16:00:00 +0200</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2025/10/08/sauvegardes-personnelles-avec-restic/</guid><description>&lt;p&gt;Ces derniers temps, j’ai mis fin à une histoire d’amour avec Debian qui aura
duré près de trois ans. Pas que je n’aime plus le système, oh non, je le préfère
toujours à sa cousine Ubuntu. Mais voilà, j’ai un attachement particulier à
Archlinux depuis, wouh, au moins les bancs de l’école d’ingénieur où nous
l’installions sur des machines virtuelles et où nous nous adonnions à la
pratique du &lt;a href="https://github.com/fosslife/awesome-ricing" target="_blank" rel="noopener"&gt;linux ricing&lt;/a&gt; pour passer le temps.&lt;/p&gt;
&lt;p&gt;Après une expérimentation de &lt;a href="https://manjaro.org/" target="_blank" rel="noopener"&gt;Manjaro i3&lt;/a&gt; entre 2019 et 2022 sur mon poste
professionnel, je me suis convaincu qu’il me fallait retourner à l’essentiel. Au
début du mois de septembre, j’ai réussi le portage de ma &lt;a href="https://gitlab.com/fljdin/dotfiles/-/tree/main/sway" target="_blank" rel="noopener"&gt;config Sway&lt;/a&gt; de
Debian Trixie vers Archlinux. L’occasion parfaite pour requestionner mes
pratiques en matière de sauvegarde de poste et de remplacer &lt;a href="https://fljd.in/2021/08/24/borg-ou-la-sauvegarde-facile/"&gt;BorgBackup&lt;/a&gt; par
Restic.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="prise-en-main-éclair"&gt;Prise en main éclair&lt;/h2&gt;
&lt;p&gt;J’ai lu plusieurs articles et avis au sujet de Restic. Je l’avais vite identifié
comme un outil équivalent à BorgBackup, mais je ne m’étais pas pressé de le
tester. Quelle erreur ! J’ai été déconcerté par la facilité d’utilisation, par
son interface en ligne de commande claire et complète, et par sa
&lt;a href="https://restic.readthedocs.io/en/stable/010_introduction.html" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt; de qualité.&lt;/p&gt;
&lt;p&gt;Pour avoir régulièrement vécu des dégâts sur des clés de mauvaise qualité avec
ma pratique exotique sur BorgBackup, je m’étais penché sur la possibilité de
sauvegarder vers un dossier distant avec SSH. J’ai du insisté deux bonnes heures
à l’époque sans parvenir à quoique ce soit avec Borg.&lt;/p&gt;
&lt;p&gt;Avec Restic, ce n’est pas moins de 13 types différents de dépôts supportés, dont
le &lt;a href="https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html#sftp" target="_blank" rel="noopener"&gt;SFTP&lt;/a&gt;. Il ne m’en fallait pas plus pour tourner le dos à son homologue. La
mise en place d’un dépôt est simplifiée à l’extrême :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="na"&gt;-x&lt;/span&gt; &lt;span class="nv"&gt;RESTIC_REPOSITORY&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sftp:&amp;lt;server&amp;gt;:&amp;lt;directory&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;restic&lt;/span&gt; init
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;restic&lt;/span&gt; backup ~ &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--dry-run&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--exclude-file&lt;/span&gt; ~/.config/restic/excludes.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Je ne suis pas en reste avec une gestion poussée des stratégies de rétention des
instantanés. Pour l’exemple, je maintiens une sauvegarde par semaine, pendant
trois mois.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;restic&lt;/span&gt; forget &lt;span class="na"&gt;--prune&lt;/span&gt; &lt;span class="na"&gt;--keep-within-weekly&lt;/span&gt; 3m
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La technique d’exploration d’un instantané est identique à Borg, avec un montage
&lt;a href="https://www.kernel.org/doc/html/latest/filesystems/fuse.html" target="_blank" rel="noopener"&gt;FUSE&lt;/a&gt; sous Linux. Une fois encore, la &lt;a href="https://restic.readthedocs.io/en/stable/050_restore.html#restore-using-mount" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt; est limpide à ce
sujet.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;sudo&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt; &lt;span class="na"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt; &lt;span class="na"&gt;-g&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt; &lt;span class="na"&gt;-d&lt;/span&gt; /mnt/restic
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;restic&lt;/span&gt; mount /mnt/restic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="planification-avec-systemd"&gt;Planification avec systemd&lt;/h2&gt;
&lt;p&gt;Je me suis prêté au même jeu qu’avec BorgBackup sur ma précédente installation
Debian. En lieu et place de &lt;code&gt;cron&lt;/code&gt;, je planifie mes sauvegardes avec un &lt;em&gt;timer&lt;/em&gt;
utilisateur pour déclencher une tâche deux fois par jour sur mes journées de
travail.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# ~/.config/systemd/user/restic.timer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Daily&lt;/span&gt; &lt;span class="nx"&gt;Restic&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;man&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;restic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;Fri&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;Fri&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La tâche consiste à appeler &lt;code&gt;restic&lt;/code&gt; avec mon fichier d’exclusion, suivi d’une
purge des vieux instantanés présents sur le dépôt.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# ~/.config/systemd/user/restic.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Restic&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;oneshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;restic&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="nx"&gt;--exclude-file&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;RESTIC_EXCLUDE_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ExecStartPost&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;restic&lt;/span&gt; &lt;span class="nx"&gt;forget&lt;/span&gt; &lt;span class="nx"&gt;--prune&lt;/span&gt; &lt;span class="nx"&gt;--keep-within-weekly&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pour la configuration, je déclare les variables dans un fichier d’environnement
pour que &lt;code&gt;restic&lt;/code&gt; sache identifier et déchiffrer correctement les instantanés.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ~/.config/environment.d/restic.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;RESTIC_REPOSITORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sftp:&amp;lt;server&amp;gt;:&amp;lt;directory&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;RESTIC_PASSWORD_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;$HOME/.config/restic/pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;RESTIC_EXCLUDE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;$HOME/.config/restic/excludes.txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pour armer la planification, il suffit de recharger &lt;code&gt;systemd&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;systemctl&lt;/span&gt; &lt;span class="na"&gt;--user&lt;/span&gt; daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En exportant les bonnes variables dans votre shell, la consultation des
instantanés se fait sobrement avec la commande suivante.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;restic&lt;/span&gt; snapshots
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;repository 3db74709 opened (version 2, compression level auto)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID Time Host Tags Paths Size
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;-----------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;f7bb4fea 2025-09-16 11:50:18 florent-dalibo /home/florent 4.309 GiB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;5a4dbf59 2025-09-19 09:30:01 florent-dalibo /home/florent 5.639 GiB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;1012f0ab 2025-09-27 19:28:38 florent-dalibo /home/florent 5.871 GiB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;629c2687 2025-10-03 09:30:00 florent-dalibo /home/florent 6.006 GiB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;98b32adb 2025-10-08 10:00:08 florent-dalibo /home/florent 4.677 GiB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;-----------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;5 snapshots
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Restic est un bon outil. Tellement bon, que la communauté Gnome l’utilise à
présent comme &lt;em&gt;backend&lt;/em&gt; pour l’interface Déjà Dup dans leur dernière version
49.0 sortie le &lt;a href="https://discourse.gnome.org/t/deja-dup-49-0-released/31441" target="_blank" rel="noopener"&gt;mois dernier&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pour ma part, tout se déroule bien depuis presque un mois. Il ne manque plus
qu’à sécuriser le serveur distant qui fait office de dépôt avec ses propres
sauvegardes ! Pour ce faire, j’envisage de mettre en place Restic sur la plupart
des répertoires d’importances de mon serveur multimédia, en gardant bien en
mémoire le principe 3-2-1 de la sauvegarde réussie.&lt;/p&gt;
&lt;p&gt;Prenez soin de vos sauvegardes.&lt;/p&gt;</description></item><item><title>Faire vivre une communauté</title><link>https://fljd.in/2024/07/30/faire-vivre-une-communaute/</link><pubDate>Tue, 30 Jul 2024 09:30:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2024/07/30/faire-vivre-une-communaute/</guid><description>&lt;p&gt;Le &lt;a href="https://pgday.fr/" target="_blank" rel="noopener"&gt;PG Day France&lt;/a&gt; s&amp;rsquo;est tenu les 11 et 12 juin derniers à Lille, ma ville natale.
Il s&amp;rsquo;agit de l&amp;rsquo;événement de la communauté française de PostgreSQL qui pose ses valises dans une
ville différente chaque année. L&amp;rsquo;occasion était trop belle pour moi et j&amp;rsquo;y ai rencontré de nombreuses
personnes venant de toute la France et de ses alentours, pour discuter de PostgreSQL au cours
de deux jours d&amp;rsquo;ateliers et de conférences.&lt;/p&gt;
&lt;p&gt;Pour cette édition, j&amp;rsquo;ai eu le plaisir de prendre la parole et de faire un retour d&amp;rsquo;expérience
sur l&amp;rsquo;animation du groupe Meetup local dont j&amp;rsquo;ai repris les rennes il y a maintenant quatre ans.
Dans cet article, je souhaite retranscrire les principaux points abordés lors de cette présentation,
en attendant que la vidéo de la conférence soit mise en ligne.&lt;/p&gt;
&lt;div class="message"&gt;Le support de ma présentation est &lt;a href="https://fljd.in/documents/pgdayfr-faire-vivre-une-communaute.pdf"&gt;disponible à cette adresse&lt;/a&gt;.&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="quest-ce-quun-pug-"&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;un PUG ?&lt;/h2&gt;
&lt;p&gt;Un « PUG » est un groupe d&amp;rsquo;utilisateur·rices de PostgreSQL, ou « PostgreSQL User Group » en anglais.
Réunis régulièrement pour échanger autour de PostgreSQL, les PUGs sont des lieux de rencontres
et d&amp;rsquo;apprentissage pour les personnes intéressées par ce système de gestion de base de données. Ces
communautés locales sont fréquentes, particulièrement pour les logiciels libres, et permettent de
fédérer les utilisateurs et utilisatrices autour d&amp;rsquo;un même outil.&lt;/p&gt;
&lt;p&gt;À Lille, nous avons la chance d&amp;rsquo;avoir un tissu économique très dynamique, avec de nombreuses communautés,
telles que le &lt;a href="https://www.meetup.com/fr-FR/chtijug/" target="_blank" rel="noopener"&gt;Ch&amp;rsquo;ti JUG&lt;/a&gt; (&lt;em&gt;Java User Group&lt;/em&gt;), le &lt;a href="https://www.meetup.com/GDG-Lille/" target="_blank" rel="noopener"&gt;GDG Lille&lt;/a&gt; (&lt;em&gt;Google Developer Group&lt;/em&gt;), &lt;a href="https://www.meetup.com/fr-FR/nord-agile/" target="_blank" rel="noopener"&gt;Nord Agile&lt;/a&gt;
ou encore &lt;a href="https://www.meetup.com/Software-Craftsmanship-Lille/" target="_blank" rel="noopener"&gt;Software Craft Lille&lt;/a&gt;. Quoi de plus naturel que de vouloir proposer à une poignée de
ce public des rencontres autour de PostgreSQL ?&lt;/p&gt;
&lt;p&gt;Une &lt;a href="https://www.postgresql.org/community/user-groups/" target="_blank" rel="noopener"&gt;page du projet&lt;/a&gt; PostgreSQL recense les différents PUGs à travers le monde, et il est possible
d&amp;rsquo;y trouver des groupes dans de nombreux pays, dont la France. À l&amp;rsquo;heure de la rédaction de ma conférence,
je dénombrais 62 groupes mondiaux, dont 5 en France : Paris, Lyon, Nantes (non référencée), Toulouse et Lille.&lt;/p&gt;
&lt;p&gt;La plateforme Meetup est souvent utilisée pour organiser les rencontres, et pour preuve, 46 PUGs y sont
affiliés. Il s&amp;rsquo;agit d&amp;rsquo;une commodité depuis plus de 10 ans, et la plupart des groupes français y sont inscrits.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2024-07-30-carte-de-france-des-pugs.png" alt="Carte de France des PUGs"&gt;&lt;/p&gt;
&lt;p&gt;La création d&amp;rsquo;un PUG reconnu par la communauté internationale est un processus simple, qui nécessite
de constituer un groupe d&amp;rsquo;organisation et de postuler à l&amp;rsquo;adresse e-mail &lt;code&gt;usergroups@postgresql.org&lt;/code&gt;.
Le groupe doit respecter un certain nombre de règles, prévues par la &lt;a href="https://www.postgresql.org/about/policies/user-groups/" target="_blank" rel="noopener"&gt;charte des PUGs&lt;/a&gt;, et doit
s&amp;rsquo;engager à respecter les valeurs de la communauté PostgreSQL. En substance, voici les points qu&amp;rsquo;il
faut retenir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le groupe doit être ouvert à tous et toutes, sans discrimination.&lt;/li&gt;
&lt;li&gt;Les rencontres doivent être proposées &lt;em&gt;a minima&lt;/em&gt; une fois tous les deux ans.&lt;/li&gt;
&lt;li&gt;Les rencontres ne doivent pas faire l&amp;rsquo;objet d&amp;rsquo;un accord de confidentialité (NDA).&lt;/li&gt;
&lt;li&gt;Les rencontres sont rattachées à la zone géographique du groupe.&lt;/li&gt;
&lt;li&gt;Une entreprise ne peut pas être représentée à 50% ou plus dans le comité d&amp;rsquo;organisation.&lt;/li&gt;
&lt;li&gt;La sélection des conférences est à la discrétion du comité d&amp;rsquo;organisation.&lt;/li&gt;
&lt;li&gt;Les entreprises peuvent promouvoir leurs produits et leurs services, si leurs activités facilitent
l&amp;rsquo;adoption de PostgreSQL et si les contenus présentés sont de nature technique.&lt;/li&gt;
&lt;li&gt;Le groupe doit divulguer le nom des sponsors, et peut les mentionner en introduction des rencontres.&lt;/li&gt;
&lt;li&gt;Le PUG doit adopter un code de conduite et peut utiliser &lt;a href="https://www.postgresql.org/about/policies/coc/fr/" target="_blank" rel="noopener"&gt;celui de la communauté PostgreSQL&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="genèse-du-groupe"&gt;Genèse du groupe&lt;/h2&gt;
&lt;p&gt;Le groupe Meetup PostgreSQL Lille a été fondé le 25 février 2016 sur la plateforme éponyme. À l&amp;rsquo;époque,
l&amp;rsquo;idée de reproduire le format des Meetup à Paris était partagée entre Guillaume Lelarge et Pierre Hilbert,
deux lillois qui se croisaient régulièrement aux événements. L&amp;rsquo;une des principales motivations était de
participer à la promotion du logiciel libre avec un partage de retours d&amp;rsquo;expérience pour inspirer les autres
acteurs locaux.&lt;/p&gt;
&lt;p&gt;À cette période, je fréquentais moi-même les événements organisés par le groupe Meetup Oracle Paris et Province,
et je suivais avec attention les conférences autour de PostgreSQL dans ma région. La première rencontre
du groupe PG Lille a eu lieu le &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/231446425/" target="_blank" rel="noopener"&gt;24 juin 2016&lt;/a&gt;, dans les locaux de Decathlon Campus (Villeneuve D&amp;rsquo;Ascq, 59).
J&amp;rsquo;ai été séduit par le format et l&amp;rsquo;ambiance décontractée, et j&amp;rsquo;en ai gardé un excellent souvenir.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2024-07-30-premier-meetup-pg-lille.png" alt="Premier Meetup PG Lille"&gt;&lt;/p&gt;
&lt;p&gt;Cependant, la régularité des rencontres n&amp;rsquo;a pas été au rendez-vous du projet. L&amp;rsquo;événement suivant fut planifié
plus d&amp;rsquo;une année plus tard, le 17 octobre 2017 et ce fut le seul auquel je n&amp;rsquo;ai pas pu participer. Les impératifs
professionnels de Guillaume et de Pierre ne leur ont pas permis de maintenir le rythme, et le groupe a été
laissé en sommeil pendant plus de trois années.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est en 2019 que j&amp;rsquo;ai pris la décision de reprendre le flambeau, en proposant à Pierre de me transmettre
les droits d&amp;rsquo;administration du groupe. Mon arrivée chez Dalibo en tant que consultant PostgreSQL m&amp;rsquo;a donné
l&amp;rsquo;opportunité de faire la rencontre d&amp;rsquo;experts passionnés, notamment Guillaume Lelarge et Stefan Fercot,
qui m&amp;rsquo;ont encouragé à reprendre le projet.&lt;/p&gt;
&lt;p&gt;Fort d&amp;rsquo;un réseau professionnel de qualité sur Twitter à l&amp;rsquo;époque, j&amp;rsquo;ai pu rapidement mobiliser une équipe
de bénévoles pour organiser le Meetup du &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/267319389/" target="_blank" rel="noopener"&gt;28 janvier 2020&lt;/a&gt;. J&amp;rsquo;ai pu compter sur le soutien de Stefan Fercot
(Dalibo), de Stéphane Definin (Think) et de Sébastien Freiss (SFEIR) pour faire de cette reprise un petit
succès.&lt;/p&gt;
&lt;p&gt;&amp;hellip; Malheureusement, en mars de cette même année, la pandémie de COVID-19 a contraint le groupe à suspendre
ses projets et je n&amp;rsquo;ai pas eu le courage de proposer des contenus en ligne, au grand dam des membres du groupe.
Pour l&amp;rsquo;anecdote, aucun autre groupe Meetup de France n&amp;rsquo;a été épargné par la crise sanitaire et les restrictions
en vigueur.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2024-07-30-frise-chronologique.png" alt="Frise chronologiques des Meetup"&gt;&lt;/p&gt;
&lt;p&gt;Une pause de deux années supplémentaires s&amp;rsquo;est imposée au groupe, avant que ne se stabilise la situation sanitaire.
En 2022, j&amp;rsquo;ai ainsi contacté Lætitia Avrot, une membre très active de la communauté française, pour relancer les
activités et retrouver une dynamique et un rythme pour ses membres. L&amp;rsquo;événement du &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/284819405/" target="_blank" rel="noopener"&gt;14 avril 2022&lt;/a&gt; fut un
soulagement pour moi, voyant que je pouvais toujours compter sur la participation de la communauté.&lt;/p&gt;
&lt;p&gt;À partir de cet instant, aucune nouvelle ombre n&amp;rsquo;est venue assombrir le tableau, et le groupe a pu reprendre
l&amp;rsquo;organisation régulière de rencontres, de deux à trois par ans. La communauté s&amp;rsquo;est élargie, et le comité
d&amp;rsquo;organisation s&amp;rsquo;est renforcé avec l&amp;rsquo;arrivée de Yoann La Cancellera en 2023, qui a permis notamment de déposer
une demande de reconnaissance au sein de la communauté internationale, le 24 février 2023.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-to-meetup"&gt;How-to Meetup&lt;/h2&gt;
&lt;p&gt;En guise de conclusion à ce retour d&amp;rsquo;expérience, j&amp;rsquo;ai voulu partager toutes les étapes nécessaires pour
organiser un Meetup, en appuyant sur les points cruciaux de réussites et les pièges à éviter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accueil&lt;/strong&gt; : trouver le lieu d&amp;rsquo;accueil de pour l&amp;rsquo;événement&lt;/p&gt;
&lt;p&gt;À l&amp;rsquo;image du PG Day France qui change de lieu chaque année, le groupe Meetup PG Lille a fait le
choix de varier les lieux d&amp;rsquo;accueil à chaque rencontre. Cela permet de découvrir de nouveaux espaces
et de stimuler la curiosité inter-communauté. Les entreprises locales sont souvent ravies de pouvoir
accueillir des événements techniques, et cela permet de renforcer les liens entre les acteurs locaux.&lt;/p&gt;
&lt;p&gt;La recherche d&amp;rsquo;un lieu est souvent la première étape de l&amp;rsquo;organisation d&amp;rsquo;un Meetup. Il est important
de trouver un espace qui puisse accueillir entre dix et quarante personnes, avec un accès en transports
en commun ou un parking à proximité. Pour cela, j&amp;rsquo;ai eu l&amp;rsquo;occasion d&amp;rsquo;user de plusieurs stratégies :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le bouche-à-oreille : demander à ses contacts professionnels s&amp;rsquo;ils connaissent des lieux d&amp;rsquo;accueil
potentiels&lt;/li&gt;
&lt;li&gt;Les réseaux sociaux : publier une annonce sur Twitter ou LinkedIn pour solliciter des propositions
spontanées ou des pistes à étudier&lt;/li&gt;
&lt;li&gt;La privatisation d&amp;rsquo;un espace : contacter les espaces de coworking ou les salles de réunion pour
obtenir un devis de location&lt;/li&gt;
&lt;li&gt;S&amp;rsquo;allier avec d&amp;rsquo;autres groupes : proposer un partenariat avec un autre groupe Meetup pour partager
les frais de location ou pour bénéficier d&amp;rsquo;un lieu d&amp;rsquo;accueil déjà identifié&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Intervenant·es&lt;/strong&gt; : trouver &lt;u&gt;deux&lt;/u&gt; présentations variées&lt;/p&gt;
&lt;p&gt;Dès le premier Meetup, Pierre et Guillaume ont fait le choix de proposer deux présentations techniques
pour varier les sujets et les formats. Cela permet de toucher un public plus large et de satisfaire
les attentes des membres du groupe. Cette formule a été reprise pour les rencontres suivantes, et a
été un succès à chaque fois.&lt;/p&gt;
&lt;p&gt;La recherche d&amp;rsquo;intervenant·es est un travail qui demande du temps et de la patience. Souvent, une
rencontre opportune ou une discussion informelle peut déboucher sur une proposition de présentation.
Le mieux est de se constituer un vivier de personnes ressources, qui pourront être sollicitées en cas
de besoin. Ce n&amp;rsquo;est pas toujours facile, c&amp;rsquo;est un travail de longue haleine, mais cela en vaut la peine.&lt;/p&gt;
&lt;p&gt;Discutez autours de vous, demandez à vos collègues, à vos amis, à vos contacts professionnels s&amp;rsquo;ils
ne sont pas intéressés pour partager leur expérience ou leur expertise, ou s&amp;rsquo;ils ne connaissent pas
quelqu&amp;rsquo;un qui pourrait être intéressé.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apéro&lt;/strong&gt; : prolonger la soirée avec un temps fort communautaire (&lt;em&gt;networking&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Selon moi, c&amp;rsquo;est l&amp;rsquo;étape &lt;strong&gt;la plus importante&lt;/strong&gt; de l&amp;rsquo;organisation d&amp;rsquo;un Meetup. (rires !)&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est le moment où les membres du groupe peuvent échanger, discuter, partager, et se rencontrer. C&amp;rsquo;est
un temps fort de la soirée qui me tient particulièrement à cœur, et qui est intimement lié à la réussite
de l&amp;rsquo;événement. Le &lt;em&gt;networking&lt;/em&gt; est ce qui favorise le mieux la sérendipité, la rencontre fortuite, le
partage d&amp;rsquo;expérience, et la création de liens durables. Je discute des prochains projets du groupe avec
les membres et le comité d&amp;rsquo;organisation, et je prends des notes sur les attentes de chacun·es et j&amp;rsquo;écoute
leurs suggestions.&lt;/p&gt;
&lt;p&gt;Bien entendu, il est important de prévoir des boissons et des encas pour prolonger la soirée. Il faut
prévoir un espace pour faciliter la circulation et la création naturelle de petits groupes de discussion.
Le choix d&amp;rsquo;un sponsor est principalement motivé par la prise en charge de ces frais, avec une facture ou
une note de frais à l&amp;rsquo;appui.&lt;/p&gt;
&lt;p&gt;Jusqu&amp;rsquo;à présent, aucun incident n&amp;rsquo;est à déplorer, les participants et participantes respectent les quelques
règles de sécurité et de conduite que l&amp;rsquo;on énonce en début de soirée.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt; : atteindre la jauge d&amp;rsquo;inscription&lt;/p&gt;
&lt;p&gt;Cette étape est à prendre au sérieux un mois avant la tenue de l&amp;rsquo;événement. Il s&amp;rsquo;agit de maximiser la
visibilité de l&amp;rsquo;événement pour atteindre la jauge maximale établie par le lieu qui accueille l&amp;rsquo;événement.
Pour cela, j&amp;rsquo;ai eu l&amp;rsquo;occasion de me doter de plusieurs méthodes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Être le plus exhaustif possible dans la description de l&amp;rsquo;événement (date, lieu, horaires, programme,
sponsors, visuels, etc.)&lt;/li&gt;
&lt;li&gt;Créer du contenu régulier sur les réseaux sociaux pour rappeler la date de l&amp;rsquo;événement et les modalités
d&amp;rsquo;inscription (LinkedIn majoritaire, Twitter par le passé)&lt;/li&gt;
&lt;li&gt;Solliciter les participants de l&amp;rsquo;événement pour qu&amp;rsquo;ils partagent l&amp;rsquo;événement sur leurs réseaux sociaux ou
leurs réseaux d&amp;rsquo;entreprise&lt;/li&gt;
&lt;li&gt;Informer la communauté française avec la liste de diffusion &lt;code&gt;pgsql-fr-generale&lt;/code&gt; pour toucher un public plus
large, voire donner envie à d&amp;rsquo;autres groupes de se lancer dans l&amp;rsquo;organisation de Meetup (qui sait ?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Identité visuelle&lt;/strong&gt; (optionnelle)&lt;/p&gt;
&lt;p&gt;Au cours des derniers mois, nous avons travaillé avec Yoann, la refonte de l&amp;rsquo;identité visuelle du groupe Meetup
avec un nouveau logo et une nouvelle charte graphique. C&amp;rsquo;est cosmétique, mais cela permet de donner une image
plus professionnelle et une identité forte au sein des communautés lilloises existantes. Nous espérons nous
faire une petite place et une petite notoriété dans le paysage local.&lt;/p&gt;
&lt;p&gt;Pour avoir délégué par deux fois la création de la vignette de l&amp;rsquo;événement, je recommande de travailler avec
un graphiste ou un designer pour obtenir un résultat professionnel et soigné. En attendant qu&amp;rsquo;un des membres
qui m&amp;rsquo;entoure soit suffisamment qualifié, on fait avec les moyens du bord.&lt;/p&gt;
&lt;p&gt;Enfin, et c&amp;rsquo;était notre petite fierté de l&amp;rsquo;année, nous nous sommes dotées d&amp;rsquo;un logo Slonik, la mascotte de
PostgreSQL. La démarche de création s&amp;rsquo;est appuyée énormément sur les nouveaux outils de génération par intelligences
artificielles, et nous avons été bluffés par la qualité du résultat. Un grand merci à &lt;a href="https://www.instagram.com/_ekpyrosis/" target="_blank" rel="noopener"&gt;Isaac&lt;/a&gt;
pour son aide précieuse dans les retouches et les ajustements du logo.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2024-07-30-logo-meetup-pg-lille.png" alt="Logo du groupe Meetup PG Lille"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="remerciements"&gt;Remerciements&lt;/h2&gt;
&lt;p&gt;Au cours de cette conférence, j&amp;rsquo;ai eu l&amp;rsquo;occasion de remercier un tas de monde. En particulier, les membres de
l&amp;rsquo;association PostgreSQL France, pour leur confiance qu&amp;rsquo;ils nous ont accordée, Matthieu Cornillon et moi-même,
d&amp;rsquo;avoir accepté notre dossier de candidature pour le choix de la ville de Lille cette année 2024.&lt;/p&gt;
&lt;p&gt;Un des slides de ma présentation était dédié à toutes les entreprises qui ont accueilli nos rencontres,
et qui, sans le savoir, m&amp;rsquo;ont permis d&amp;rsquo;ajuster le format et le contenu de nos Meetups. Un grand merci à elles
pour leur sympathie et leur accueil chaleureux. C&amp;rsquo;était également l&amp;rsquo;opportunité de mettre en avant les intervenants
et les intervenantes qui ont accepté de partager leur expérience et leur expertise lors des Meetup PostgreSQL Lille.
Un grand merci à elles et à eux d&amp;rsquo;avoir vécu l&amp;rsquo;aventure avec moi !&lt;/p&gt;
&lt;p&gt;Enfin, j&amp;rsquo;ai été heureux que ce retour d&amp;rsquo;expérience puisse faire écho à quelques personnes dans la salle, qui
m&amp;rsquo;ont félicité pour le travail accompli et m&amp;rsquo;ont informé de leur intention de se lancer dans l&amp;rsquo;organisation
de Meetup dans leur propre ville. C&amp;rsquo;est une belle récompense pour moi, et cela me donne envie de poursuivre
mon engagement et de donner l&amp;rsquo;exemple pour la promotion de PostgreSQL en France.&lt;/p&gt;</description></item><item><title>Construire PostgreSQL avec Meson</title><link>https://fljd.in/2022/09/29/construire-postgresql-avec-meson/</link><pubDate>Thu, 29 Sep 2022 00:00:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2022/09/29/construire-postgresql-avec-meson/</guid><description>&lt;p&gt;Alors que la version 15 de PostgreSQL se prépare à sortir dans les &lt;a href="https://www.postgresql.org/about/news/postgresql-15-rc-1-released-2516/" target="_blank" rel="noopener"&gt;prochains
jours&lt;/a&gt;, le groupe de développement du projet communautaire ont intégré &lt;a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=e6927270cd18d535b77cbe79c55c6584351524be" target="_blank" rel="noopener"&gt;leurs
récents travaux&lt;/a&gt; pour accélérer les tâches d&amp;rsquo;automatisation et de compilation
à l&amp;rsquo;aide du système de construction &lt;a href="https://mesonbuild.com/" target="_blank" rel="noopener"&gt;Meson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ce chantier n&amp;rsquo;est pas anodin et redessine les contours de l&amp;rsquo;écosystème du moteur
de bases de données open-source le plus avancé au monde. Depuis sa forme libre
publiée en 1998, PostgreSQL repose sur des solutions robustes et éprouvées, mais
de plus en plus complexes à maintenir pour les nouvelles générations de
contributeur·rices. En proposant de se tourner vers un logiciel comme Meson, ces
amoureux et amoureuses du libre se tournent résolument vers l&amp;rsquo;avenir.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-andres-freund-e692323.png" alt="patch"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="en-finir-avec-autoconf"&gt;En finir avec autoconf&lt;/h2&gt;
&lt;p&gt;Un système de construction ou &lt;em&gt;build system&lt;/em&gt; (ne vous y trompez pas, j&amp;rsquo;ai une
préférence pour la dénomination anglaise) est un ensemble d&amp;rsquo;instructions dans
une syntaxe qui lui est propre, qui facilite la compilation d&amp;rsquo;un logiciel. Les
ramifications d&amp;rsquo;un projet, les dépendances et les librairies ou tout simplement
l&amp;rsquo;outillage interne, deviennent inexorablement la rançon d&amp;rsquo;une complexité après
plusieurs décennies d&amp;rsquo;existence.&lt;/p&gt;
&lt;p&gt;Le système le plus répandu est sans conteste &lt;a href="https://en.wikipedia.org/wiki/Make_%28software%29" target="_blank" rel="noopener"&gt;Make&lt;/a&gt; et son fichier déclaratif
&lt;em&gt;Makefile&lt;/em&gt; qui contiendra les instructions de compilation. Son principe absolu
consiste à transformer un fichier source (le code) en un autre fichier cible (le
binaire). Dans un projet minimaliste, le &lt;em&gt;Makefile&lt;/em&gt; suivant permet de générer le
binaire &lt;code&gt;foo&lt;/code&gt; si le code source &lt;code&gt;foo.c&lt;/code&gt; ou son en-tête &lt;code&gt;foo.h&lt;/code&gt; contiennent des
nouveautés.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-make" data-lang="make"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# ./Makefile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-I.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;.&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;.&lt;span class="n"&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;$(&lt;/span&gt;CC&lt;span class="k"&gt;)&lt;/span&gt; -o foo foo.c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;À l&amp;rsquo;appel de la commande &lt;code&gt;make&lt;/code&gt; à la racine du projet, le fichier &lt;em&gt;Makefile&lt;/em&gt;
sera parcouru pour détecter les cibles du projet et suivre les instructions
selon les règles qui y sont renseignées pour construire les fichiers binaires.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-makefile-workflow.png" alt="Compilation par Makefile"&gt;&lt;/p&gt;
&lt;p&gt;Une &lt;a href="https://en.wikipedia.org/wiki/List_of_build_automation_software#Build_script_generation" target="_blank" rel="noopener"&gt;pratique plus sophistiquée&lt;/a&gt; propose de générer ces instructions dans un
format compatible avec le système de construction, lorsque celles-ci sont
nombreuses, évolutives voire dépendantes d&amp;rsquo;un contexte ou d&amp;rsquo;un environnement tel
que le système d&amp;rsquo;exploitation ou l&amp;rsquo;utilisation d&amp;rsquo;une option spécifique.&lt;/p&gt;
&lt;p&gt;Dans le cas du projet PostgreSQL, c&amp;rsquo;est la suite &lt;a href="https://en.wikipedia.org/wiki/GNU_Autotools" target="_blank" rel="noopener"&gt;GNU Autotools&lt;/a&gt; qui a été
partiellement retenue pour faciliter la création des binaires sur un ensemble de
systèmes compatibles Unix. La génération repose sur les composants &lt;em&gt;Autoconf&lt;/em&gt;
(fichier &lt;code&gt;configure.ac&lt;/code&gt;) et &lt;em&gt;Automake&lt;/em&gt; (fichier &lt;code&gt;Makefile.am&lt;/code&gt;) pour aboutir au
même résultat que la commande &lt;code&gt;make&lt;/code&gt; de notre précédent exemple. Dans les faits,
seul le premier composant est véritablement employé pour préparer le script
&lt;code&gt;configure&lt;/code&gt; lors de la compilation de PostgreSQL.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-autotools-workflow.png" alt="Génération avec Autotools"&gt;&lt;/p&gt;
&lt;p&gt;Comme le montre le diagramme ci-dessus, les étapes avant d&amp;rsquo;obtenir le fichier
binaire sont un peu plus nombreuses, et dépendent d&amp;rsquo;un ensemble de fichiers
d&amp;rsquo;instructions qui deviennent complexes à rédiger sans introduire d&amp;rsquo;incohérences
ou de bogues.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs l&amp;rsquo;un des constats de la communauté qui, depuis fin 2021, a
questionné la possibilité de passer sur un autre système de construction.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Autoconf is showing its age, fewer and fewer contributors know how to wrangle
it. Recursive make has a lot of hard to resolve dependency issues and slow
incremental rebuilds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Le principal moteur de la réflexion, Andres Freund, annonçait dans un
&lt;a href="https://www.postgresql.org/message-id/20211012083721.hvixq4pnh2pixr3j%40alap3.anarazel.de" target="_blank" rel="noopener"&gt;message&lt;/a&gt; sur &lt;em&gt;pgsql-hackers&lt;/em&gt; qu&amp;rsquo;il observait de bien meilleures performances
avec une alternative bien plus moderne. Il y énonçait par ailleurs ses arguments
pour en finir avec &lt;em&gt;Autoconf&lt;/em&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;« &lt;em&gt;Autoconf&lt;/em&gt; et &lt;em&gt;make&lt;/em&gt; ne sont plus activement maintenus. Notamment &lt;em&gt;autoconf&lt;/em&gt;
qui reçoit à peine quelques correctifs mineurs. C&amp;rsquo;est également des
technologies que peu de monde veut utiliser &amp;ndash; m4 d&amp;rsquo;autoconf est effrayant et
effraie les personnes qui démarrent bien plus récemment que nous autres, les
&lt;em&gt;committers&lt;/em&gt; » ;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;« &lt;em&gt;make&lt;/em&gt; en mode récursif comme nous l&amp;rsquo;utilisons n&amp;rsquo;est pas aussi bien employé
que ce qu&amp;rsquo;il devrait être. L&amp;rsquo;une des raisons pour laquelle le nettoyage du
&lt;em&gt;build&lt;/em&gt; est si lent est que nous devons retrouver les dépendances dans un
paquet d&amp;rsquo;endroits. En malgré cela, il m&amp;rsquo;arrive régulièrement de voir des
&lt;em&gt;builds&lt;/em&gt; incrémentaux échouer et nécessitant un nouveau &lt;em&gt;rebuild&lt;/em&gt; » ;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;« Et nous n&amp;rsquo;avons pas uniquement un système de &lt;em&gt;build&lt;/em&gt; basé sur &lt;em&gt;autoconf&lt;/em&gt; et
&lt;em&gt;make&lt;/em&gt;, il y a surtout le projet de génération MSVC (&lt;em&gt;Microsoft Visual C++&lt;/em&gt;)
&amp;ndash; ce machin que la plupart d&amp;rsquo;entre nous ne veut pas toucher. Je pense qu&amp;rsquo;en
plus du fait qu&amp;rsquo;il n&amp;rsquo;y est pas facile de dérouler tous les tests, ce système
est juste tout simplement différent de l&amp;rsquo;autre, ce qui ne favorise pas l&amp;rsquo;intérêt
des développeurs sous Windows (et indirectement, la qualité de PostgreSQL sur
Windows) » ;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;« Le dernier gros problème que je vois avec la situation actuelle est qu&amp;rsquo;il
n&amp;rsquo;y a aucun bon test d&amp;rsquo;intégration. Le résultat de &lt;code&gt;make check-world&lt;/code&gt; est très
majoritairement illisible et impossible à analyser automatiquement. Ce qui
impose à la &lt;em&gt;&lt;a href="https://buildfarm.postgresql.org/cgi-bin/show_status.pl" target="_blank" rel="noopener"&gt;buildfarm&lt;/a&gt;&lt;/em&gt; de traiter les tests séparément afin que les
erreurs puissent être repérées et tracées correctement. Cette approche n&amp;rsquo;est
malheureusement pas adaptée aux processeurs multicœurs et ralentit considérablement
l&amp;rsquo;ensemble des serveurs ».&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="meson-une-alternative-moderne"&gt;Meson, une alternative moderne&lt;/h2&gt;
&lt;p&gt;À l&amp;rsquo;image des &lt;em&gt;Autotools&lt;/em&gt;, &lt;a href="https://mesonbuild.com/" target="_blank" rel="noopener"&gt;Meson&lt;/a&gt; est un système qui génère les instructions
de compilation. Ce fut le choix qu&amp;rsquo;a proposé Andres Freund à la communauté après
l&amp;rsquo;avoir analysé aux côtés de &lt;a href="https://cmake.org/cmake/help/book/mastering-cmake/chapter/Why%20CMake.html#" target="_blank" rel="noopener"&gt;CMake&lt;/a&gt; et &lt;a href="https://bazel.build/about/vision" target="_blank" rel="noopener"&gt;Bazel&lt;/a&gt;, deux autres compétiteurs
bien connus du monde libre. Meson est écrit en Python et son but premier est de
réduire la part d&amp;rsquo;efforts des développeurs dans la rédaction d&amp;rsquo;instructions au
profit d&amp;rsquo;une plus grande productivité sur le logiciel en tant que tel.&lt;/p&gt;
&lt;p&gt;Le projet Meson s&amp;rsquo;appuie sur un système de construction bas niveau appelé
&lt;a href="https://ninja-build.org/" target="_blank" rel="noopener"&gt;ninja&lt;/a&gt; qui se veut, d&amp;rsquo;après son auteur &lt;a href="https://neugierig.org/software/chromium/notes/2011/02/ninja.html" target="_blank" rel="noopener"&gt;Evan Martin&lt;/a&gt;, être minimaliste
et bien plus performant que &lt;code&gt;make&lt;/code&gt;. Les instructions de compilation sont
renseignées automatiquement par Meson dans le fichier &lt;code&gt;build.ninja&lt;/code&gt; qui sera
ensuite parcouru par la commande &lt;code&gt;ninja&lt;/code&gt; pour compiler les sources en un fichier
binaire.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-meson-workflow.png" alt="Génération avec Meson"&gt;&lt;/p&gt;
&lt;p&gt;Pour reprendre mon projet minimaliste &lt;code&gt;foo&lt;/code&gt;, le fichier &lt;em&gt;Makefile&lt;/em&gt; est remplacé
par le fichier &lt;code&gt;meson.build&lt;/code&gt;, en y renseignant les méta-données du projet, le
point d&amp;rsquo;entrée du programme et le fichier binaire souhaité.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rb" data-lang="rb"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./meson.build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;foo.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Meson est un jeune projet, dont la première sortie date de 2013. Bien qu&amp;rsquo;il
rougît de son âge face à CMake, il n&amp;rsquo;en reste pas moins un concurrent qui ne
cesse de &lt;a href="https://gms.tf/the-rise-of-meson.html" target="_blank" rel="noopener"&gt;gagner du terrain&lt;/a&gt; depuis la dernière décennie. Parmi les
&lt;a href="https://mesonbuild.com/Users.html" target="_blank" rel="noopener"&gt;projets&lt;/a&gt; qui s&amp;rsquo;appuient désormais sur Meson, je peux citer de très connus
comme : &lt;code&gt;systemd&lt;/code&gt;, Gnome et GTK+, QEMU, Xorg et Wayland&amp;hellip; De quoi se parer
d&amp;rsquo;une forte communauté d&amp;rsquo;utilisateurs dans les prochaines années !&lt;/p&gt;
&lt;p&gt;Sur la page « &lt;a href="https://mesonbuild.com/Use-of-Python.html" target="_blank" rel="noopener"&gt;Use of Python&lt;/a&gt; » du projet, les créateurs de Meson se
défendent d&amp;rsquo;un reproche souvent adressé aux technologies modernes et affirment
ne reposer que sur &lt;code&gt;python3&lt;/code&gt; tout en interdisant l&amp;rsquo;usage de modules externes,
en gages de qualité et de compatibilité. Ainsi, le projet se veut accessible
pour tous les systèmes d&amp;rsquo;exploitation, faisait un pied de nez au mastodonte
&lt;em&gt;Autotools&lt;/em&gt; qui était très couplé au &lt;em&gt;shell&lt;/em&gt; Unix dans son implémentation.&lt;/p&gt;
&lt;p&gt;La décision pour le projet PostgreSQL de progressivement glisser vers Meson est
le fruit de plusieurs mois de réflexion, avec pour ambition notamment de se
passer du système &lt;a href="https://github.com/postgres/postgres/tree/master/src/tools/msvc" target="_blank" rel="noopener"&gt;MSVC&lt;/a&gt;, un ensemble de scripts Perl maison maintenus par
une poignée de personnes pour compiler le logiciel sous Windows. En prime, les
travaux d&amp;rsquo;Andres démontrent un gain significatif dans les temps de &lt;em&gt;build&lt;/em&gt;, ce
qui ne paraît pas surprenant au regard du &lt;em&gt;&lt;a href="https://mesonbuild.com/ARM-performance-test.html" target="_blank" rel="noopener"&gt;benchmark&lt;/a&gt;&lt;/em&gt; entre les deux
systèmes sur une architecture ARM.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-meson-autotools-benchmark-conf.png" alt="Benchmark sur le temps de configuration"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2022-09-29-meson-autotools-benchmark-build.png" alt="Benchmark sur le temps de configuration"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In any case, using Autotools for a modern C/C++ project in 2021 is like using
CVS for source code version control in 2021: there are better tools available
and thus it isn&amp;rsquo;t very interesting to still consider the legacy solutions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Citation de &lt;a href="https://gms.tf/the-rise-of-meson.html" target="_blank" rel="noopener"&gt;Georg Sauthoff, The Rise of Meson&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;La route empruntée semble la bonne, bien que le chemin soit encore long pour se
débarrasser définitivement des résidus historiques d&amp;rsquo;&lt;em&gt;Autoconf&lt;/em&gt; dans le projet
PostgreSQL. Lors de la dernière &lt;em&gt;&lt;a href="https://wiki.postgresql.org/wiki/PgCon_2022_Developer_Unconference#Meson_new_build_system_proposal" target="_blank" rel="noopener"&gt;Developer Unconference&lt;/a&gt;&lt;/em&gt; qui s&amp;rsquo;est tenue
en ligne le 25 mai 2022 lors de l&amp;rsquo;événement annuel du PgCon, les membres de la
communauté ont statué sur les efforts à fournir pour porter ce chantier
titanesque.&lt;/p&gt;
&lt;p&gt;Avec ce récent &lt;a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=e6927270cd18d535b77cbe79c55c6584351524be" target="_blank" rel="noopener"&gt;patch&lt;/a&gt; rattaché à présent à la branche &lt;em&gt;master&lt;/em&gt;, la
construction des binaires sur la plupart des systèmes d&amp;rsquo;exploitation est
implémentée, avec notamment la compilation de PostgreSQL sur Windows à travers
&lt;code&gt;ninja&lt;/code&gt;. C&amp;rsquo;est une première pierre qui est posée pour la prochaine version 16 en
cours de développement et l&amp;rsquo;émergence d&amp;rsquo;une architecture qui grandira avec
d&amp;rsquo;autres améliorations dans un avenir proche.&lt;/p&gt;</description></item><item><title>Migrer vers PostgreSQL</title><link>https://fljd.in/2021/12/06/migrer-vers-postgresql/</link><pubDate>Mon, 06 Dec 2021 00:00:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2021/12/06/migrer-vers-postgresql/</guid><description>&lt;p&gt;Le marché de la migration en France est intense. Le Groupe de Travail
Inter-Entreprises PostgreSQL (PGGTIE) a même publié un &lt;a href="https://www.postgresql.fr/_media/entreprises/guide-de-transition_v2.0.pdf" target="_blank" rel="noopener"&gt;guide de transition&lt;/a&gt;
à PostgreSQL à destination des entreprises françaises. Ce dernier est le fruit
de près de cinq années de travaux au sein de plusieurs organismes publics et a
pour ambition de démontrer l&amp;rsquo;intérêt de PostgreSQL aux décideurs techniques en
présentant les forces et les faiblesses du moteur.&lt;/p&gt;
&lt;p&gt;Le mois dernier, une traduction en anglais est &lt;a href="https://twitter.com/nthonynowocien/status/1458065335387578382" target="_blank" rel="noopener"&gt;sortie des cartons&lt;/a&gt; pour
promouvoir davantage ce mouvement vers le logiciel libre dans les autres pays.
Je trouvais intéressant de profiter de cette actualité pour partager mes
réflexions du moment, entre ma vision du marché français et l&amp;rsquo;approche technique
pour engager les migrations de données vers PostgreSQL.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="une-transition-à-marche-forcée"&gt;Une transition à marche forcée&lt;/h2&gt;
&lt;p&gt;La démarche n&amp;rsquo;est pas anodine, car depuis 2012, l&amp;rsquo;État français au travers de ses
différents ministères, tente de contrer l&amp;rsquo;imposant marché des logiciels
propriétaires en favorisant leurs remplacements systématiques par une alternative
Open Source. Depuis quelques années, le &lt;a href="https://www.numerique.gouv.fr/actualites/socle-interministeriel-des-logiciels-libres-sill-2020/" target="_blank" rel="noopener"&gt;Socle Interministériel de Logiciels Libres&lt;/a&gt;
(SILL) est mis à jour par un groupe de travail pour faire un bilan des choix
structurants engagés sur le territoire français.&lt;/p&gt;
&lt;p&gt;La publication d&amp;rsquo;un guide de transition pour PostgreSQL aspire à rassurer les
équipes opérationnelles et décisionnelles en apportant un grand nombre de conseils
et de comparaisons sur ce qu&amp;rsquo;il est aujourd&amp;rsquo;hui possible de faire avec PostgreSQL.
Il mentionne les trois grandes étapes pour réaliser la migration des données
d&amp;rsquo;une application vers le moteur open-source :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;migration des données ;&lt;/li&gt;
&lt;li&gt;correction et réécriture des requêtes SQL et des procédures embarquées ;&lt;/li&gt;
&lt;li&gt;recette du système pour écarter les régressions fonctionnelles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chacune de ces étapes a un coût, qu&amp;rsquo;il sera nécessaire de pondérer durant le
chantier de migration. Les logiciels Open Source sont libres, mais ne sont pas
gratuits et c&amp;rsquo;est un rappel essentiel que proposent les conclusions de ce guide.
Les coûts de formation et de prestations externes ne sont également pas
négligeables…&lt;/p&gt;
&lt;p&gt;En 2018, la société française &lt;a href="https://dalibo.com" target="_blank" rel="noopener"&gt;Dalibo&lt;/a&gt; avait publié un
&lt;a href="https://public.dalibo.com/archives/marketing/livres_blancs/18.10/DLB01_Migrer_Oracle_Postgresql.pdf" target="_blank" rel="noopener"&gt;livre blanc&lt;/a&gt; dans ce même esprit de pédagogie sur l&amp;rsquo;importance des coûts
d&amp;rsquo;investissement et de possession. Dans un scénario de migration, acté sur cinq
années de transition, l&amp;rsquo;étude montre que l&amp;rsquo;analyse de portabilité vers PostgreSQL
est la principale dépense au cours de la première année, suivie par un
investissement important pour la recherche et le développement d&amp;rsquo;un socle d&amp;rsquo;outils
dédiés aux déploiements, la maintenance et l&amp;rsquo;administration du parc PostgreSQL.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2021-12-06-cout-d-investissement.png" alt="Plan d’investissement"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2021-12-06-cout-de-possession.png" alt="Plan de possession"&gt;&lt;/p&gt;
&lt;p&gt;La seconde courbe peut paraître surprenante pour du logiciel libre, mais la
nécessité de contracter une offre de support reste d&amp;rsquo;actualité, car trop peu de
services informatiques disposent des compétences internes pour assurer la
disponibilité et la maintenance des instances. Un effort initial de formation est
requis pour aider les administrateurs en poste à faire également leur transition
vers cette autre technologie.&lt;/p&gt;
&lt;p&gt;Pour cette raison, de nombreux acteurs se positionnent depuis des décennies pour
proposer de l&amp;rsquo;accompagnement opérationnel et technique auprès des entreprises
de toutes tailles qui souhaitent (principalement) réduire les coûts de licence
de logiciels propriétaires. Et bien que la transition pouvait être pénible il y
a dix ans, les dernières versions de PostgreSQL et la prolifération d&amp;rsquo;outils
communautaires apportent leur lot de fonctionnalités qui ne rougissent plus
devant les atouts phares de certains mastodontes historiques du marché.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sarmer-doutils-et-de-courage"&gt;S&amp;rsquo;armer d&amp;rsquo;outils et de courage&lt;/h2&gt;
&lt;p&gt;Réaliser une migration impose de s&amp;rsquo;équiper d&amp;rsquo;un panel d&amp;rsquo;outils pour simplifier
les grandes lignes de la migration et bien sûr, économiser du temps et de l&amp;rsquo;argent.
Parmi les contributions libres, nous retrouverons l&amp;rsquo;incontournable &lt;a href="https://ora2pg.darold.net/" target="_blank" rel="noopener"&gt;Ora2pg&lt;/a&gt;,
spécialisé dans la conversion d&amp;rsquo;un schéma Oracle et la migration de son contenu
vers une instance PostgreSQL. Pour une base de type SQL Server de Microsoft,
&lt;a href="https://pgloader.io/" target="_blank" rel="noopener"&gt;pgloader&lt;/a&gt; sera privilégié. Les deux outils supportent également les données
en provenance de MySQL.&lt;/p&gt;
&lt;p&gt;En &lt;a href="https://fljd.in/2021/07/16/parlons-un-peu-des-donnees-externes/"&gt;juillet dernier&lt;/a&gt;, je partageais mes réflexions sur l&amp;rsquo;implémentation de la
norme SQL/MED et des &lt;em&gt;foreign data wrappers&lt;/em&gt;, ou connecteurs. L&amp;rsquo;actualité fut
particulièrement riche cet été, avec la sortie de la &lt;a href="https://www.migops.com/blog/2021/07/01/ora2pg-now-supports-oracle_fdw-to-increase-the-data-migration-speed/" target="_blank" rel="noopener"&gt;version 22.0&lt;/a&gt; du projet
Ora2Pg, dont le nouveau support de l&amp;rsquo;extension &lt;code&gt;oracle_fdw&lt;/code&gt; annonçait des gains
vertigineux sur le temps de copie des données.&lt;/p&gt;
&lt;p&gt;Ni une ni deux, j&amp;rsquo;ai souhaité comprendre et découvrir les dessous d&amp;rsquo;un tel
procédé en manipulant moi-même les tables externes pour réaliser une migration
de schémas et de données d&amp;rsquo;une base Oracle vers PostgreSQL, sans utiliser l&amp;rsquo;outil
Ora2Pg. Cette procédure s&amp;rsquo;inspire d&amp;rsquo;un &lt;a href="https://www.migops.com/blog/2021/02/15/role-of-foreign-data-wrappers-in-migrations-to-postgresql/" target="_blank" rel="noopener"&gt;autre article&lt;/a&gt; de MigOps, publié plus
tôt cette année.&lt;/p&gt;
&lt;h3 id="1-compiler-le-wrapper-communautaire"&gt;1. Compiler le wrapper communautaire&lt;/h3&gt;
&lt;p&gt;Le connecteur &lt;code&gt;oracle_fdw&lt;/code&gt; nécessite d&amp;rsquo;être compilé à partir des &lt;a href="https://github.com/laurenz/oracle_fdw/" target="_blank" rel="noopener"&gt;sources du projet&lt;/a&gt;
pour prendre en compte les bibliothèques propriétaires d&amp;rsquo;Oracle, garantes du
protocole de connexion et des échanges de données. Dans l&amp;rsquo;exemple ci-dessous, je
dispose d&amp;rsquo;un serveur CentOS 7 avec une instance PostgreSQL 14 et j&amp;rsquo;ai installé
l&amp;rsquo;&lt;em&gt;Instant Client&lt;/em&gt; 19, &lt;a href="https://www.oracle.com/database/technologies/instant-client/downloads.html" target="_blank" rel="noopener"&gt;disponible&lt;/a&gt; sur la plateforme de téléchargement d&amp;rsquo;Oracle.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yum install -y postgresql14-devel
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;ORACLE_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/oracle/19.13/client64
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ORACLE_HOME&lt;/span&gt;/lib
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:&lt;span class="nv"&gt;$ORACLE_HOME&lt;/span&gt;/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ORACLE_FDW_2_4_0/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make &lt;span class="nv"&gt;PG_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/pgsql-14/bin/pg_config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make &lt;span class="nv"&gt;PG_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/pgsql-14/bin/pg_config install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pour résoudre &lt;a href="https://github.com/laurenz/oracle_fdw/issues/312" target="_blank" rel="noopener"&gt;d&amp;rsquo;éventuels problèmes&lt;/a&gt; de détection de la librairie Oracle
&lt;em&gt;Instant Client&lt;/em&gt; et permettre la création de l&amp;rsquo;extension, il est nécessaire de
renseigner la variable &lt;code&gt;$LD_LIBRARY_PATH&lt;/code&gt; pour l&amp;rsquo;instance PostgreSQL au niveau
de son service :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl edit postgresql-14
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ORACLE_HOME=/usr/lib/oracle/19.13/client64&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;LD_LIBRARY_PATH=/usr/lib/oracle/19.13/client64/lib&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;L&amp;rsquo;extension peut ainsi être créée avec les privilèges d&amp;rsquo;un super-utilisateur
dans la base qui accueillera les données finales. Le privilège &lt;code&gt;USAGE&lt;/code&gt; peut ensuite
être octroyé au propriétaire de la base pour préparer le schéma.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EXTENSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oracle_fdw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WRAPPER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oracle_fdw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-importer-les-tables-externes"&gt;2. Importer les tables externes&lt;/h3&gt;
&lt;p&gt;L&amp;rsquo;unique configuration repose sur la création d&amp;rsquo;un objet &lt;code&gt;SERVER&lt;/code&gt; et l&amp;rsquo;attribution
d&amp;rsquo;un mot de passe pour l&amp;rsquo;utilisateur courant, afin d&amp;rsquo;ouvrir un pont entre les
deux systèmes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SERVER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orcl_hr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WRAPPER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oracle_fdw&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OPTIONS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbserver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;//localhost:1521/hr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MAPPING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SERVER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orcl_hr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OPTIONS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;demo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La magie du &lt;em&gt;wrapper&lt;/em&gt; opère lors de l&amp;rsquo;inspection de la base de données externe
et de la création des relations dans un schéma PostgreSQL. Dans mon exemple, je
dispose d&amp;rsquo;une base Oracle XE avec le &lt;a href="https://github.com/oracle/db-sample-schemas/tree/master/human_resources" target="_blank" rel="noopener"&gt;schéma prédéfini HR&lt;/a&gt; contenant une
variété de tables, de vues, d&amp;rsquo;index et de contraintes d&amp;rsquo;intégrité.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SCHEMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;search_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IMPORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SCHEMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;HR&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SERVER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orcl_hr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Avec &lt;code&gt;psql&lt;/code&gt; et la méta-commande &lt;code&gt;\dE&lt;/code&gt;, il est dès lors possible de consulter les
tables externes importées dans le schéma &lt;code&gt;source&lt;/code&gt;. L&amp;rsquo;une des huit tables est en
réalité une vue, car l&amp;rsquo;extension consulte le catalogue &lt;code&gt;ALL_TAB_COLUMNS&lt;/code&gt; dans
lequel figurent les tables et vues accessibles par l&amp;rsquo;utilisateur connecté. Dans
la phase d&amp;rsquo;étude de la migration, ce point doit être correctement identifié pour
récupérer l&amp;rsquo;ordre de création de la vue, en adaptant la requête SQL si besoin.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; List of relations
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Schema | Name | Type | Owner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--------+------------------+---------------+-------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | countries | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | departments | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | emp_details_view | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | employees | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | job_history | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | jobs | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | locations | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; source | regions | foreign table | hr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-créer-les-tables-et-les-vues"&gt;3. Créer les tables et les vues&lt;/h3&gt;
&lt;p&gt;Pour chacune de ces tables externes, il faut créer leur équivalent dans la base
PostgreSQL. Pour mon exemple, je choisis le schéma &lt;code&gt;public&lt;/code&gt; comme destination.
L&amp;rsquo;une des méthodes les plus rapides repose sur l&amp;rsquo;option &lt;code&gt;LIKE&lt;/code&gt; de l&amp;rsquo;instruction
&lt;code&gt;CREATE TABLE&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;À partir de cette étape, il est judicieux d&amp;rsquo;apporter quelques modifications aux
types de colonnes. En effet, les types &lt;code&gt;DATE&lt;/code&gt; et &lt;code&gt;NUMBER&lt;/code&gt; en provenance d&amp;rsquo;Oracle
ont des équivalences bien plus riches et efficientes avec PostgreSQL. Pour aller
plus loin, l&amp;rsquo;un des chapitres de la formation Dalibo « &lt;a href="https://public.dalibo.com/exports/formation/manuels/modules/n3/n3.handout.html#types-de-donn%C3%A9es" target="_blank" rel="noopener"&gt;Migrer d’Oracle à
PostgreSQL&lt;/a&gt; » revient sur les différences notables entre les deux systèmes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;smallint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;smallint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hire_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;end_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La vue, quant à elle, doit être recréée pour les besoins de l&amp;rsquo;application.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VIEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emp_details_view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commission_pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state_province&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;country_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commission_pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state_province&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4-importer-les-données"&gt;4. Importer les données&lt;/h3&gt;
&lt;p&gt;La méthode la plus simple pour importer les données reste la copie table à table
avec l&amp;rsquo;instruction &lt;code&gt;INSERT INTO&lt;/code&gt;. Avec PostgreSQL, la commande &lt;code&gt;TABLE&lt;/code&gt; est
équivalente à un &lt;code&gt;SELECT * FROM&lt;/code&gt;, que j&amp;rsquo;utilise pour alléger les requêtes dans
l&amp;rsquo;exemple ci-dessous.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dans le cas de tables hautement volumineuses, cette méthode montrera très vite
des faiblesses de performance, car les instructions sont exécutées les unes après
les autres et que le parcours d&amp;rsquo;une table distante n&amp;rsquo;est réalisé que par un seul
processus.&lt;/p&gt;
&lt;p&gt;À moins de développer son propre système de distribution de requêtes et de
lectures parallélisées, sachez que l&amp;rsquo;outil Ora2Pg propose parfaitement des pistes
d&amp;rsquo;optimisation pour accélérer grandement les insertions lors de cette phase de
chargement.&lt;/p&gt;
&lt;h3 id="5-créer-les-séquences-les-index-et-les-contraintes"&gt;5. Créer les séquences, les index et les contraintes&lt;/h3&gt;
&lt;p&gt;Cette dernière étape est indispensable pour maintenir la cohérence du modèle et
pour garantir des performances similaires, il ne faut donc pas mésestimer la
phase d&amp;rsquo;étude qui permettra d&amp;rsquo;inventorier les séquences, les index et les
contraintes à recréer dans la base PostgreSQL. Pour aller jusqu&amp;rsquo;au bout de ma
démonstration, voici les instructions à exécuter pour finaliser la migration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- regions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reg_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- countries
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;country_c_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;countr_reg_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- locations
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEQUENCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locations_seq&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3300&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INCREMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;MAXVALUE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9900&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;locations_seq&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc_c_id_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- departments
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEQUENCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;departments_seq&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INCREMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;MAXVALUE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9990&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;departments_seq&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dept_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dept_loc_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- jobs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- employees
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEQUENCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employees_seq&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;207&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INCREMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;employees_seq&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emp_emp_id_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emp_dept_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emp_job_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emp_manager_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dept_mgr_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- job_history
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job_history&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jhist_emp_id_st_date_pk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jhist_job_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jhist_emp_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jhist_dept_fk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dans des situations particulièrement simples, la migration vers PostgreSQL peut
être intégralement réalisée à l&amp;rsquo;aide du langage SQL et d&amp;rsquo;une extension de la
famille des &lt;em&gt;foreign data wrappers&lt;/em&gt;. Cependant, le temps de copie restera très
dépendant de l&amp;rsquo;ordonnancement que l&amp;rsquo;on peut en faire, et du débit de transfert
entre les deux systèmes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;La migration deviendra complexe lorsque la base de données à migrer embarque du
code PL/SQL stratégique. À moins d&amp;rsquo;extraire la logique métier et de l&amp;rsquo;implémenter
dans une application dédiée, la conversion du code en routines PL/pgSQL méritera
toute votre attention. Bien qu&amp;rsquo;Ora2Pg puisse simplifier le travail de portage
avec une conversion automatique tout à fait honorable, il sera indispensable de
tester la non-régression fonctionnelle.&lt;/p&gt;
&lt;p&gt;Un outil comme &lt;a href="https://fljd.in/2020/05/14/ecrire-ses-tests-unitaires-en-sql/"&gt;pgTAP&lt;/a&gt; peut aider à rédiger des tests unitaires ou de bout
en bout pour couvrir le code et vous accompagner dans le portage des procédures.
L&amp;rsquo;année dernière, j&amp;rsquo;avais pris plaisir à rédiger un &lt;a href="https://blog.dalibo.com/2020/12/21/migration_oracle_vers_postgresql.html" target="_blank" rel="noopener"&gt;atelier complet&lt;/a&gt; sur la
traduction des procédures PL/SQL fournies avec le schéma HR présenté aujourd&amp;rsquo;hui.
N&amp;rsquo;hésitez pas à le consulter !&lt;/p&gt;</description></item><item><title>BorgBackup ou la sauvegarde facile</title><link>https://fljd.in/2021/08/24/borg-ou-la-sauvegarde-facile/</link><pubDate>Tue, 24 Aug 2021 00:00:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2021/08/24/borg-ou-la-sauvegarde-facile/</guid><description>&lt;p&gt;Jusqu&amp;rsquo;à très récemment, je ne me préoccupais pas de la pertinence de mes
sauvegardes de fichiers personnels réalisées naïvement avec un script &lt;code&gt;rsync&lt;/code&gt;.
C&amp;rsquo;est honteux dans nos métiers, mais l&amp;rsquo;adage du cordonnier s&amp;rsquo;est vérifié avec
moi lors de l&amp;rsquo;exécution d&amp;rsquo;un vulgaire &lt;code&gt;find $NOVAR/ -delete&lt;/code&gt; durant des tests.&lt;/p&gt;
&lt;p&gt;Après cet épisode et l&amp;rsquo;amertume d&amp;rsquo;avoir perdu quelques travaux, ou la surprise
de découvrir les ravages de leur disparition plusieurs semaines après ma fatale
erreur, je me suis tourné vers l&amp;rsquo;outil incontournable dont tous mes collègues me
parlaient : &lt;a href="https://borgbackup.readthedocs.io/en/stable/" target="_blank" rel="noopener"&gt;BorgBackup&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="des-atouts-séduisants"&gt;Des atouts séduisants&lt;/h2&gt;
&lt;p&gt;Cela fait à présent deux années que je travaille au quotidien sur Linux pour mon
activité professionnelle. Étant responsable de mon matériel et des données que
je manipule, il n&amp;rsquo;existe pas de politique particulière pour les sauvegardes
régulières ni le stockage de celles-ci.&lt;/p&gt;
&lt;p&gt;Mon précédent script était passablement trivial, à savoir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sauvegarder une copie unique des documents courants ;&lt;/li&gt;
&lt;li&gt;Sauvegarder une liste des fichiers de configuration, appelés &lt;em&gt;dotfiles&lt;/em&gt; ;&lt;/li&gt;
&lt;li&gt;Exclure les projets contenant un répertoire &lt;code&gt;.git&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Exclure les fichiers de plus de 200 Mo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le tout sur une clé USB de 8 Go, chiffrée avec le système &lt;a href="https://fr.wikipedia.org/wiki/LUKS" target="_blank" rel="noopener"&gt;LUKS&lt;/a&gt; pour se
prévenir de la perte ou du vol dans un espace public dudit support.&lt;/p&gt;
&lt;p&gt;Borg (son petit nom), permet de répondre correctement à l&amp;rsquo;ensemble de ces
points, et le fait à merveille. En bénéficiant de la déduplication et de la
compression, j&amp;rsquo;ai pu conserver ma clé USB et m&amp;rsquo;engager dans la rédaction d&amp;rsquo;un
&lt;a href="https://gist.github.com/fljdin/de46c7c8d18cc37e591cb8364ecf8eef" target="_blank" rel="noopener"&gt;nouveau script&lt;/a&gt; plus complet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exclusion de fichiers et de répertoires&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Certains fichiers ou répertoires cachés vivent leur vie sur nos postes, et un
fichier d&amp;rsquo;exclusion peut être transmis à Borg pour ne pas les inclure dans la
routine de sauvegarde. Le mien est relativement léger pour le moment, et
pourrait s&amp;rsquo;enrichir avec l&amp;rsquo;expérience.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ~/.config/borg/exclude.list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/**/.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/**/.vagrant
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.cache
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.mozilla
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.thunderbird
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.config/chromium
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.config/discord
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.npm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/*/.cpan
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Déduplication et rétention d&amp;rsquo;une semaine&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le gain remarquable entre mon script et la nouvelle solution que m&amp;rsquo;offre Borg
réside dans son algorithme de déduplication, où la version d&amp;rsquo;un fichier n&amp;rsquo;est
stockée qu&amp;rsquo;une seule fois jusqu&amp;rsquo;à une éventuelle modification, permettant de ne
conserver qu&amp;rsquo;une copie de mes documents sur une bien plus longue période avec
un coût de stockage ridiculement faible.&lt;/p&gt;
&lt;p&gt;À chaque exécution, je demande le rapport de sauvegarde avec l&amp;rsquo;option &lt;code&gt;--stats&lt;/code&gt;
dont voici un exemplaire sur l&amp;rsquo;exécution de ce matin.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Archive name: florent-2021-08-24T08:42:05
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Archive fingerprint: be38707dd52f5a86f8e75fe3ad4997aae58b3016366a988b7cb37ce3b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Time &lt;span class="o"&gt;(&lt;/span&gt;start&lt;span class="o"&gt;)&lt;/span&gt;: Tue, 2021-08-24 08:42:06
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Time &lt;span class="o"&gt;(&lt;/span&gt;end&lt;span class="o"&gt;)&lt;/span&gt;: Tue, 2021-08-24 08:42:06
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Duration: 0.96 seconds
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Number of files: &lt;span class="m"&gt;3720&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Utilization of max. archive size: 0%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Original size Compressed size Deduplicated size
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This archive: 2.11 GB 1.78 GB 480.51 kB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;All archives: 12.63 GB 10.67 GB 1.75 GB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Unique chunks Total chunks
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Chunk index: &lt;span class="m"&gt;3974&lt;/span&gt; &lt;span class="m"&gt;25888&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pour une semaine de rétention, l&amp;rsquo;espace total consommé sur le montage USB
n&amp;rsquo;excède pas (encore) les 1,75 Go alors que les images décompressées pèsent au
total 12,63 Go que ne parvennait pas à stocker mon ancien script sur plusieurs
jours.&lt;/p&gt;
&lt;p&gt;Au passage, la durée d&amp;rsquo;exécution est significativement réduite chaque jour grâce
à ce mécanisme, qui ne sélectionne que les nouveautés d&amp;rsquo;une exécution à l&amp;rsquo;autre.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un montage virtuel pour naviguer dans le temps&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;outil propose deux options pour la restauration :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;extract&lt;/code&gt; pour restaurer le contenu d&amp;rsquo;une archive dans le répertoire courant
(&lt;a href="https://borgbackup.readthedocs.io/en/stable/usage/extract.html" target="_blank" rel="noopener"&gt;doc&lt;/a&gt;) ;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount&lt;/code&gt; pour visualiser les fichiers ou dossiers à restaurer, voire les
déplacer manuellement dans le répertoire cible avec un outil graphique
(&lt;a href="https://borgbackup.readthedocs.io/en/stable/usage/mount.html" target="_blank" rel="noopener"&gt;doc&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cette seconde proposition est particulièrement utile pour naviguer d&amp;rsquo;une archive
à une autre, à la recherche d&amp;rsquo;un fichier perdu dans le temps. Le montage
s&amp;rsquo;effectue à l&amp;rsquo;aide du système de fichiers &lt;a href="https://www.kernel.org/doc/html/latest/filesystems/fuse.html" target="_blank" rel="noopener"&gt;FUSE&lt;/a&gt; qui ne nécessite aucun
droit administrateur.&lt;/p&gt;
&lt;p&gt;Pour être utilisé à partir de Borg, le paquet &lt;code&gt;python-llfuse&lt;/code&gt; sera requis selon
votre distribution.&lt;/p&gt;
&lt;h2 id="planification-avec-systemd"&gt;Planification avec systemd&lt;/h2&gt;
&lt;p&gt;Ce fut une découverte lors de mes lectures émerveillées de la &lt;a href="https://gist.github.com/fljdin/de46c7c8d18cc37e591cb8364ecf8eef" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;
et des &lt;a href="https://wiki.archlinux.org/title/Borg_backup_%28Fran%C3%A7ais%29" target="_blank" rel="noopener"&gt;ressources&lt;/a&gt; de la communauté française d&amp;rsquo;Archlinux. N&amp;rsquo;étant pas
particulièrement fan de &lt;code&gt;cron&lt;/code&gt; pour un usage personnel, j&amp;rsquo;ai suivi à la lettre
la configuration d&amp;rsquo;un service utilisateur et du &lt;em&gt;timer&lt;/em&gt; associé pour garantir le
lancement de la sauvegarde chaque jour, 30 minutes après le démarrage de mon
système.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .config/systemd/user/borg.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Borg backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;%h/.bin/borg-create.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .config/systemd/user/borg.timer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Daily Borg backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Documentation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;man:borg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Timer]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;OnBootSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;30min&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;OnUnitActiveSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;timers.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Une fois le service activé, je n&amp;rsquo;ai qu&amp;rsquo;à attendre chaque matin la notification
de fin de sauvegarde pour consulter les rapports avec la commande &lt;code&gt;journalctl&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl --user -xeu borg.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;août &lt;span class="m"&gt;24&lt;/span&gt; 08:42:02 florent systemd&lt;span class="o"&gt;[&lt;/span&gt;877&lt;span class="o"&gt;]&lt;/span&gt;: Starting Borg backup...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;août &lt;span class="m"&gt;24&lt;/span&gt; 08:42:27 florent systemd&lt;span class="o"&gt;[&lt;/span&gt;877&lt;span class="o"&gt;]&lt;/span&gt;: Finished Borg backup.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;août &lt;span class="m"&gt;24&lt;/span&gt; 08:42:27 florent systemd&lt;span class="o"&gt;[&lt;/span&gt;877&lt;span class="o"&gt;]&lt;/span&gt;: borg.service: Consumed 1.535s CPU time.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;De trop nombreux utilisateurs (Windows notamment) comptent sur leur miraculeuse
corbeille, jusqu&amp;rsquo;à ce qu&amp;rsquo;une véritable suppression ne survienne ou qu&amp;rsquo;un disque
défaillant fasse des siennes. Pour s&amp;rsquo;assurer qu&amp;rsquo;une donnée ne disparaisse pas
définitivement, la sauvegarde doit être correctement mise en place, idéalement
sur plusieurs périodes et sur plusieurs supports.&lt;/p&gt;
&lt;p&gt;Le conseil vaut aussi bien pour les bases de données que pour les fichiers
personnels : une sauvegarde n&amp;rsquo;est fiable que si la restauration de son
contenu est possible. Une corruption de l&amp;rsquo;archive, une erreur dans l&amp;rsquo;exécution
du script ou une mise à jour majeure de l&amp;rsquo;outil de sauvegarde sont autant de
situations qui peuvent rendre vos sauvegardes irrécupérables.&lt;/p&gt;
&lt;p&gt;Prenez soin de vos sauvegardes.&lt;/p&gt;</description></item></channel></rss>