Run postgres in a docker container and get a shell into it.
docker pull postgres
docker run -d --name ext-demo --hostname ext-demo -e POSTGRES_HOST_AUTH_METHOD=trust -p 127.0.0.1:5432:5432 postgres
docker exec ext-demo /bin/bash -c "apt-get update && apt-get install -y vim"
docker exec -it ext-demo /bin/bash- a
.controlfile - one or more
.sqlfiles - [optionally] one or more shared libaries
The .control file and the .sql files go in the extension directory.
You can use the pg_config tool to find this directory.
It is nested under the sharedir.
pg_config --sharedir
#/usr/share/postgresql/17
cd `pg_config --sharedir`/extension
pwd
#/usr/share/postgresql/17/extension
lsCreate a src directory for us to work in
cd ~
mkdir src
cd srcPut the following in hello--1.0.0.sql. Our extension will define a single function named "hello".
NOTE: the double-dashes are important! A single dash won't work.
create or replace function hello(person text) returns text
as $func$
begin
return format('hello %s', person);
end
$func$ language plpgsql;Now, we need a .control file. Put the following in hello.control.
comment='the hello extension'
default_version='1.0.0'
To make our extension available in the database, we simply copy the .sql and .control files to the extension directory.
cp ~/src/* `pg_config --sharedir`/extension/Now, it should be available in our database. Let's check
psql -U postgres -c "select * from pg_available_extensions where name = 'hello';"We should see that the hello extension is available with a default version of 1.0.0 but is not installed.
name | default_version | installed_version | comment
-------+-----------------+-------------------+---------------------
hello | 1.0.0 | | the hello extension
(1 row)
Get a psql shell
psql -U postgresRun these commands in the shell
create extension hello;
select * from pg_available_extensions where name = 'hello';
select * from pg_extension where extname = 'hello';
\dx+ hello
select hello('bob');Put the following in hello--2.0.0.sql.
create or replace function add_forty_two(num int) returns int
as $func$
select num + 42
$func$ language sql;Alter hello.control. Change the default version to 2.0.0
comment='the hello extension'
default_version='2.0.0'
Copy the files into the extension directory
cp ~/src/* `pg_config --sharedir`/extension/Let's see what versions postgres thinks are available
psql -U postgres -c "select * from pg_available_extension_versions where name = 'hello';" name | version | installed | superuser | trusted | relocatable | schema | requires | comment
-------+---------+-----------+-----------+---------+-------------+--------+----------+---------------------
hello | 1.0.0 | t | t | f | f | | | the hello extension
hello | 2.0.0 | f | t | f | f | | | the hello extension
(2 rows)
Let's update to the 2.0.0 version.
psql -U postgres -c "alter extension hello update;"ERROR: extension "hello" has no update path from version "1.0.0" to version "2.0.0"
Uhoh. We can't update to 2.0.0.
Well, let's try installing 2.0.0 directly.
drop extension hello;
create extension hello with version '2.0.0';
\dx+ hello Objects in extension "hello"
Object description
---------------------------------
function add_forty_two(integer)
(1 row)
Our new add_forty_two function is there, but what happened to our hello function?
Installing version 2.0.0 ONLY runs the hello--2.0.0.sql file. It doesn't also run the hello--1.0.0.sql file. If we want to install directly into 2.0.0 via the hello--2.0.0.sql file, it will need to include everything from version 1.0.0 explicitly.
There's no magic to make updates automatic. We have to explicitly tell postgres how to get from one version of the extension to the next. To do that, we provide another SQL file.
An update path is defined by a SQL file that has two versions in the file name. The first is the prior version, and the second is the target version. Postgres will parse these versions out of the file name to determine what update paths are supported.
Let's rename hello--2.0.0.sql to hello--1.0.0--2.0.0.sql. Again, the double-dashes are important. Single dashes will not work. Then, let's replace our files in the postgres extension directory again.
mv hello--2.0.0.sql hello--1.0.0--2.0.0.sql
rm `pg_config --sharedir`/extension/hello*
cp ~/src/* `pg_config --sharedir`/extension/Now, ask postgres what update paths it detects.
psql -U postgres -c "select * from pg_extension_update_paths('hello');" source | target | path
--------+--------+--------------
1.0.0 | 2.0.0 | 1.0.0--2.0.0
2.0.0 | 1.0.0 |
(2 rows)
Let's try it out
drop extension hello;
create extension hello with version '1.0.0';
alter extension hello update;
\dx hello
\dx+ hello List of installed extensions
Name | Version | Schema | Description
-------+---------+--------+---------------------
hello | 2.0.0 | public | the hello extension
(1 row)
Objects in extension "hello"
Object description
---------------------------------
function add_forty_two(integer)
function hello(text)
(2 rows)
Ah! We were able to install version 1.0.0, update to 2.0.0, and now the extension has both functions.
If we want to install version 2.0.0 without installing 1.0.0 first, postgres can do that. With the files we have, it will run the 1.0.0 file and then the upgrade path file to get to 2.0.0.
drop extension hello;
create extension hello with version '2.0.0';
\dx hello
\dx+ hello List of installed extensions
Name | Version | Schema | Description
-------+---------+--------+---------------------
hello | 2.0.0 | public | the hello extension
(1 row)
Objects in extension "hello"
Object description
---------------------------------
function add_forty_two(integer)
function hello(text)
(2 rows)
We can create a hello--2.0.0.sql file that works as expected, too. It needs to be cumulative though.
cat hello--1.0.0.sql > hello--2.0.0.sql
cat hello--1.0.0--2.0.0.sql >> hello--2.0.0.sql
cp ~/src/* `pg_config --sharedir`/extension/
ls `pg_config --sharedir`/extension/hello*/usr/share/postgresql/17/extension/hello--1.0.0--2.0.0.sql
/usr/share/postgresql/17/extension/hello--1.0.0.sql
/usr/share/postgresql/17/extension/hello--2.0.0.sql
/usr/share/postgresql/17/extension/hello.control
Now, when installing 2.0.0 without updating from 1.0.0, postgres will just execute the hello--2.0.0.sql file.
Enjoy!
