Commit 24f25f0b authored by Adam Hegyi's avatar Adam Hegyi

Use row comparison in keyset pagination

This change uses row comparison in keyset pagination when the ORDER BY
columns are not nullable and the order direction is the same.

Example: (created_at, id) > ('2012-09-18 01:40:01+00', 15)
parent cb527d94
......@@ -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