<?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 Discovery notebook</title><link>https://fljd.in/en/tags/opensource/</link><description>Recent content in Opensource on Discovery notebook</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>Florent Jardin</managingEditor><lastBuildDate>Tue, 30 Jul 2024 09:30:00 +0000</lastBuildDate><atom:link href="https://fljd.in/en/tags/opensource/index.xml" rel="self" type="application/rss+xml"/><item><title>How to keep a community alive</title><link>https://fljd.in/en/2024/07/30/how-to-keep-a-community-alive/</link><pubDate>Tue, 30 Jul 2024 09:30:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/en/2024/07/30/how-to-keep-a-community-alive/</guid><description>&lt;p&gt;The &lt;a href="https://pgday.fr/" target="_blank" rel="noopener"&gt;PG Day France&lt;/a&gt; took place on June 11th and 12th in Lille, my hometown.
It is the event of the French PostgreSQL community that settles in a different city each year.
The opportunity was too good for me and I met many people from all over France and its surroundings,
to discuss PostgreSQL during two days of workshops and conferences.&lt;/p&gt;
&lt;p&gt;For this edition, I had the pleasure of speaking and sharing my experience on the animation of
the local Meetup group that I took over four years ago. In this article, I want to write down the
main points discussed during this presentation, while waiting for the video of the conference to be
posted online.&lt;/p&gt;
&lt;div class="message"&gt;The slides of my presentation are &lt;a href="https://fljd.in/documents/pgdayfr-faire-vivre-une-communaute.pdf"&gt;available at this address&lt;/a&gt; (french).&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="what-is-a-pug"&gt;What is a PUG?&lt;/h2&gt;
&lt;p&gt;A « PUG » is a PostgreSQL User Group, a community of PostgreSQL users who meet regularly to exchange
about PostgreSQL. These local communities are common, especially for free software, and allow users
to share their experiences and knowledge about the software.&lt;/p&gt;
&lt;p&gt;Lille is a city with a rich economic ecosystem, and many communities are active in the region, such as
the &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;), the &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;
or &lt;a href="https://www.meetup.com/Software-Craftsmanship-Lille/" target="_blank" rel="noopener"&gt;Software Craft Lille&lt;/a&gt;. What could be more natural than wanting to offer a handful of this audience
more meetings with PostgreSQL?&lt;/p&gt;
&lt;p&gt;A &lt;a href="https://www.postgresql.org/community/user-groups/" target="_blank" rel="noopener"&gt;dedicated page&lt;/a&gt; of the PostgreSQL project lists the different PUGs around the world, and it is possible
to find groups in many countries, including France. At the time of writing my presentation, I counted 62 global
groups, including 5 in France: Paris, Lyon, Nantes (not referenced), Toulouse and Lille.&lt;/p&gt;
&lt;p&gt;Meetup&amp;rsquo;s platform is often used to organize meetings, and as proof, 46 PUGs are affiliated with it. This has
been a convenience for more than 10 years, and most French groups are registered there.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/en/2024-07-30-map-of-pugs-in-france.png" alt="Map of PUGs in France"&gt;&lt;/p&gt;
&lt;p&gt;The creation of a PUG recognized by the international community is a simple process, which requires
to form an organizing committee and to apply to the email address &lt;code&gt;usergroups@postgresql.org&lt;/code&gt;.
The group must respect a number of rules, provided by the &lt;a href="https://www.postgresql.org/about/policies/user-groups/" target="_blank" rel="noopener"&gt;PUG policy&lt;/a&gt;, and must commit to
respecting the values of the PostgreSQL community. In essence, here are the points to remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The group must be open to all, without discrimination.&lt;/li&gt;
&lt;li&gt;Meetings must be proposed at least once every two years.&lt;/li&gt;
&lt;li&gt;Meetings must not be subject to a non-disclosure agreement (NDA).&lt;/li&gt;
&lt;li&gt;Meetings are attached to the geographical area of the group.&lt;/li&gt;
&lt;li&gt;A company cannot be represented at 50% or more in the organizing committee.&lt;/li&gt;
&lt;li&gt;The selection of conferences is at the discretion of the organizing committee.&lt;/li&gt;
&lt;li&gt;Companies can promote their products and services, if their activities facilitate
the adoption of PostgreSQL and if the content presented is technical in nature.&lt;/li&gt;
&lt;li&gt;The group must disclose the names of sponsors, and may mention them in the
introduction of meetings.&lt;/li&gt;
&lt;li&gt;The PUG must adopt a code of conduct and can use &lt;a href="https://www.postgresql.org/about/policies/coc/fr/" target="_blank" rel="noopener"&gt;the one from the PostgreSQL community&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="genesis-of-the-group"&gt;Genesis of the group&lt;/h2&gt;
&lt;p&gt;The Meetup PostgreSQL Lille group was founded on February 25, 2016 on the eponymous platform. At the time,
the idea of reproducing the Paris&amp;rsquo;s Meetup format was shared between Guillaume Lelarge and Pierre Hilbert,
two Lille residents who regularly crossed paths at events. One of the main motivations was to promote free
software with a sharing of feedback to inspire other local actors.&lt;/p&gt;
&lt;p&gt;At that time, I myself attended events organized by the Meetup Oracle Paris and Province group, and I followed
with interest the conferences about PostgreSQL in my region. The first meeting of the PG Lille group took place
on &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/231446425/" target="_blank" rel="noopener"&gt;June 24, 2016&lt;/a&gt;, in the premises of Decathlon Campus (Villeneuve D&amp;rsquo;Ascq, 59). I was seduced by the format
and the relaxed atmosphere, and I have kept an excellent memory of it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/en/2024-07-30-first-meetup-pg-lille.png" alt="First Meetup PG Lille"&gt;&lt;/p&gt;
&lt;p&gt;However, the regularity of the meetings did not meet the expectations of the project. The next event was scheduled
more than a year later, on October 17, 2017, and it was the only one I could not attend. Guillaume and Pierre&amp;rsquo;s
professional commitments did not allow them to keep up the pace, and the group remained dormant for more than three
years.&lt;/p&gt;
&lt;p&gt;I took the decision to take over the group in 2019, by offering Pierre to transfer me the administration rights
of the group. My arrival at Dalibo as a PostgreSQL consultant gave me the opportunity to meet passionate experts,
including Guillaume Lelarge and Stefan Fercot, who encouraged me to take over the project.&lt;/p&gt;
&lt;p&gt;With a quality professional network on Twitter at the time, I was able to quickly mobilize a team of volunteers
to organize the Meetup of &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/267319389/" target="_blank" rel="noopener"&gt;January 28, 2020&lt;/a&gt;. I have been supported by Stefan Fercot (Dalibo), Stéphane Definin
(Think) and Sébastien Freiss (SFEIR) to make this renewal a small success.&lt;/p&gt;
&lt;p&gt;&amp;hellip; Unfortunately, in March of the same year, the COVID-19 pandemic forced the group to suspend its projects
and I did not have the courage to propose online content, much to the chagrin of the group&amp;rsquo;s members.
For the record, no other Meetup group in France was spared by the health crisis and the restrictions in force.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/en/2024-07-30-timeline-meetups.png" alt="Timeline of Meetups"&gt;&lt;/p&gt;
&lt;p&gt;Two additional years of pause were imposed on the group, before the health situation stabilized. In 2022, I
contacted Lætitia Avrot, a very active member of the French community, to revive activities and find a dynamic
and a rhythm for its members. The event of &lt;a href="https://www.meetup.com/meetup-postgresql-lille/events/284819405/" target="_blank" rel="noopener"&gt;April 14, 2022&lt;/a&gt; was a relief for me, seeing that I could still
count on the participation of the community.&lt;/p&gt;
&lt;p&gt;To this day, no more shadows have come to darken the picture, and the group has been able to resume
the regular organization of meetings, from two to three per year. The community has expanded, and the
organizing committee has been strengthened with the arrival of Yoann La Cancellera in 2023, who has
allowed in particular to submit a request for recognition within the international community, on
February 24, 2023.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-to-meetup"&gt;How-to Meetup&lt;/h2&gt;
&lt;p&gt;To conclude this feedback, I wanted to share all the necessary steps to organize a Meetup, focusing on
the crucial points of success and pitfalls to avoid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Welcome&lt;/strong&gt; : find the venue for the event&lt;/p&gt;
&lt;p&gt;As the PG Day France changes location every year, the Meetup PG Lille group has chosen to vary the
venues for each meeting. This allows you to discover new spaces and stimulate inter-community curiosity.
Local companies are often delighted to be able to host technical events, and this helps to strengthen
ties between local actors.&lt;/p&gt;
&lt;p&gt;Searching for a venue is often the first step in organizing a Meetup. It is important to find a space
that can accommodate between ten and forty people, with access to public transport or parking nearby.
To do this, I have had the opportunity to use several strategies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chinest whispers: ask your professional contacts if they know of potential hosting locations&lt;/li&gt;
&lt;li&gt;Social networks: post an announcement on Twitter or LinkedIn to solicit spontaneous proposals or leads
to explore&lt;/li&gt;
&lt;li&gt;Privatization of a space: contact coworking spaces or meeting rooms to get a rental quote&lt;/li&gt;
&lt;li&gt;Partnering with other groups: propose a partnership with another Meetup group to share rental costs
or to benefit from an already identified hosting location&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Speakers&lt;/strong&gt;: find &lt;u&gt;two&lt;/u&gt; varied presentations&lt;/p&gt;
&lt;p&gt;From the beginning, Pierre and Guillaume have chosen to offer two technical presentations to vary the
topics and formats. This allows to reach a wider audience and to meet the expectations of the group&amp;rsquo;s
members. This formula has been used for subsequent meetings, and has been a success every time.&lt;/p&gt;
&lt;p&gt;Finding speakers is a time-consuming and patience-testing task. Often, a chance meeting or an informal
discussion can lead to a presentation proposal. It is best to build a pool of resource persons who can
be called upon when needed. It is not always easy, it is a long-term job, but it is worth it.&lt;/p&gt;
&lt;p&gt;Spread the word, ask your colleagues, friends, and professional contacts if they are interested in
sharing their experience or expertise, or if they know someone who might be interested.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Networking&lt;/strong&gt;: extend the evening with a community highlight&lt;/p&gt;
&lt;p&gt;From my point of view, this is the &lt;strong&gt;most important&lt;/strong&gt; step in organizing a Meetup. (laughs!)&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s the moment when group members can exchange, discuss, share, and meet. It&amp;rsquo;s a highlight of the evening
that I particularly cherish, and that is intimately linked to the success of the event. Networking is what
best promotes serendipity, chance encounters, sharing experiences, and creating lasting bonds. I discuss
the group&amp;rsquo;s upcoming projects with members and the organizing committee, and I take notes on everyone&amp;rsquo;s
expectations and listen to their suggestions.&lt;/p&gt;
&lt;p&gt;Of course, it is important to provide drinks and snacks to extend the evening. It is necessary to provide
a space to facilitate circulation and the natural creation of small discussion groups. The choice of a
sponsor is mainly motivated by the coverage of these costs, with an invoice or an expense report as proof.&lt;/p&gt;
&lt;p&gt;Until now, no incidents have been reported, and participants respect the few safety and conduct rules that
are announced at the beginning of the evening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;: reach the registration limit&lt;/p&gt;
&lt;p&gt;This step is to be taken seriously a month before the event. It is about maximizing the visibility of the
event to reach the maximum capacity set by the venue hosting the event. To do this, I have had the opportunity
to use several methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Beeing as exhaustive as possible in the event description (date, location, schedule, program, sponsors,
visuals, etc.)&lt;/li&gt;
&lt;li&gt;Creating regular content on social networks to remind the date of the event and the registration modalities
(LinkedIn majority, Twitter in the past)&lt;/li&gt;
&lt;li&gt;Asking event participants to share the event on their social networks or their company networks&lt;/li&gt;
&lt;li&gt;Informing the French community with the &lt;code&gt;pgsql-fr-generale&lt;/code&gt; mailing list to reach a wider audience, or even
to inspire other groups to organize Meetups (who knows?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Visual identity&lt;/strong&gt; (optional)&lt;/p&gt;
&lt;p&gt;We worked with Yoann on the redesign of the Meetup group&amp;rsquo;s visual identity with a new logo and a new graphic
charter in the last few months. It&amp;rsquo;s cosmetic, but it allows us to give a more professional image and a strong
identity within the existing Lille communities. We hope to make a small place and a small notoriety in the local
landscape.&lt;/p&gt;
&lt;p&gt;We have delegated the creation of the event thumbnail twice, and I recommend working with a graphic designer
or a designer to get a professional and neat result. Until one of the members around me is qualified enough,
we do with the means at hand.&lt;/p&gt;
&lt;p&gt;Last but not least, and it was our little pride of the year, we have given ourselves a Slonik logo, the PostgreSQL
mascot. The creation process relied heavily on the new AI generation tools, and we were blown away by the quality
of the result. A big thank you to &lt;a href="https://www.instagram.com/_ekpyrosis/" target="_blank" rel="noopener"&gt;Isaac&lt;/a&gt; for his invaluable help in the
retouching and adjustments of the logo.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/en/2024-07-30-meetup-pg-lille-logo.png" alt="Meetup PG Lille Logo"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;During this conference, I thanked a lot of people. In particular, the members of the PostgreSQL France
association, for their trust they have given us, Matthieu Cornillon and myself, for accepting our application
file for the choice of the city of Lille this year 2024.&lt;/p&gt;
&lt;p&gt;One of the slides of my presentation was dedicated to all the companies that hosted our meetings, and who, without
knowing it, allowed me to adjust the format and content of our Meetups. A thank you to them for their sympathy
and warm welcome. It was also an opportunity to highlight the speakers who agreed to share their experience and
expertise during the PostgreSQL Lille Meetups. A big thank you to them for living the adventure with me!&lt;/p&gt;
&lt;p&gt;After the conference, I felt that feedback had a positive impact on the audience, with a few people coming to
me to congratulate me on the work done and inform me of their intention to organize a Meetup in their own city.
It was a nice reward for me, and it made me want to continue my commitment and set an example for the promotion
of PostgreSQL in France.&lt;/p&gt;</description></item><item><title>On the road to freedom with db_migrator</title><link>https://fljd.in/en/2023/07/28/on-the-road-to-freedom-with-db_migrator/</link><pubDate>Fri, 28 Jul 2023 00:00:00 +0000</pubDate><author>Florent Jardin</author><guid>https://fljd.in/en/2023/07/28/on-the-road-to-freedom-with-db_migrator/</guid><description>&lt;p&gt;Over the past months, I have spent several weeks contributing to the
&lt;a href="https://github.com/cybertec-postgresql/db_migrator" target="_blank" rel="noopener"&gt;db_migrator&lt;/a&gt; extension. Written solely in PL/pgSQL, it enables the migration of
schemas and data from a database system to PostgreSQL using the external data I
had presented &lt;a href="https://fljd-in.translate.goog/2021/07/16/parlons-un-peu-des-donnees-externes/?_x_tr_sl=fr&amp;amp;_x_tr_tl=en&amp;amp;_x_tr_hl=fr&amp;amp;_x_tr_pto=wapp" target="_blank" rel="noopener"&gt;few years ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post, I present the functionality of the tool, its philosophy, and
the reason I found for its existence, even though it joins the ecosystem of
well-established open-source projects in the migration landscape. How does it
compare to &lt;a href="https://ora2pg.darold.net/" target="_blank" rel="noopener"&gt;Ora2Pg&lt;/a&gt; or &lt;a href="https://pgloader.io/" target="_blank" rel="noopener"&gt;pgloader&lt;/a&gt; in terms of value and capabilities?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="db_migrator-enters-the-arena"&gt;db_migrator enters the arena&lt;/h2&gt;
&lt;p&gt;My interest in this project dates back to last December when a &lt;a href="https://blog-dalibo-com.translate.goog/2022/12/21/depart_philippe.html?_x_tr_sl=fr&amp;amp;_x_tr_tl=en&amp;amp;_x_tr_hl=fr&amp;amp;_x_tr_pto=wapp" target="_blank" rel="noopener"&gt;colleague from
Dalibo&lt;/a&gt; left us a &lt;a href="https://github.com/dalibo/data2pg" target="_blank" rel="noopener"&gt;similar tool&lt;/a&gt;, which allowed copying data from Oracle
or Sybase instances using Foreign Data Wrappers (FDW) technology. Although this
tool remained in alpha, many good ideas were experimented with internally.&lt;/p&gt;
&lt;p&gt;The promise of FDWs lies in adhering to the SQL/MED standard, allowing a
PostgreSQL instance to interface with another storage system and manipulate its
data through external tables using simple SQL queries. Therefore, provided that
a community has developed the wrapper, it becomes possible to query a remote
catalog, replicate the structure of tables, their relationships, and
constraints, and &lt;a href="https://fljd-in.translate.goog/2021/12/06/migrer-vers-postgresql/?_x_tr_sl=en&amp;amp;_x_tr_tl=fr&amp;amp;_x_tr_hl=fr&amp;amp;_x_tr_pto=wapp" target="_blank" rel="noopener"&gt;retrieve data&lt;/a&gt; into PostgreSQL.&lt;/p&gt;
&lt;p&gt;And &lt;a href="https://github.com/cybertec-postgresql/db_migrator" target="_blank" rel="noopener"&gt;db_migrator&lt;/a&gt; enters the arena.&lt;/p&gt;
&lt;p&gt;Made public in November 2019 by Laurenz Albe, well-known for his active
contributions to PostgreSQL for decades and also for developing &lt;a href="https://github.com/laurenz/oracle_fdw" target="_blank" rel="noopener"&gt;oracle_fdw&lt;/a&gt;,
the extension presents itself as a generic tool where one must use &lt;em&gt;plugins&lt;/em&gt; for
FDW support. It is easy to create new plugins, as I discovered with the
&lt;a href="https://github.com/fljdin/mysql_migrator" target="_blank" rel="noopener"&gt;mysql_migrator&lt;/a&gt; plugin, written in just a few days, thanks to the comprehensive
documentation of the &lt;a href="https://github.com/cybertec-postgresql/db_migrator#plugin-api" target="_blank" rel="noopener"&gt;API for plugins&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After installing the extensions with &lt;code&gt;make install&lt;/code&gt; and the appropriate FDW for
the system, it is necessary to create the objects in the database that will hold
the future schemas and their data.&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;mysql_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;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;mysql_migrator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CASCADE&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="n"&gt;SERVER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mysql&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;mysql_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="k"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_db&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;fetch_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1000&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="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="k"&gt;PUBLIC&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;mysql&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;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;root&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;password&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;The migration process can be performed in a single command for the simplest
cases (no stored procedures or exotic column types) using the &lt;code&gt;db_migrate()&lt;/code&gt;
method. Otherwise, for more complex scenarios requiring adjustments such as
changing column types or removing a table in the target schema, the migration
may involve multiple steps.&lt;/p&gt;
&lt;p&gt;During the development of the &lt;code&gt;mysql_migration&lt;/code&gt; extension, I started with the
sample database &lt;a href="https://dev.mysql.com/doc/sakila/en/" target="_blank" rel="noopener"&gt;Sakila&lt;/a&gt; provided by MySQL to have comprehensive complexity.
The first step involves creating two internal schemas, one with external tables
provided by the plugin and the other with catalog tables that can be edited
before the extension continues the 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="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_prepare&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;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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 class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql&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 class="n"&gt;only_schemas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{sakila}&amp;#39;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This part can be relatively lengthy, as it involves retrieving the data model,
which I refer to as the catalog, in the form of several tables that describe the
structure of tables, column names, and associated constraints. The extension
also imports the sources of all stored procedures, functions, and views but does
not perform their conversion to PL/pgSQL (you cannot imagine the &lt;a href="https://blog-dalibo-com.translate.goog/2020/12/21/migration_oracle_vers_postgresql.html?_x_tr_sl=fr&amp;amp;_x_tr_tl=en&amp;amp;_x_tr_hl=fr&amp;amp;_x_tr_pto=wapp" target="_blank" rel="noopener"&gt;amount of work
involved&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For the migration of the Sakila database, several modifications to the catalog
are necessary. Like the rest of this extension, all the preparation is done in
SQL, making it easy to automate with a single script serving as configuration.&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="cm"&gt;/* exclude bytea columns from migration */&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;DELETE&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;pgsql_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type_name&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="s1"&gt;&amp;#39;bytea&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="cm"&gt;/* quote character expression */&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;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgsql_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&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;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default_value&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;quote_literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_value&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;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regexp_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^\-?[0-9]+$&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 class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default_value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;CURRENT_TIMESTAMP&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="cm"&gt;/* disable view migration */&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;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgsql_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;views&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="n"&gt;migrate&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;false&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;Of course, we could go further, such as reinjecting the definition of rewritten
views into the &lt;code&gt;pgsql_stage.views&lt;/code&gt; table or enabling the migration of procedures
by changing the &lt;code&gt;migrate&lt;/code&gt; column of the &lt;code&gt;pgsql_stage.functions&lt;/code&gt; table. However,
let&amp;rsquo;s proceed with the next step.&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;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_mkforeign&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;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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 class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql&amp;#39;&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&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;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_tables&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;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&amp;#39;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first method, &lt;code&gt;db_migrate_mkforeign()&lt;/code&gt;, is responsible for creating schemas
and sequences, followed by foreign tables with columns based on the previous
adjustments. Next comes the most crucial step, where we execute the function
&lt;code&gt;db_migrate_tables()&lt;/code&gt;: blank tables are created with their partitions if
necessary, and for each of them, the data copying begins using the &lt;code&gt;INSERT INTO SELECT *&lt;/code&gt; statement.&lt;/p&gt;
&lt;p&gt;Other objects, such as indexes or constraints, have their own methods. It is
necessary to create the functions before these objects if you encounter
functional indexes or other similar cases.&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;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_functions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_triggers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_views&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_indexes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_constraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mysql_migrator&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;div class="message"&gt;It is possible that this mechanism may change in the future, especially if I
manage to realize this &lt;a href="https://github.com/cybertec-postgresql/db_migrator/issues/26" target="_blank" rel="noopener"&gt;issue&lt;/a&gt;, which would allow breaking down the
&lt;code&gt;db_migrate_*()&lt;/code&gt; methods into smaller steps.&lt;/div&gt;
&lt;p&gt;The end of the migration process involves deleting the temporary schemas that
contained the catalog tables.&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;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_migrate_finish&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;hr&gt;
&lt;h2 id="one-more-migration-tool"&gt;One more migration tool&lt;/h2&gt;
&lt;p&gt;As I mentioned in the introduction, it is quite surprising to see a new
migration tool emerge in 2023 (the version 1.0.0 was &lt;a href="https://github.com/cybertec-postgresql/db_migrator/blob/master/CHANGELOG.md" target="_blank" rel="noopener"&gt;released in January&lt;/a&gt;
with my patch on partitioning). In the open-source landscape, we can mention
&lt;strong&gt;Ora2Pg&lt;/strong&gt;, which released its &lt;a href="https://github.com/darold/ora2pg/releases/tag/v24.0" target="_blank" rel="noopener"&gt;version 24.0&lt;/a&gt; in July with SQL Server
support, and &lt;strong&gt;pgloader&lt;/strong&gt;, which has an excellent reputation.&lt;/p&gt;
&lt;p&gt;A vast number of projects are listed on the &lt;a href="https://wiki.postgresql.org/wiki/Converting_from_other_Databases_to_PostgreSQL" target="_blank" rel="noopener"&gt;community wiki&lt;/a&gt;. Some are
specialized for a single system, while others support migration for multiple
systems. The majority of these projects are either proprietary or lack recent
contributions. Many of them are black boxes, and their documentation may appear
cryptic or almost non-existent.&lt;/p&gt;
&lt;p&gt;The ecosystem is rich, and I do not claim to know all of its aspects, but I have
had an intuition that I have been forming over the past few years. The global
economy is in a state of turmoil. Some companies are doing well, while others
are making budget cuts. The transition to a free and non-commercially licensed
system like PostgreSQL remains relevant, perhaps even more urgent today compared
to the past decade.&lt;/p&gt;
&lt;p&gt;And yet, with my DBA perspective, I am not fully satisfied with the existing
tools. I wish for a new alternative, something universal and accessible to
everyone. If I turn to &lt;strong&gt;db_migrator&lt;/strong&gt; today, it would be for the following main
advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A low-level implementation close to the instance: using PL/pgSQL as the
exclusive language. This would not have been possible without the prolific
development of &lt;a href="https://wiki.postgresql.org/wiki/Foreign_data_wrappers" target="_blank" rel="noopener"&gt;Foreign Data Wrappers&lt;/a&gt; for a wide range of systems;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A high level of configuration flexibility: adjustments are made with &lt;code&gt;UPDATE&lt;/code&gt;
or &lt;code&gt;DELETE&lt;/code&gt; queries on the catalog. Once one is familiar with the model of the
catalog, it becomes easy to change behavior without consulting technical
documentation on the available options;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Freedom in orchestration: currently, executions are triggered sequentially for
indexes and constraints, but the tool&amp;rsquo;s architecture could allow external
tools to consume the extension&amp;rsquo;s results and trigger operations in parallel;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Plugins have the freedom to enrich migration: if an operation is not generic,
it is entirely possible to provide an additional method through the plugin.
For example, the incremental copy (and its &lt;a href="https://github.com/cybertec-postgresql/ora_migrator#replication-functions" target="_blank" rel="noopener"&gt;replication functions&lt;/a&gt;) in the
&lt;strong&gt;ora_migrator&lt;/strong&gt; plugin or the conversion of auto-increments to identity
columns with the &lt;strong&gt;mysql_migrator&lt;/strong&gt; plugin.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The road to freedom still seems long to achieve half of what Ora2Pg already
offers, especially when it comes to automatic conversion, which is not on the
agenda at all. But with small, regular, and thoughtful advancements, who knows?&lt;/p&gt;</description></item></channel></rss>