diff options
Diffstat (limited to 'spec/lib/gitlab/pagination/keyset/in_operator_optimization')
3 files changed, 136 insertions, 9 deletions
diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/order_by_column_data_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/order_by_column_data_spec.rb new file mode 100644 index 00000000000..b4869f49081 --- /dev/null +++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/order_by_column_data_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::OrderByColumnData do + let(:arel_table) { Issue.arel_table } + + let(:column) do + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: :id, + column_expression: arel_table[:id], + order_expression: arel_table[:id].desc + ) + end + + subject(:column_data) { described_class.new(column, 'column_alias', arel_table) } + + describe '#arel_column' do + it 'delegates to column_expression' do + expect(column_data.arel_column).to eq(column.column_expression) + end + end + + describe '#column_for_projection' do + it 'returns the expression with AS using the original column name' do + expect(column_data.column_for_projection.to_sql).to eq('"issues"."id" AS id') + end + end + + describe '#projection' do + it 'returns the expression with AS using the specified column lias' do + expect(column_data.projection.to_sql).to eq('"issues"."id" AS column_alias') + end + end +end 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 00beacd4b35..58db22e5a9c 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,14 +33,14 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder ] end - shared_examples 'correct ordering examples' do - let(:iterator) do - Gitlab::Pagination::Keyset::Iterator.new( - scope: scope.limit(batch_size), - in_operator_optimization_options: in_operator_optimization_options - ) - end + let(:iterator) do + Gitlab::Pagination::Keyset::Iterator.new( + scope: scope.limit(batch_size), + in_operator_optimization_options: in_operator_optimization_options + ) + end + shared_examples 'correct ordering examples' do |opts = {}| let(:all_records) do all_records = [] iterator.each_batch(of: batch_size) do |records| @@ -49,8 +49,10 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder all_records end - it 'returns records in correct order' do - expect(all_records).to eq(expected_order) + unless opts[:skip_finder_query_test] + it 'returns records in correct order' do + expect(all_records).to eq(expected_order) + end end context 'when not passing the finder query' do @@ -248,4 +250,57 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder expect { described_class.new(**options).execute }.to raise_error(/The order on the scope does not support keyset pagination/) end + + context 'when ordering by SQL expression' do + let(:order) do + # ORDER BY (id * 10), id + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: 'id_multiplied_by_ten', + order_expression: Arel.sql('(id * 10)').asc, + sql_type: 'integer' + ), + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: :id, + order_expression: Issue.arel_table[:id].asc + ) + ]) + end + + let(:scope) { Issue.reorder(order) } + let(:expected_order) { issues.sort_by(&:id) } + + 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) { Issue.where(Issue.arel_table[:project_id].eq(id_expression)) } + } + end + + context 'when iterating records one by one' do + let(:batch_size) { 1 } + + it_behaves_like 'correct ordering examples', skip_finder_query_test: true + end + + context 'when iterating records with LIMIT 3' do + let(:batch_size) { 3 } + + it_behaves_like 'correct ordering examples', skip_finder_query_test: true + end + + context 'when passing finder query' do + let(:batch_size) { 3 } + + it 'raises error, loading complete rows are not supported with SQL expressions' do + in_operator_optimization_options[:finder_query] = -> (_, _) { Issue.select(:id, '(id * 10)').where(id: -1) } + + expect(in_operator_optimization_options[:finder_query]).not_to receive(:call) + + expect do + iterator.each_batch(of: batch_size) { |records| records.to_a } + end.to raise_error /The "RecordLoaderStrategy" does not support/ + end + end + end end diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb index fe95d5406dd..ab1037b318b 100644 --- a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb @@ -31,4 +31,41 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::O ]) end end + + context 'when an SQL expression is given' do + context 'when the sql_type attribute is missing' do + let(:order) do + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: 'id_times_ten', + order_expression: Arel.sql('id * 10').asc + ) + ]) + end + + let(:keyset_scope) { Project.order(order) } + + it 'raises error' do + expect { strategy.initializer_columns }.to raise_error(Gitlab::Pagination::Keyset::SqlTypeMissingError) + end + end + + context 'when the sql_type_attribute is present' do + let(:order) do + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: 'id_times_ten', + order_expression: Arel.sql('id * 10').asc, + sql_type: 'integer' + ) + ]) + end + + let(:keyset_scope) { Project.order(order) } + + it 'returns the initializer columns' do + expect(strategy.initializer_columns).to eq(['NULL::integer AS id_times_ten']) + end + end + end end |