Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
ac503c52
Commit
ac503c52
authored
Mar 21, 2022
by
Eulyeon Ko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support lower named function
Undo refactor
parent
9f4d41c7
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
164 additions
and
45 deletions
+164
-45
lib/gitlab/pagination/keyset/order.rb
lib/gitlab/pagination/keyset/order.rb
+6
-0
lib/gitlab/pagination/keyset/simple_order_builder.rb
lib/gitlab/pagination/keyset/simple_order_builder.rb
+100
-44
spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb
...ion/keyset/in_operator_optimization/query_builder_spec.rb
+1
-1
spec/lib/gitlab/pagination/keyset/order_spec.rb
spec/lib/gitlab/pagination/keyset/order_spec.rb
+41
-0
spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb
...lib/gitlab/pagination/keyset/simple_order_builder_spec.rb
+16
-0
No files found.
lib/gitlab/pagination/keyset/order.rb
View file @
ac503c52
...
@@ -99,6 +99,8 @@ module Gitlab
...
@@ -99,6 +99,8 @@ module Gitlab
field_value
.
strftime
(
'%Y-%m-%d %H:%M:%S.%N %Z'
)
field_value
.
strftime
(
'%Y-%m-%d %H:%M:%S.%N %Z'
)
elsif
field_value
.
nil?
elsif
field_value
.
nil?
nil
nil
elsif
lower_named_function?
(
column_definition
)
field_value
.
downcase
else
else
field_value
.
to_s
field_value
.
to_s
end
end
...
@@ -184,6 +186,10 @@ module Gitlab
...
@@ -184,6 +186,10 @@ module Gitlab
private
private
def
lower_named_function?
(
column_definition
)
column_definition
.
column_expression
.
is_a?
(
Arel
::
Nodes
::
NamedFunction
)
&&
column_definition
.
column_expression
.
name
&
.
downcase
==
'lower'
end
def
composite_row_comparison_possible?
def
composite_row_comparison_possible?
!
column_definitions
.
one?
&&
!
column_definitions
.
one?
&&
column_definitions
.
all?
(
&
:not_nullable?
)
&&
column_definitions
.
all?
(
&
:not_nullable?
)
&&
...
...
lib/gitlab/pagination/keyset/simple_order_builder.rb
View file @
ac503c52
...
@@ -24,55 +24,65 @@ module Gitlab
...
@@ -24,55 +24,65 @@ module Gitlab
end
end
def
build
def
build
return
[
scope
.
reorder!
(
primary_key_order
(
primary_key_desc
)),
true
]
if
order_values
.
empty?
order
=
if
order_values
.
empty?
return
[
scope
.
reorder!
(
keyset_order
),
true
]
if
Gitlab
::
Pagination
::
Keyset
::
Order
.
keyset_aware?
(
scope
)
primary_key_descending_order
elsif
Gitlab
::
Pagination
::
Keyset
::
Order
.
keyset_aware?
(
scope
)
simple_order
?
[
scope
.
reorder!
(
simple_order
),
true
]
:
[
scope
,
false
]
# [scope, success]
Gitlab
::
Pagination
::
Keyset
::
Order
.
extract_keyset_order_object
(
scope
)
# Ordered by a primary key. Ex. 'ORDER BY id'.
elsif
ordered_by_primary_key?
primary_key_order
# Ordered by one non-primary table column. Ex. 'ORDER BY created_at'.
elsif
ordered_by_other_column?
column_with_tie_breaker_order
# Ordered by two table columns with the last column as a tie breaker. Ex. 'ORDER BY created, id ASC'.
elsif
ordered_by_other_column_with_tie_breaker?
tie_breaker_attribute
=
order_values
.
second
tie_breaker_column_order
=
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
model_class
.
primary_key
,
order_expression:
tie_breaker_attribute
)
column_with_tie_breaker_order
(
tie_breaker_column_order
)
end
order
?
[
scope
.
reorder!
(
order
),
true
]
:
[
scope
,
false
]
# [scope, success]
end
end
private
private
attr_reader
:scope
,
:order_values
,
:model_class
,
:arel_table
,
:primary_key
attr_reader
:scope
,
:order_values
,
:model_class
,
:arel_table
,
:primary_key
def
keyset_order
def
table_column?
(
name
)
Gitlab
::
Pagination
::
Keyset
::
Order
.
extract_keyset_order_object
(
scope
)
model_class
.
column_names
.
include?
(
name
.
to_s
)
end
end
def
simple_order
def
primary_key?
(
attribute
)
return
unless
ordered_with_arel_attributes?
arel_table
[
primary_key
].
to_s
==
attribute
.
to_s
# Ordered by a primary key: 'ORDER BY id'.
if
order_values
.
one?
&&
primary_key?
(
order_values
.
first
)
primary_key_order
(
order_values
.
first
)
# Ordered by one non-primary table column: 'ORDER BY created_at'.
elsif
order_values
.
one?
&&
table_column?
(
order_values
.
first
)
simple_double_column_order
# Ordered by two table columns with the last column as a tie breaker: 'ORDER BY lower(title), id'.
elsif
order_values
.
size
==
2
&&
table_column?
(
order_values
.
first
)
&&
primary_key?
(
order_values
.
second
)
tie_breaker_value
=
order_values
.
second
simple_double_column_order
(
tie_breaker_value
)
end
end
end
def
ordered_with_arel_attributes?
def
lower_named_function?
(
attribute
)
arel_attributes
=
order_values
.
map
{
|
o
|
o
.
try
(
:expr
)
}.
compact
attribute
.
is_a?
(
Arel
::
Nodes
::
NamedFunction
)
&&
attribute
.
name
&
.
downcase
==
'lower'
arel_attributes
.
size
==
order_values
.
size
end
end
def
primary_key?
(
order_value
)
def
supported_column?
(
attribute
)
arel_table
[
primary_key
].
to_s
==
order_value
.
expr
.
to_s
if
lower_named_function?
(
attribute
)
attribute
.
expressions
.
one?
&&
attribute
.
expressions
.
first
.
respond_to?
(
:name
)
&&
table_column?
(
attribute
.
expressions
.
first
.
name
)
else
attribute
.
respond_to?
(
:name
)
&&
table_column?
(
attribute
.
name
)
end
end
end
def
table_column?
(
order_value
)
def
column_name
(
order_value
)
return
unless
order_value
.
expr
.
try
(
:name
)
if
lower_named_function?
(
order_value
.
expr
)
order_value
.
expr
.
expressions
.
first
.
name
model_class
.
column_names
.
include?
(
order_value
.
expr
.
name
.
to_s
)
else
order_value
.
expr
.
name
end
end
end
def
nullability
(
order_value
)
def
nullability
(
order_value
)
nullable
=
model_class
.
columns
.
find
{
|
column
|
column
.
name
==
order_value
.
expr
.
name
}.
null
nullable
=
model_class
.
columns
.
find
{
|
column
|
column
.
name
==
column_name
(
order_value
)
}.
null
if
nullable
&&
order_value
.
is_a?
(
Arel
::
Nodes
::
Ascending
)
if
nullable
&&
order_value
.
is_a?
(
Arel
::
Nodes
::
Ascending
)
:nulls_last
:nulls_last
...
@@ -83,36 +93,82 @@ module Gitlab
...
@@ -83,36 +93,82 @@ module Gitlab
end
end
end
end
def
primary_key_desc
def
primary_key_descending_order
arel_table
[
primary_key
].
desc
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
model_class
.
primary_key
,
order_expression:
arel_table
[
primary_key
].
desc
)
])
end
end
def
primary_key_order
(
order_value
)
def
primary_key_order
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
primary_key_column
(
order_value
)])
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
model_class
.
primary_key
,
order_expression:
order_values
.
first
)
])
end
end
def
simple_double_column_order
(
tie_breaker_value
=
primary_key_desc
)
def
column_with_tie_breaker_order
(
tie_breaker_column_order
=
default_tie_breaker_column_order
)
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
regular_
column
(
order_values
.
first
),
column
(
order_values
.
first
),
primary_key_column
(
tie_breaker_value
)
tie_breaker_column_order
])
])
end
end
def
primary_key_column
(
order_value
)
def
column
(
order_value
)
return
lower_named_function_column
(
order_value
)
if
lower_named_function?
(
order_value
.
expr
)
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
model_class
.
primary_key
,
attribute_name:
order_value
.
expr
.
name
,
order_expression:
order_value
order_expression:
order_value
,
nullable:
nullability
(
order_value
),
distinct:
false
)
)
end
end
def
regular
_column
(
order_value
)
def
lower_named_function
_column
(
order_value
)
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
order_value
.
expr
.
name
,
attribute_name:
column_name
(
order_value
)
,
order_expression:
order_value
,
order_expression:
order_value
,
column_expression:
Arel
::
Nodes
::
NamedFunction
.
new
(
"LOWER"
,
[
model_class
.
arel_table
[
column_name
(
order_value
)]]),
nullable:
nullability
(
order_value
),
nullable:
nullability
(
order_value
),
distinct:
false
distinct:
false
)
)
end
end
def
ordered_by_primary_key?
return
unless
order_values
.
one?
attribute
=
order_values
.
first
.
try
(
:expr
)
attribute
&&
primary_key?
(
attribute
)
end
def
ordered_by_other_column?
return
unless
order_values
.
one?
attribute
=
order_values
.
first
.
try
(
:expr
)
attribute
&&
supported_column?
(
attribute
)
end
def
ordered_by_other_column_with_tie_breaker?
return
unless
order_values
.
size
==
2
attribute
=
order_values
.
first
.
try
(
:expr
)
return
unless
attribute
&&
supported_column?
(
attribute
)
tie_breaker_attribute
=
order_values
.
second
.
try
(
:expr
)
tie_breaker_attribute
&&
primary_key?
(
tie_breaker_attribute
)
end
def
default_tie_breaker_column_order
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
model_class
.
primary_key
,
order_expression:
arel_table
[
primary_key
].
desc
)
end
end
end
end
end
end
end
...
...
spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb
View file @
ac503c52
...
@@ -239,7 +239,7 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
...
@@ -239,7 +239,7 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
end
end
it
'raises error when unsupported scope is passed'
do
it
'raises error when unsupported scope is passed'
do
scope
=
Issue
.
order
(
Issue
.
arel_table
[
:id
].
lower
.
desc
)
scope
=
Issue
.
order
(
Arel
::
Nodes
::
NamedFunction
.
new
(
'UPPER'
,
[
Issue
.
arel_table
[
:id
]])
)
options
=
{
options
=
{
scope:
scope
,
scope:
scope
,
...
...
spec/lib/gitlab/pagination/keyset/order_spec.rb
View file @
ac503c52
...
@@ -441,6 +441,47 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
...
@@ -441,6 +441,47 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
end
end
end
context
'when ordering by the named function LOWER'
do
let
(
:order
)
do
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
'title'
,
column_expression:
Arel
::
Nodes
::
NamedFunction
.
new
(
"LOWER"
,
[
table
[
'title'
].
desc
]),
order_expression:
table
[
'title'
].
lower
.
desc
,
nullable: :not_nullable
,
distinct:
false
),
Gitlab
::
Pagination
::
Keyset
::
ColumnOrderDefinition
.
new
(
attribute_name:
'id'
,
column_expression:
table
[
'id'
],
order_expression:
table
[
'id'
].
desc
,
nullable: :not_nullable
,
distinct:
true
)
])
end
let
(
:table_data
)
do
<<-
SQL
VALUES (1, 'A')
SQL
end
let
(
:query
)
do
<<-
SQL
SELECT id, title
FROM (
#{
table_data
}
) my_table (id, title)
ORDER BY
#{
order
}
;
SQL
end
subject
{
run_query
(
query
)
}
it
"uses downcased value for encoding and decoding a cursor"
do
expect
(
order
.
cursor_attributes_for_node
(
subject
.
first
)[
'title'
]).
to
eq
(
"a"
)
end
end
context
'when the passed cursor values do not match with the order definition'
do
context
'when the passed cursor values do not match with the order definition'
do
let
(
:order
)
do
let
(
:order
)
do
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
Gitlab
::
Pagination
::
Keyset
::
Order
.
build
([
...
...
spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb
View file @
ac503c52
...
@@ -67,6 +67,22 @@ RSpec.describe Gitlab::Pagination::Keyset::SimpleOrderBuilder do
...
@@ -67,6 +67,22 @@ RSpec.describe Gitlab::Pagination::Keyset::SimpleOrderBuilder do
end
end
end
end
context
'when ordering by a column with the lower named function'
do
let
(
:scope
)
{
Project
.
where
(
id:
[
1
,
2
,
3
]).
order
(
Project
.
arel_table
[
:name
].
lower
.
desc
)
}
it
'sets the column definition for name'
do
column_definition
=
order_object
.
column_definitions
.
first
expect
(
column_definition
.
attribute_name
).
to
eq
(
'name'
)
expect
(
column_definition
.
column_expression
.
expressions
.
first
.
name
).
to
eq
(
'name'
)
expect
(
column_definition
.
column_expression
.
name
).
to
eq
(
'LOWER'
)
end
it
'adds extra primary key order as tie-breaker'
do
expect
(
sql_with_order
).
to
end_with
(
'ORDER BY LOWER("projects"."name") DESC, "projects"."id" DESC'
)
end
end
context
'return :unable_to_order symbol when order cannot be built'
do
context
'return :unable_to_order symbol when order cannot be built'
do
subject
(
:success
)
{
described_class
.
build
(
scope
).
last
}
subject
(
:success
)
{
described_class
.
build
(
scope
).
last
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment