Commit a45cd3cc authored by Andreas Brandl's avatar Andreas Brandl

More verbose error message when creating extension

Creating a PG extension requires superuser privileges. We now fail with
a more verbose error message explaining the background.

Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/243729
parent b76ff978
# frozen_string_literal: true # frozen_string_literal: true
class EnableBtreeGistExtension < ActiveRecord::Migration[6.0] class EnableBtreeGistExtension < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false DOWNTIME = false
def up def up
execute 'CREATE EXTENSION IF NOT EXISTS btree_gist' create_extension :btree_gist
end end
def down def down
execute 'DROP EXTENSION IF EXISTS btree_gist' drop_extension :btree_gist
end end
end end
...@@ -1212,6 +1212,63 @@ into similar problems in the future (e.g. when new tables are created). ...@@ -1212,6 +1212,63 @@ into similar problems in the future (e.g. when new tables are created).
) )
end end
def create_extension(extension)
execute('CREATE EXTENSION IF NOT EXISTS %s' % extension)
rescue ActiveRecord::StatementInvalid => e
dbname = Database.database_name
user = Database.username
warn(<<~MSG) if e.to_s =~ /permission denied/
GitLab requires the PostgreSQL extension '#{extension}' installed in database '#{dbname}', but
the database user is not allowed to install the extension.
You can either install the extension manually using a database superuser:
CREATE EXTENSION IF NOT EXISTS #{extension}
Or, you can solve this by logging in to the GitLab
database (#{dbname}) using a superuser and running:
ALTER #{user} WITH SUPERUSER
This query will grant the user superuser permissions, ensuring any database extensions
can be installed through migrations.
For more information please refer to doc/install/postgresql_extensions.md.
MSG
raise
end
def drop_extension(extension)
execute('DROP EXTENSION IF EXISTS %s' % extension)
rescue ActiveRecord::StatementInvalid => e
dbname = Database.database_name
user = Database.username
warn(<<~MSG) if e.to_s =~ /permission denied/
This migration attempts to drop the PostgreSQL extension '#{extension}'
installed in database '#{dbname}', but the database user is not allowed
to drop the extension.
You can either drop the extension manually using a database superuser:
DROP EXTENSION IF EXISTS #{extension}
Or, you can solve this by logging in to the GitLab
database (#{dbname}) using a superuser and running:
ALTER #{user} WITH SUPERUSER
This query will grant the user superuser permissions, ensuring any database extensions
can be dropped through migrations.
For more information please refer to doc/install/postgresql_extensions.md.
MSG
raise
end
private private
def validate_check_constraint_name!(constraint_name) def validate_check_constraint_name!(constraint_name)
......
...@@ -2329,4 +2329,56 @@ RSpec.describe Gitlab::Database::MigrationHelpers do ...@@ -2329,4 +2329,56 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end end
end end
end end
describe '#create_extension' do
subject { model.create_extension(extension) }
let(:extension) { :btree_gist }
it 'executes CREATE EXTENSION statement' do
expect(model).to receive(:execute).with(/CREATE EXTENSION IF NOT EXISTS #{extension}/)
subject
end
context 'without proper permissions' do
before do
allow(model).to receive(:execute).with(/CREATE EXTENSION IF NOT EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
end
it 'raises the exception' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
end
it 'prints an error message' do
expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error
end
end
end
describe '#drop_extension' do
subject { model.drop_extension(extension) }
let(:extension) { 'btree_gist' }
it 'executes CREATE EXTENSION statement' do
expect(model).to receive(:execute).with(/DROP EXTENSION IF EXISTS #{extension}/)
subject
end
context 'without proper permissions' do
before do
allow(model).to receive(:execute).with(/DROP EXTENSION IF EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
end
it 'raises the exception' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
end
it 'prints an error message' do
expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment