Commit 661c4000 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch 'use_row_comparison_in_keyset_pagination' into 'master'

Use row comparison in keyset pagination

See merge request gitlab-org/gitlab!65396
parents 1e1a10dd 24f25f0b
......@@ -120,7 +120,7 @@ module Gitlab
AREL_ORDER_CLASSES = { Arel::Nodes::Ascending => :asc, Arel::Nodes::Descending => :desc }.freeze
ALLOWED_NULLABLE_VALUES = [:not_nullable, :nulls_first, :nulls_last].freeze
attr_reader :attribute_name, :column_expression, :order_expression, :add_to_projections
attr_reader :attribute_name, :column_expression, :order_expression, :add_to_projections, :order_direction
def initialize(attribute_name:, order_expression:, column_expression: nil, reversed_order_expression: nil, nullable: :not_nullable, distinct: true, order_direction: nil, add_to_projections: false)
@attribute_name = attribute_name
......@@ -175,7 +175,7 @@ module Gitlab
private
attr_reader :reversed_order_expression, :nullable, :distinct, :order_direction
attr_reader :reversed_order_expression, :nullable, :distinct
def calculate_reversed_order(order_expression)
unless AREL_ORDER_CLASSES.has_key?(order_expression.class) # Arel can reverse simple orders
......
......@@ -139,6 +139,8 @@ module Gitlab
verify_incoming_values!(values)
return use_composite_row_comparison(values) if composite_row_comparison_possible?
where_values = []
reversed_column_definitions = column_definitions.reverse
......@@ -187,6 +189,28 @@ module Gitlab
private
def composite_row_comparison_possible?
!column_definitions.one? &&
column_definitions.all?(&:not_nullable?) &&
column_definitions.map(&:order_direction).uniq.one? # all columns uses the same order direction
end
# composite row comparison works with NOT NULL columns and may use only one index scan given a proper index setup
# Example: (created_at, id) > ('2012-09-18 01:40:01+00', 15)
def use_composite_row_comparison(values)
columns = Arel::Nodes::Grouping.new(column_definitions.map(&:column_expression))
values = Arel::Nodes::Grouping.new(column_definitions.map do |column_definition|
value = values[column_definition.attribute_name]
Arel::Nodes.build_quoted(value, column_definition.column_expression)
end)
if column_definitions.first.ascending_order?
[columns.gt(values)]
else
[columns.lt(values)]
end
end
# Adds extra columns to the SELECT clause
def apply_custom_projections(scope)
additional_projections = column_definitions.select(&:add_to_projections).map do |column_definition|
......
......@@ -171,6 +171,12 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
it_behaves_like 'order examples'
it 'uses the row comparison method' do
sql = order.where_values_with_or_query({ year: 2010, month: 5, id: 1 }).to_sql
expect(sql).to eq('(("my_table"."year", "my_table"."month", "my_table"."id") > (2010, 5, 1))')
end
end
context 'when ordering by nullable columns and a distinct column' do
......
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