summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/database/migration_helpers_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/database/migration_helpers_spec.rb')
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb190
1 files changed, 190 insertions, 0 deletions
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index a763dc08b73..6b709cba5b3 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::MigrationHelpers do
+ include Database::TableSchemaHelpers
+
let(:model) do
ActiveRecord::Migration.new.extend(described_class)
end
@@ -96,6 +98,131 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
+ describe '#create_table_with_constraints' do
+ let(:table_name) { :test_table }
+ let(:column_attributes) do
+ [
+ { name: 'id', sql_type: 'bigint', null: false, default: nil },
+ { name: 'created_at', sql_type: 'timestamp with time zone', null: false, default: nil },
+ { name: 'updated_at', sql_type: 'timestamp with time zone', null: false, default: nil },
+ { name: 'some_id', sql_type: 'integer', null: false, default: nil },
+ { name: 'active', sql_type: 'boolean', null: false, default: 'true' },
+ { name: 'name', sql_type: 'text', null: true, default: nil }
+ ]
+ end
+
+ before do
+ allow(model).to receive(:transaction_open?).and_return(true)
+ end
+
+ context 'when no check constraints are defined' do
+ it 'creates the table as expected' do
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+ end
+
+ expect_table_columns_to_match(column_attributes, table_name)
+ end
+ end
+
+ context 'when check constraints are defined' do
+ context 'when the text_limit is explicity named' do
+ it 'creates the table as expected' do
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+
+ t.text_limit :name, 255, name: 'check_name_length'
+ t.check_constraint :some_id_is_positive, 'some_id > 0'
+ end
+
+ expect_table_columns_to_match(column_attributes, table_name)
+
+ expect_check_constraint(table_name, 'check_name_length', 'char_length(name) <= 255')
+ expect_check_constraint(table_name, 'some_id_is_positive', 'some_id > 0')
+ end
+ end
+
+ context 'when the text_limit is not named' do
+ it 'creates the table as expected, naming the text limit' do
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+
+ t.text_limit :name, 255
+ t.check_constraint :some_id_is_positive, 'some_id > 0'
+ end
+
+ expect_table_columns_to_match(column_attributes, table_name)
+
+ expect_check_constraint(table_name, 'check_cda6f69506', 'char_length(name) <= 255')
+ expect_check_constraint(table_name, 'some_id_is_positive', 'some_id > 0')
+ end
+ end
+
+ it 'runs the change within a with_lock_retries' do
+ expect(model).to receive(:with_lock_retries).ordered.and_yield
+ expect(model).to receive(:create_table).ordered.and_call_original
+ expect(model).to receive(:execute).with(<<~SQL).ordered
+ ALTER TABLE "#{table_name}"\nADD CONSTRAINT "check_cda6f69506" CHECK (char_length("name") <= 255)
+ SQL
+
+ model.create_table_with_constraints table_name do |t|
+ t.text :name
+ t.text_limit :name, 255
+ end
+ end
+
+ context 'when constraints are given invalid names' do
+ let(:expected_max_length) { described_class::MAX_IDENTIFIER_NAME_LENGTH }
+ let(:expected_error_message) { "The maximum allowed constraint name is #{expected_max_length} characters" }
+
+ context 'when the explicit text limit name is not valid' do
+ it 'raises an error' do
+ too_long_length = expected_max_length + 1
+
+ expect do
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+
+ t.text_limit :name, 255, name: ('a' * too_long_length)
+ t.check_constraint :some_id_is_positive, 'some_id > 0'
+ end
+ end.to raise_error(expected_error_message)
+ end
+ end
+
+ context 'when a check constraint name is not valid' do
+ it 'raises an error' do
+ too_long_length = expected_max_length + 1
+
+ expect do
+ model.create_table_with_constraints table_name do |t|
+ t.timestamps_with_timezone
+ t.integer :some_id, null: false
+ t.boolean :active, null: false, default: true
+ t.text :name
+
+ t.text_limit :name, 255
+ t.check_constraint ('a' * too_long_length), 'some_id > 0'
+ end
+ end.to raise_error(expected_error_message)
+ end
+ end
+ end
+ end
+ end
+
describe '#add_concurrent_index' do
context 'outside a transaction' do
before do
@@ -1548,6 +1675,69 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
+ describe '#initialize_conversion_of_integer_to_bigint' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:issue) { create(:issue, project: project) }
+ let!(:event) do
+ create(:event, :created, project: project, target: issue, author: user)
+ end
+
+ context 'in a transaction' do
+ it 'raises RuntimeError' do
+ allow(model).to receive(:transaction_open?).and_return(true)
+
+ expect { model.initialize_conversion_of_integer_to_bigint(:events, :id) }
+ .to raise_error(RuntimeError)
+ end
+ end
+
+ context 'outside a transaction' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
+ it 'creates a bigint column and starts backfilling it' do
+ expect(model)
+ .to receive(:add_column)
+ .with(
+ :events,
+ 'id_convert_to_bigint',
+ :bigint,
+ default: 0,
+ null: false
+ )
+
+ expect(model)
+ .to receive(:install_rename_triggers)
+ .with(:events, :id, 'id_convert_to_bigint')
+
+ expect(model).to receive(:queue_background_migration_jobs_by_range_at_intervals).and_call_original
+
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_in)
+ .ordered
+ .with(
+ 2.minutes,
+ 'CopyColumnUsingBackgroundMigrationJob',
+ [event.id, event.id, :events, :id, :id, 'id_convert_to_bigint', 100]
+ )
+
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:steal)
+ .ordered
+ .with('CopyColumnUsingBackgroundMigrationJob')
+
+ model.initialize_conversion_of_integer_to_bigint(
+ :events,
+ :id,
+ batch_size: 300,
+ sub_batch_size: 100
+ )
+ end
+ end
+ end
+
describe '#index_exists_by_name?' do
it 'returns true if an index exists' do
ActiveRecord::Base.connection.execute(