diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-18 13:50:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-18 13:50:51 +0300 |
commit | db384e6b19af03b4c3c82a5760d83a3fd79f7982 (patch) | |
tree | 34beaef37df5f47ccbcf5729d7583aae093cffa0 /gems/gitlab-schema-validation | |
parent | 54fd7b1bad233e3944434da91d257fa7f63c3996 (diff) |
Add latest changes from gitlab-org/gitlab@16-3-stable-eev16.3.0-rc42
Diffstat (limited to 'gems/gitlab-schema-validation')
15 files changed, 268 insertions, 9 deletions
diff --git a/gems/gitlab-schema-validation/Gemfile.lock b/gems/gitlab-schema-validation/Gemfile.lock index 5ad804d3660..124e03422e9 100644 --- a/gems/gitlab-schema-validation/Gemfile.lock +++ b/gems/gitlab-schema-validation/Gemfile.lock @@ -40,6 +40,7 @@ GEM parser (3.2.2.3) ast (~> 2.4.1) racc + pg (1.5.3) pg_query (4.2.1) google-protobuf (>= 3.22.3) proc_to_ast (0.1.0) @@ -126,6 +127,7 @@ PLATFORMS DEPENDENCIES gitlab-schema-validation! gitlab-styles (~> 10.1.0) + pg (~> 1.5.3) pry rspec (~> 3.0) rspec-benchmark (~> 0.6.0) diff --git a/gems/gitlab-schema-validation/gitlab-schema-validation.gemspec b/gems/gitlab-schema-validation/gitlab-schema-validation.gemspec index 47ca8b65b5d..513035dac4b 100644 --- a/gems/gitlab-schema-validation/gitlab-schema-validation.gemspec +++ b/gems/gitlab-schema-validation/gitlab-schema-validation.gemspec @@ -23,6 +23,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency "pg_query" spec.add_development_dependency "gitlab-styles", "~> 10.1.0" + spec.add_development_dependency "pg", "~> 1.5.3" spec.add_development_dependency "pry" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rspec-benchmark", "~> 0.6.0" diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation.rb index 5211358a197..98da37cf16b 100644 --- a/gems/gitlab-schema-validation/lib/gitlab/schema/validation.rb +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation.rb @@ -28,8 +28,12 @@ require_relative 'validation/validators/different_definition_triggers' require_relative 'validation/validators/extra_triggers' require_relative 'validation/validators/missing_triggers' +require_relative 'validation/sources/connection_adapters/base' +require_relative 'validation/sources/connection_adapters/active_record_adapter' +require_relative 'validation/sources/connection_adapters/pg_adapter' require_relative 'validation/sources/structure_sql' require_relative 'validation/sources/database' +require_relative 'validation/sources/connection' require_relative 'validation/schema_objects/base' require_relative 'validation/schema_objects/column' diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection.rb new file mode 100644 index 00000000000..83f282ef56d --- /dev/null +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Gitlab + module Schema + module Validation + module Sources + class AdapterNotSupportedError < StandardError + def initialize(adapter) + @adapter = adapter + end + + def message + "#{adapter} is not supported" + end + + private + + attr_reader :adapter + end + + class Connection + CONNECTION_ADAPTERS = { + 'Gitlab::Database::LoadBalancing::ConnectionProxy' => ConnectionAdapters::ActiveRecordAdapter, + 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter' => ConnectionAdapters::ActiveRecordAdapter, + 'PG::Connection' => ConnectionAdapters::PgAdapter + }.freeze + + def initialize(connection) + @connection_adapter = fetch_adapter(connection) + end + + def current_schema + connection_adapter.current_schema + end + + def select_rows(sql, schemas = []) + connection_adapter.select_rows(sql, schemas) + end + + def exec_query(sql, schemas = []) + connection_adapter.exec_query(sql, schemas) + end + + private + + attr_reader :connection_adapter + + def fetch_adapter(connection) + CONNECTION_ADAPTERS.fetch(connection.class.name).new(connection) + rescue KeyError => e + raise AdapterNotSupportedError, e.key + end + end + end + end + end +end diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/active_record_adapter.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/active_record_adapter.rb new file mode 100644 index 00000000000..10af0fc3647 --- /dev/null +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/active_record_adapter.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module Schema + module Validation + module Sources + module ConnectionAdapters + class ActiveRecordAdapter < Base + extend Forwardable + + def_delegators :@connection, :current_schema + + def exec_query(sql, schemas) + connection.exec_query(sql, nil, schemas) + end + + def select_rows(sql, schemas) + connection.select_rows(sql, nil, schemas) + end + end + end + end + end + end +end diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/base.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/base.rb new file mode 100644 index 00000000000..713cbbe0a6d --- /dev/null +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/base.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Gitlab + module Schema + module Validation + module Sources + module ConnectionAdapters + class Base + def initialize(connection) + @connection = connection + end + + def current_schema + raise NotImplementedError, "#{self.class} does not implement #{__method__}" + end + + def select_rows(sql, schemas = []) + raise NotImplementedError, "#{self.class} does not implement #{__method__}" + end + + def exec_query(sql, schemas = []) + raise NotImplementedError, "#{self.class} does not implement #{__method__}" + end + + private + + attr_reader :connection + end + end + end + end + end +end diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/pg_adapter.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/pg_adapter.rb new file mode 100644 index 00000000000..4dcaf15be71 --- /dev/null +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/connection_adapters/pg_adapter.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Gitlab + module Schema + module Validation + module Sources + module ConnectionAdapters + class PgAdapter < Base + def initialize(connection) + @connection = connection + @connection.type_map_for_results = PG::BasicTypeMapForResults.new(connection) + end + + def current_schema + connection.exec('SELECT current_schema').first['current_schema'] + end + + def exec_query(sql, schemas) + connection.exec(sql, schemas) + end + + def select_rows(sql, schemas) + exec_query(sql, schemas).values + end + end + end + end + end + end +end diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb index 8505d1f149a..45ce4d8ebfe 100644 --- a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb @@ -8,7 +8,7 @@ module Gitlab STATIC_PARTITIONS_SCHEMA = 'gitlab_partitions_static' def initialize(connection) - @connection = connection + @connection = Connection.new(connection) end def fetch_index_by_name(index_name) @@ -102,7 +102,7 @@ module Gitlab SQL # rubocop:enable Rails/SquishedSQLHeredocs - connection.select_rows(sql, nil, schemas).to_h + connection.select_rows(sql, schemas).to_h end def table_map @@ -136,7 +136,7 @@ module Gitlab SQL # rubocop:enable Rails/SquishedSQLHeredocs - connection.exec_query(sql, nil, schemas).group_by { |row| row['table_name'] } + connection.exec_query(sql, schemas).group_by { |row| row['table_name'] } end def fetch_indexes @@ -148,7 +148,7 @@ module Gitlab SQL # rubocop:enable Rails/SquishedSQLHeredocs - connection.select_rows(sql, nil, schemas).to_h + connection.select_rows(sql, schemas).to_h end def index_map @@ -183,7 +183,7 @@ module Gitlab SQL # rubocop:enable Rails/SquishedSQLHeredocs - connection.exec_query(sql, nil, [connection.current_schema]) + connection.exec_query(sql, [connection.current_schema]) end end end diff --git a/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/sources/connection_spec.rb b/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/sources/connection_spec.rb new file mode 100644 index 00000000000..f3469665d57 --- /dev/null +++ b/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/sources/connection_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Schema::Validation::Sources::Connection, feature_category: :database do + let(:sql) { 'SELECT column_one, column_two FROM my_table WHERE schema_name IN ($1);' } + let(:schemas) { ['public'] } + let(:query_result) do + [ + { 'name' => 'Person one', 'email' => 'person.one@gitlab.com' }, + { 'name' => 'Person two', 'email' => 'person.two@gitlab.com' } + ] + end + + let(:rows) { query_result.map(&:values) } + + context 'when using active record for postgres adapter' do + let(:schema) { 'public' } + let(:connection_class_name) { 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter' } + let(:adapter_class) { Gitlab::Schema::Validation::Sources::ConnectionAdapters::ActiveRecordAdapter } + + it_behaves_like 'connection adapter' + end + + context 'when using gitlab active record adapter' do + let(:schema) { 'gitlab_main' } + let(:connection_class_name) { 'Gitlab::Database::LoadBalancing::ConnectionProxy' } + let(:adapter_class) { Gitlab::Schema::Validation::Sources::ConnectionAdapters::ActiveRecordAdapter } + + it_behaves_like 'connection adapter' + end + + context 'when using postgres adapter' do + let(:schema) { 'public' } + let(:connection_class_name) { 'PG::Connection' } + let(:adapter_class) { Gitlab::Schema::Validation::Sources::ConnectionAdapters::PgAdapter } + + before do + allow(connection_object).to receive(:exec) + allow(connection_object).to receive(:type_map_for_results=) + end + + it_behaves_like 'connection adapter' + end + + context 'when using an unsupported connection adapter' do + subject(:connection) { described_class.new(connection_object) } + + let(:connection_class_name) { 'ActiveRecord::ConnectionAdapters::InvalidAdapter' } + let(:connection_class) { class_double(Class, name: connection_class_name) } + let(:connection_object) { instance_double(connection_class_name, class: connection_class) } + let(:error_class) { Gitlab::Schema::Validation::Sources::AdapterNotSupportedError } + let(:error_message) { 'ActiveRecord::ConnectionAdapters::InvalidAdapter is not supported' } + + it { expect { connection }.to raise_error(error_class, error_message) } + end +end diff --git a/gems/gitlab-schema-validation/spec/spec_helper.rb b/gems/gitlab-schema-validation/spec/spec_helper.rb index c11c5021e3b..4eee709dc67 100644 --- a/gems/gitlab-schema-validation/spec/spec_helper.rb +++ b/gems/gitlab-schema-validation/spec/spec_helper.rb @@ -2,6 +2,7 @@ require "gitlab/schema/validation" require 'rspec-parameterized' +require 'pg' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure diff --git a/gems/gitlab-schema-validation/spec/support/shared_examples/connection_adapter_shared_examples.rb b/gems/gitlab-schema-validation/spec/support/shared_examples/connection_adapter_shared_examples.rb new file mode 100644 index 00000000000..f2b1ee7a934 --- /dev/null +++ b/gems/gitlab-schema-validation/spec/support/shared_examples/connection_adapter_shared_examples.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'connection adapter' do + subject(:connection) { described_class.new(connection_object) } + + let(:connection_class) { class_double(Class, name: connection_class_name) } + let(:connection_object) { instance_double(connection_class_name, class: connection_class) } + let(:adapter) do + instance_double( + described_class::CONNECTION_ADAPTERS[connection_class_name], + current_schema: schema, + exec_query: query_result, + select_rows: rows + ) + end + + before do + allow(connection).to receive(:connection_adapter).and_return(adapter) + end + + context 'when using a valid connection adapter' do + describe '#current_schema' do + it { expect(connection.current_schema).to eq(schema) } + end + + describe '#select_rows' do + it { expect(connection.select_rows(sql, schemas)).to eq(rows) } + end + + describe '#exec_query' do + it { expect(connection.exec_query(sql, schemas)).to eq(query_result) } + end + end +end diff --git a/gems/gitlab-schema-validation/spec/support/shared_examples/foreign_key_validators_shared_examples.rb b/gems/gitlab-schema-validation/spec/support/shared_examples/foreign_key_validators_shared_examples.rb index 1f33c8bd760..8de9c165f8a 100644 --- a/gems/gitlab-schema-validation/spec/support/shared_examples/foreign_key_validators_shared_examples.rb +++ b/gems/gitlab-schema-validation/spec/support/shared_examples/foreign_key_validators_shared_examples.rb @@ -10,8 +10,11 @@ RSpec.shared_examples 'foreign key validators' do |validator, expected_result| let(:inconsistency_type) { validator.to_s } let(:database_name) { 'main' } let(:schema) { 'public' } + let(:connection_class) { class_double(Class, name: 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter') } # rubocop:disable RSpec/VerifiedDoubleReference - let(:connection) { instance_double('connection', exec_query: database_query, current_schema: 'public') } + let(:connection) do + instance_double('connection', class: connection_class, exec_query: database_query, current_schema: 'public') + end # rubocop:enable RSpec/VerifiedDoubleReference let(:database) { Gitlab::Schema::Validation::Sources::Database.new(connection) } diff --git a/gems/gitlab-schema-validation/spec/support/shared_examples/index_validators_shared_examples.rb b/gems/gitlab-schema-validation/spec/support/shared_examples/index_validators_shared_examples.rb index cc20c0dc765..36a621ffb35 100644 --- a/gems/gitlab-schema-validation/spec/support/shared_examples/index_validators_shared_examples.rb +++ b/gems/gitlab-schema-validation/spec/support/shared_examples/index_validators_shared_examples.rb @@ -13,9 +13,12 @@ RSpec.shared_examples 'index validators' do |validator, expected_result| end let(:inconsistency_type) { validator.name } + let(:connection_class) { class_double(Class, name: 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter') } # rubocop:disable RSpec/VerifiedDoubleReference - let(:connection) { instance_double('connection', select_rows: database_indexes, current_schema: 'public') } + let(:connection) do + instance_double('connection', class: connection_class, select_rows: database_indexes, current_schema: 'public') + end # rubocop:enable RSpec/VerifiedDoubleReference let(:schema) { 'public' } diff --git a/gems/gitlab-schema-validation/spec/support/shared_examples/table_validators_shared_examples.rb b/gems/gitlab-schema-validation/spec/support/shared_examples/table_validators_shared_examples.rb index d2a51a9b202..5c5a67c4628 100644 --- a/gems/gitlab-schema-validation/spec/support/shared_examples/table_validators_shared_examples.rb +++ b/gems/gitlab-schema-validation/spec/support/shared_examples/table_validators_shared_examples.rb @@ -7,9 +7,13 @@ RSpec.shared_examples "table validators" do |validator, expected_result| let(:structure_file_path) { 'spec/fixtures/structure.sql' } let(:inconsistency_type) { validator.to_s } + let(:connection_class) { class_double(Class, name: 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter') } # rubocop:disable RSpec/VerifiedDoubleReference - let(:connection) { instance_double('connection', exec_query: database_tables, current_schema: 'public') } + let(:connection) do + instance_double('connection', class: connection_class, exec_query: database_tables, current_schema: 'public') + end # rubocop:enable RSpec/VerifiedDoubleReference + let(:schema) { 'public' } let(:database) { Gitlab::Schema::Validation::Sources::Database.new(connection) } let(:structure_file) { Gitlab::Schema::Validation::Sources::StructureSql.new(structure_file_path, schema) } diff --git a/gems/gitlab-schema-validation/spec/support/shared_examples/trigger_validators_shared_examples.rb b/gems/gitlab-schema-validation/spec/support/shared_examples/trigger_validators_shared_examples.rb index 45ed87082bb..36eedcde280 100644 --- a/gems/gitlab-schema-validation/spec/support/shared_examples/trigger_validators_shared_examples.rb +++ b/gems/gitlab-schema-validation/spec/support/shared_examples/trigger_validators_shared_examples.rb @@ -11,9 +11,12 @@ RSpec.shared_examples 'trigger validators' do |validator, expected_result| let(:database_name) { 'main' } let(:schema) { 'public' } let(:database) { Gitlab::Schema::Validation::Sources::Database.new(connection) } + let(:connection_class) { class_double(Class, name: 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter') } # rubocop:disable RSpec/VerifiedDoubleReference - let(:connection) { instance_double('connection', select_rows: database_triggers, current_schema: 'public') } + let(:connection) do + instance_double('connection', class: connection_class, select_rows: database_triggers, current_schema: 'public') + end # rubocop:enable RSpec/VerifiedDoubleReference let(:database_triggers) do |