diff options
Diffstat (limited to 'spec/lib/gitlab/pagination/keyset')
4 files changed, 135 insertions, 16 deletions
diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb index cc85c897019..1c67c9e0b95 100644 --- a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb @@ -33,6 +33,18 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder ] end + let_it_be(:ignored_column_model) do + Class.new(ApplicationRecord) do + self.table_name = 'issues' + + include IgnorableColumns + + ignore_column :title, remove_with: '16.4', remove_after: '2023-08-22' + end + end + + let(:scope_model) { Issue } + let(:created_records) { issues } let(:iterator) do Gitlab::Pagination::Keyset::Iterator.new( scope: scope.limit(batch_size), @@ -79,6 +91,55 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder end end + context 'when the scope model has ignored columns' do + let(:scope) { ignored_column_model.order(id: :desc) } + let(:expected_order) { ignored_column_model.where(id: issues.map(&:id)).sort_by(&:id).reverse } + + let(:in_operator_optimization_options) do + { + array_scope: Project.where(namespace_id: top_level_group.self_and_descendants.select(:id)).select(:id), + array_mapping_scope: -> (id_expression) { ignored_column_model.where(ignored_column_model.arel_table[:project_id].eq(id_expression)) }, + finder_query: -> (id_expression) { ignored_column_model.where(ignored_column_model.arel_table[:id].eq(id_expression)) } + } + end + + context 'when iterating records one by one' do + let(:batch_size) { 1 } + + it_behaves_like 'correct ordering examples' + + context 'when scope selects only some columns' do + let(:scope) { ignored_column_model.order(id: :desc).select(:id) } + + it_behaves_like 'correct ordering examples' + end + end + + context 'when iterating records with LIMIT 3' do + let(:batch_size) { 3 } + + it_behaves_like 'correct ordering examples' + + context 'when scope selects only some columns' do + let(:scope) { ignored_column_model.order(id: :desc).select(:id) } + + it_behaves_like 'correct ordering examples' + end + end + + context 'when loading records at once' do + let(:batch_size) { issues.size + 1 } + + it_behaves_like 'correct ordering examples' + + context 'when scope selects only some columns' do + let(:scope) { ignored_column_model.order(id: :desc).select(:id) } + + it_behaves_like 'correct ordering examples' + end + end + end + context 'when ordering by issues.id DESC' do let(:scope) { Issue.order(id: :desc) } let(:expected_order) { issues.sort_by(&:id).reverse } @@ -332,7 +393,7 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder end context 'when ordering by JOIN-ed columns' do - let(:scope) { cte_with_issues_and_projects.apply_to(Issue.where({})).reorder(order) } + let(:scope) { cte_with_issues_and_projects.apply_to(Issue.where({}).select(Arel.star)).reorder(order) } let(:cte_with_issues_and_projects) do cte_query = Issue.select('issues.id AS id', 'project_id', 'projects.id AS projects_id', 'projects.name AS projects_name').joins(:project) diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy_spec.rb index 5180403b493..3fe858f33da 100644 --- a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::RecordLoaderStrategy do - let(:finder_query) { -> (created_at_value, id_value) { Project.where(Project.arel_table[:id].eq(id_value)) } } + let(:finder_query) { -> (created_at_value, id_value) { model.where(model.arel_table[:id].eq(id_value)) } } let(:model) { Project } let(:keyset_scope) do scope, _ = Gitlab::Pagination::Keyset::SimpleOrderBuilder.build( - Project.order(:created_at, :id) + model.order(:created_at, :id) ) scope @@ -22,6 +22,16 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::R Gitlab::Pagination::Keyset::InOperatorOptimization::OrderByColumns.new(keyset_order.column_definitions, model.arel_table) end + let_it_be(:ignored_column_model) do + Class.new(ApplicationRecord) do + self.table_name = 'projects' + + include IgnorableColumns + + ignore_column :name, remove_with: '16.4', remove_after: '2023-08-22' + end + end + subject(:strategy) { described_class.new(finder_query, model, order_by_columns) } describe '#initializer_columns' do @@ -57,4 +67,22 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::R expect(strategy.columns).to eq([expected_loader_query.chomp]) end end + + describe '#final_projections' do + context 'when model does not have ignored columns' do + it 'does not specify the selected column names' do + expect(strategy.final_projections).to contain_exactly("(#{described_class::RECORDS_COLUMN}).*") + end + end + + context 'when model has ignored columns' do + let(:model) { ignored_column_model } + + it 'specifies the selected column names' do + expect(strategy.final_projections).to match_array( + model.default_select_columns.map { |column| "(#{described_class::RECORDS_COLUMN}).#{column.name}" } + ) + end + end + end end diff --git a/spec/lib/gitlab/pagination/keyset/iterator_spec.rb b/spec/lib/gitlab/pagination/keyset/iterator_spec.rb index eee743c5e48..afaad48d363 100644 --- a/spec/lib/gitlab/pagination/keyset/iterator_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/iterator_spec.rb @@ -87,7 +87,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do time = Time.current iterator.each_batch(of: 2) do |relation| - Issue.connection.execute("UPDATE issues SET updated_at = '#{time.to_s(:inspect)}' WHERE id IN (#{relation.reselect(:id).to_sql})") + Issue.connection.execute("UPDATE issues SET updated_at = '#{time.to_fs(:inspect)}' WHERE id IN (#{relation.reselect(:id).to_sql})") end expect(Issue.pluck(:updated_at)).to all(be_within(5.seconds).of(time)) diff --git a/spec/lib/gitlab/pagination/keyset/order_spec.rb b/spec/lib/gitlab/pagination/keyset/order_spec.rb index e99846ad424..05bb0bb8b3a 100644 --- a/spec/lib/gitlab/pagination/keyset/order_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/order_spec.rb @@ -148,7 +148,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do end let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'id', @@ -193,7 +193,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do end let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'year', @@ -260,7 +260,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do end let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'year', @@ -327,7 +327,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do end let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'year', @@ -394,7 +394,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do end let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'year', @@ -452,7 +452,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do context 'when ordering by the named function LOWER' do let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'title', @@ -494,7 +494,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do context 'when the passed cursor values do not match with the order definition' do let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'year', @@ -564,7 +564,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do context 'when string attribute name is given' do let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'id', @@ -580,7 +580,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do context 'when symbol attribute name is given' do let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: :id, @@ -606,7 +606,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do context 'when there are additional_projections' do let(:order) do - order = Gitlab::Pagination::Keyset::Order.build( + order = described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'created_at_field', @@ -645,6 +645,16 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do let_it_be(:user_2) { create(:user, created_at: five_months_ago) } let_it_be(:user_3) { create(:user, created_at: 1.month.ago) } let_it_be(:user_4) { create(:user, created_at: 2.months.ago) } + let_it_be(:ignored_column_model) do + Class.new(ApplicationRecord) do + self.table_name = 'users' + + include IgnorableColumns + include FromUnion + + ignore_column :username, remove_with: '16.4', remove_after: '2023-08-22' + end + end let(:expected_results) { [user_3, user_4, user_2, user_1] } let(:scope) { User.order(created_at: :desc, id: :desc) } @@ -672,6 +682,26 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do iterator_options[:use_union_optimization] = true end + context 'when the scope model has ignored columns' do + let(:ignored_expected_results) { expected_results.map { |r| r.becomes(ignored_column_model) } } # rubocop:disable Cop/AvoidBecomes + + context 'when scope selects all columns' do + let(:scope) { ignored_column_model.order(created_at: :desc, id: :desc) } + + it 'returns items in the correct order' do + expect(items).to eq(ignored_expected_results) + end + end + + context 'when scope selects only specific columns' do + let(:scope) { ignored_column_model.order(created_at: :desc, id: :desc).select(:id, :created_at) } + + it 'returns items in the correct order' do + expect(items).to eq(ignored_expected_results) + end + end + end + it 'returns items in the correct order' do expect(items).to eq(expected_results) end @@ -687,7 +717,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do it 'builds UNION query' do cursor_attributes = { created_at: five_months_ago, id: user_2.id } - order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(keyset_aware_scope) + order = described_class.extract_keyset_order_object(keyset_aware_scope) query = order.apply_cursor_conditions(scope, cursor_attributes, use_union_optimization: true).to_sql expect(query).to include('UNION ALL') @@ -698,7 +728,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do describe '#attribute_names' do let(:expected_attribute_names) { %w(id name) } let(:order) do - Gitlab::Pagination::Keyset::Order.build( + described_class.build( [ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( attribute_name: 'id', |