Skip to content

Add optional VERSION clause to CREATE EXTENSION - #1654

Open
aba-malu wants to merge 11 commits into
puppetlabs:mainfrom
abacusresearch:feature/create-extension-version
Open

aba-malu wants to merge 11 commits into
puppetlabs:mainfrom
abacusresearch:feature/create-extension-version

Conversation

@aba-malu

@aba-malu aba-malu commented Dec 17, 2025 •

Copy link
Copy Markdown

Summary

This pull request adds support for specifying an explicit extension version when using CREATE EXTENSION. Allowing users to control which extension version is installed instead of relying on the default.

Additional Context

I recently encountered this issue with TimescaleDB 2.23.1 and PostgreSQL 18 on Debian 12 using the official TimescaleDB APT repository. The package timescaledb-2-loader-postgresql-18 (dependency of timescaledb-2-2.23.1-postgresql-18) sets the default to 2.24.0 in /usr/share/postgresql/18/extension/timescaledb.control. When creating a new database, Puppet wants to create the extension with CREATE EXTENSION "timescaledb" ;. In the next step ALTER EXTENSION "timescaledb" UPDATE TO "2.23.1" ; is executed and fails with following error:

Error: Error executing SQL; psql returned pid 115701 exit 1: 'ERROR:  extension "timescaledb" has no installation script nor update path for version "2.24.0"
'
Error: /Stage[main]/Profile::Postgresql/Postgresql::Server::Extension[timescaledb]/Postgresql_psql[zabbix: CREATE EXTENSION "timescaledb"]/command: change from 'notrun' to 'CREATE EXTENSION "timescaledb"' failed: Error executing SQL; psql returned pid 115701 exit 1: 'ERROR:  extension "timescaledb" has no installation script nor update path for version "2.24.0"
' (corrective)
Error: Error executing SQL; psql returned pid 115705 exit 1: 'ERROR:  extension "timescaledb" does not exist
'
Error: /Stage[main]/Profile::Postgresql/Postgresql::Server::Extension[timescaledb]/Postgresql_psql[zabbix: ALTER EXTENSION "timescaledb" UPDATE TO '2.23.1']/command: change from 'notrun' to 'ALTER EXTENSION "timescaledb" UPDATE TO \'2.23.1\'' failed: Error executing SQL; psql returned pid 115705 exit 1: 'ERROR:  extension "timescaledb" does not exist
' (corrective)

If no version is specified, the default from the file /usr/share/postgresql/18/extension/timescaledb.control is used. Since the “version” is already implemented, I would like to use it in CREATE as well.

Related Issues (if any)

Mention any related issues or pull requests.

Checklist

  • 🟢 Spec tests.
  • 🟢 Acceptance tests.
  • Manually verified. (For example puppet apply)

@CLAassistant

CLAassistant commented Dec 17, 2025 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@aba-malu
aba-malu force-pushed the feature/create-extension-version branch 2 times, most recently from 4f90186 to 5d39ac5 Compare May 19, 2026 07:33

@smortex smortex left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some unit test that ensure this feature does not break in the future would be cool. Other that that, that feels good to me.

Comment thread manifests/server/extension.pp Outdated
case $ensure {
'present': {
$command = "CREATE EXTENSION \"${extension}\""
$command = "CREATE EXTENSION \"${extension}\"${if $version and $version != 'latest' { " VERSION \"${version}\"" } else { '' }}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the code in the ${} confuse the linter. While this is syntactically correct, that is quite uncommon and I would rather avoid this.

Switching to inline epp would be a valid alternative, something like (untested):

Suggested change
$command = "CREATE EXTENSION \"${extension}\"${if $version and $version != 'latest' { " VERSION \"${version}\"" } else { '' }}"
$command = inline_epp('CREATE EXTENSION "<%= $extension %>"<% if $version and $version != 'latest' { %> VERSION "<%= $version %>"<% } %>', { extension => $extension, version => $version })

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works as expected, I just had to replace the quotation marks for 'latest' , but it works.

      $command = inline_epp('CREATE EXTENSION "<%= $extension %>"<% if $version and $version != "latest" { %> VERSION "<%= $version %>"<% } %>', { extension => $extension, version => $version })

My output:

...
Notice: /Stage[main]/Profile::Postgresql/Postgresql::Server::Grant[monitoring_execute_pg_ls_waldir]/Postgresql_psql[grant:monitoring_execute_pg_ls_waldir]/command: command changed 'notrun' to 'GRANT EXECUTE ON FUNCTION pg_ls_waldir() TO "monitoring"'
Notice: /Stage[main]/Zabbix::Database/Postgresql::Server::Db[zabbix]/Postgresql::Server::Role[zabbix]/Postgresql_psql[CREATE ROLE zabbix ENCRYPTED PASSWORD ****]/command: changed [redacted] to [redacted]
Notice: /Stage[main]/Zabbix::Database/Postgresql::Server::Db[zabbix]/Postgresql::Server::Database[zabbix]/Postgresql_psql[CREATE DATABASE "zabbix"]/command: command changed 'notrun' to 'CREATE DATABASE "zabbix" WITH TEMPLATE = "template0" ENCODING = \'UTF-8\' LC_COLLATE = \'en_US.UTF-8\' LC_CTYPE = \'en_US.UTF-8\' '
Info: /Stage[main]/Zabbix::Database/Postgresql::Server::Db[zabbix]/Postgresql::Server::Database[zabbix]/Postgresql_psql[CREATE DATABASE "zabbix"]: Scheduling refresh of Postgresql_psql[REVOKE CONNECT ON DATABASE "zabbix" FROM public]
Notice: /Stage[main]/Zabbix::Database/Postgresql::Server::Db[zabbix]/Postgresql::Server::Database[zabbix]/Postgresql_psql[REVOKE CONNECT ON DATABASE "zabbix" FROM public]: Triggered 'refresh' from 1 event
Notice: /Stage[main]/Zabbix::Database/Postgresql::Server::Db[zabbix]/Postgresql::Server::Database[zabbix]/Postgresql_psql[ALTER DATABASE "zabbix" OWNER TO "zabbix"]/command: command changed 'notrun' to 'ALTER DATABASE "zabbix" OWNER TO "zabbix"'
Notice: /Stage[main]/Profile::Postgresql/Postgresql::Server::Extension[timescaledb]/Postgresql_psql[zabbix: CREATE EXTENSION "timescaledb" VERSION "2.23.1"]/command: command changed 'notrun' to 'CREATE EXTENSION "timescaledb" VERSION "2.23.1"'
Notice: /Stage[main]/Profile::Postgresql/Postgresql::Server::Extension[pg_repack]/Postgresql_psql[zabbix: CREATE EXTENSION "pg_repack" VERSION "1.5.3"]/command: command changed 'notrun' to 'CREATE EXTENSION "pg_repack" VERSION "1.5.3"'
...

@aba-malu
aba-malu requested a review from smortex May 22, 2026 09:08
@dasbaum-ch

Copy link
Copy Markdown

/ping

@aba-malu

Copy link
Copy Markdown
Author

I tried my best to implement spec/acceptance/extension_spec.rb and spec/defines/server/extension_spec.rb.

@jst-cyr

jst-cyr commented Sep 30, 2026

Copy link
Copy Markdown
Member

@aba-malu : Thanks for the updates! I've approved the tests to run so that you can see the CI feedback. It looks like there are some RuboCop failures to address.

@aba-malu

aba-malu commented Oct 1, 2026

Copy link
Copy Markdown
Author

@jst-cyr : Thanks, when I run bundle exec rubocop, I no longer see any errors (locally).

@jst-cyr

jst-cyr commented Oct 1, 2026

Copy link
Copy Markdown
Member

I approved the CI to run and we have new results: https://github.com/puppetlabs/puppetlabs-postgresql/actions/runs/36817873546?pr=1654

The good news is that the Rubocop issues appear to be resolved!

The bad news is that we appear to have metadata that is not allowing for the latest releases of dependencies to be pulled down (stdlib, concat, systemd).

I don't see these errors in main, so I think your branch needs a rebase to pick up the fixes from the main branch.

@aba-malu
aba-malu force-pushed the feature/create-extension-version branch from f93ca35 to fe1cde6 Compare October 2, 2026 05:34
@aba-malu

aba-malu commented Oct 2, 2026

Copy link
Copy Markdown
Author

Oh, yeah my PR was with v10.6.0. Now I have rebased my branch.

@jst-cyr

jst-cyr commented Oct 2, 2026

Copy link
Copy Markdown
Member

Thank you @aba-malu ! The spec tests look good. I'll kick off some acceptance testing so that we can get that initial feedback done.

@jst-cyr

jst-cyr commented Oct 2, 2026

Copy link
Copy Markdown
Member

Running CI tests for this PR here: #1711

@aba-malu

aba-malu commented Oct 5, 2026

Copy link
Copy Markdown
Author

Good morning @jst-cyr, could it be, that I should split the apply_manifest in two calls? Like:

    apply_manifest(pp, catch_failures: true)
    expect(psql("-tA --command=\"SELECT extversion FROM pg_extension WHERE extname = '#{ext}'\" ext_vtest", 'postgres').stdout)
      .to include(ver)
    apply_manifest(pp, catch_changes: true)
  end
end

@jst-cyr

jst-cyr commented Oct 5, 2026

Copy link
Copy Markdown
Member

Acceptance Test Results: https://github.com/puppetlabs/puppetlabs-postgresql/actions/runs/37061237668?pr=1711

Full red across the board. Some are expected (Debian 11 and Red Hat 7) but other failures appear to be caused by the changes in this PR.

Initial inspection sees two errors, at least:

  1. apply_manifest is rejecting having both catch_failures and expect_changes being passed
  2. spec/acceptance/extension_spec.rb needs postgresql-contrib, but that package seems to be missing on several platforms. If the tests don't need to excercise that path, might be worth revisiting the test. Otherwise, the requirements for the test need to be updated to ensure the needed packages are there.

@aba-malu : To your question, I think you should still use one apply_manifest, you just can't pass two params in. Using catch_changes: true is probably the one you want.

@aba-malu

aba-malu commented Oct 5, 2026

Copy link
Copy Markdown
Author

Now I need help from an AI :( If I understand the reports right, I choose a contrib extension (with i now have in setup_pp) which should be available and test the code with it.
I hope my last commit will make the pipeline green.

@aba-malu

aba-malu commented Oct 5, 2026

Copy link
Copy Markdown
Author

Changed to extensions from docs https://www.postgresql.org/docs/current/contrib.html.

@jst-cyr

jst-cyr commented Oct 5, 2026

Copy link
Copy Markdown
Member

Thank you @aba-malu ! CI is looking good, I'll kick off acceptance tests next.

@jst-cyr

jst-cyr commented Oct 5, 2026

Copy link
Copy Markdown
Member

Running CI for this PR here: #1712

@jst-cyr

jst-cyr commented Oct 5, 2026

Copy link
Copy Markdown
Member

Acceptance Test Results: https://github.com/puppetlabs/puppetlabs-postgresql/actions/runs/37334103392?pr=1712

Full red across the board, regression from prevous tests. Spot-checking across a few platforms seems to show a similar error popping up:

Error: Evaluation Error: Error while evaluating a Resource Statement, Postgresql::Server::Extension[hstore|1.6_v]: parameter 'version' expects a value of type Undef or String[1], got String (file: /root/manifest_20261005_5040_jh1fhk.pp, line: 1)

@aba-malu

aba-malu commented Oct 6, 2026

Copy link
Copy Markdown
Author

I try to split the output extension name and default_version with :.

postgres=# SELECT name, default_version FROM pg_available_extensions WHERE name IN ('unaccent', 'uuid-ossp', 'pgcrypto', 'hstore') ORDER BY name LIMIT 1;
  name  | default_version 
--------+-----------------
 hstore | 1.8
(1 row)

postgres=# SELECT name || ':' || default_version FROM pg_available_extensions;
        ?column?         
-------------------------
 hstore:1.8
 cube:1.5
 xml2:1.2

I hope the part with out.split(';', 2) is correct

 describe 'postgresql::server::extension' do
   # Choose a contrib extension that exists on this distro
   def discover_extension
-    q = 'SELECT name, default_version FROM pg_available_extensions ' \
+    q = 'SELECT name || \':\' || default_version FROM pg_available_extensions ' \
         "WHERE name IN ('unaccent', 'uuid-ossp', 'pgcrypto', 'hstore') ORDER BY name LIMIT 1"
-    ext, ver = psql("-At --command=\"#{q}\" postgres", 'postgres').stdout.strip.split(%r{\s+})
-    raise 'no contrib extension available on this platform' if ext.nil?
+    out = psql("-At --command=\"#{q}\" postgres", 'postgres').stdout.strip
+    raise 'no contrib extension available on this platform' if out.empty?
+
+    ext, ver = out.split(':', 2).map(&:strip)
+    raise "malformed discover_extension output: #{out.inspect}" if ext.nil? || ext.empty? || ver.nil? || ver.empty?
 
     [ext, ver]
   end

@jst-cyr

jst-cyr commented Oct 7, 2026

Copy link
Copy Markdown
Member

Thanks @aba-malu ! I'll kick off some more acceptance tests and let's see if this approach yields better results cross-platform.

@jst-cyr

jst-cyr commented Oct 7, 2026

Copy link
Copy Markdown
Member

Running CI tests for this PR here: #1713

@jst-cyr

jst-cyr commented Oct 8, 2026

Copy link
Copy Markdown
Member

Acceptance Test Results: https://github.com/puppetlabs/puppetlabs-postgresql/actions/runs/37665536933?pr=1713

Full failure across all platforms. Red Hat 9 seemed to have an infra issue, but on the spot checks I made there is a consistent theme:

Error: Could not find resource 'Postgresql::Server::Database[ext_vtest]' in parameter 'require' (file: /etc/puppetlabs/code/environments/production/modules/postgresql/manifests/server/extension.pp, line: 97) on node bd6dcf21556b

Ref 1654#issuecomment-6060338754
@aba-malu

aba-malu commented Oct 8, 2026

Copy link
Copy Markdown
Author

New day, new try.
Sorry... this is my first try with Puppet/Ruby.

@jst-cyr

jst-cyr commented Oct 8, 2026

Copy link
Copy Markdown
Member

@aba-malu : No need to apologize! Thank you so much for trying to make the module better. We'll get there eventually!
I've approved the CI to run and will kick off acceptance testing again, assuming that CI passes.

@jst-cyr

jst-cyr commented Oct 8, 2026

Copy link
Copy Markdown
Member

Running CI tests for this PR here: #1714

@jst-cyr

jst-cyr commented Oct 8, 2026

Copy link
Copy Markdown
Member

Acceptance Test Results: https://github.com/puppetlabs/puppetlabs-postgresql/actions/runs/37832984393?pr=1714

Full green! No errors, this is good to go forward for engineering review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants