Patch #25066 » Added_null_placement_at_order_statement.patch
app/models/query.rb (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
716 | 716 |
arg = arg.keys.sort.collect {|k| arg[k]} |
717 | 717 |
end |
718 | 718 |
if arg |
719 |
c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, (o == 'desc' || o == false) ? 'desc' : 'asc']}
|
|
719 |
c = arg.select { |k, o| !k.to_s.blank? }.slice(0, 3).collect { |k, o| [k.to_s, o.to_s] }
|
|
720 | 720 |
end |
721 | 721 |
write_attribute(:sort_criteria, c) |
722 | 722 |
end |
... | ... | |
984 | 984 |
" SELECT customized_id FROM #{CustomValue.table_name}" + |
985 | 985 |
" WHERE customized_type='#{target_class}' AND custom_field_id=#{chained_custom_field_id}" + |
986 | 986 |
" AND #{sql_for_field(field, operator, value, CustomValue.table_name, 'value')}))" |
987 |
|
|
987 |
|
|
988 | 988 |
end |
989 | 989 |
|
990 | 990 |
def sql_for_custom_field_attribute(field, operator, value, custom_field_id, attribute) |
config/locales/en.yml (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
898 | 898 |
label_sort: Sort |
899 | 899 |
label_ascending: Ascending |
900 | 900 |
label_descending: Descending |
901 |
label_ascending_nulls_last: Ascending with null values at the end |
|
902 |
label_descending_nulls_first: Descending with null values at the top |
|
901 | 903 |
label_date_from_to: From %{start} to %{end} |
902 | 904 |
label_wiki_content_added: Wiki page added |
903 | 905 |
label_wiki_content_updated: Wiki page updated |
config/locales/ru.yml (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
1331 | 1331 |
field_updated_by: Updated by |
1332 | 1332 |
field_last_updated_by: Last updated by |
1333 | 1333 |
field_full_width_layout: Full width layout |
1334 |
label_ascending_nulls_last: По возрастанию с пустыми значениями в конце |
|
1335 |
label_descending_nulls_first: По убыванию с пустыми значениями в начале |
lib/redmine/database.rb (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
49 | 49 |
(ActiveRecord::Base.connection.adapter_name =~ /mysql/i).present? |
50 | 50 |
end |
51 | 51 |
|
52 |
def sqlite3? |
|
53 |
(ActiveRecord::Base.connection.adapter_name =~ /sqlite/i).present? |
|
54 |
end |
|
55 |
|
|
56 |
def sqlserver? |
|
57 |
(ActiveRecord::Base.connection.adapter_name =~ /sqlserver/i).present? |
|
58 |
end |
|
59 |
|
|
52 | 60 |
# Returns a SQL statement for case/accent (if possible) insensitive match |
53 | 61 |
def like(left, right, options={}) |
54 | 62 |
neg = (options[:match] == false ? 'NOT ' : '') |
... | ... | |
64 | 72 |
end |
65 | 73 |
end |
66 | 74 |
|
75 |
def firebird? |
|
76 |
(ActiveRecord::Base.connection.adapter_name =~ /firebird/i).present? |
|
77 |
end |
|
78 |
|
|
67 | 79 |
# Resets database information |
68 | 80 |
def reset |
69 | 81 |
@postgresql_unaccent = nil |
app/helpers/sort_helper.rb (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
61 | 61 |
|
62 | 62 |
def available_criteria=(criteria) |
63 | 63 |
unless criteria.is_a?(Hash) |
64 |
criteria = criteria.inject({}) {|h,k| h[k] = k; h}
|
|
64 |
criteria = criteria.inject({}) { |h, k| h[k] = k; h }
|
|
65 | 65 |
end |
66 | 66 |
@available_criteria = criteria |
67 | 67 |
end |
68 | 68 |
|
69 | 69 |
def from_param(param) |
70 |
@criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
|
|
70 |
@criteria = param.to_s.split(',').collect { |s| s.split(':')[0..1] }
|
|
71 | 71 |
normalize! |
72 | 72 |
end |
73 | 73 |
|
... | ... | |
77 | 77 |
end |
78 | 78 |
|
79 | 79 |
def to_param |
80 |
@criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
|
|
80 |
@criteria.collect { |k, o| k + ":#{o.to_s}" }.join(',')
|
|
81 | 81 |
end |
82 | 82 |
|
83 | 83 |
# Returns an array of SQL fragments used to sort the list |
84 | 84 |
def to_sql |
85 |
sql = @criteria.collect do |k,o| |
|
85 |
sql = @criteria.collect do |k, o|
|
|
86 | 86 |
if s = @available_criteria[k] |
87 | 87 |
s = [s] unless s.is_a?(Array) |
88 |
s.collect {|c| append_order(c, o ? "ASC" : "DESC")}
|
|
88 |
s.collect { |c| append_order(c, o.to_s) }
|
|
89 | 89 |
end |
90 | 90 |
end.flatten.compact |
91 | 91 |
sql.blank? ? nil : sql |
... | ... | |
96 | 96 |
end |
97 | 97 |
|
98 | 98 |
def add!(key, asc) |
99 |
@criteria.delete_if {|k,o| k == key}
|
|
99 |
@criteria.delete_if { |k, o| k == key }
|
|
100 | 100 |
@criteria = [[key, asc]] + @criteria |
101 | 101 |
normalize! |
102 | 102 |
end |
... | ... | |
112 | 112 |
end |
113 | 113 |
|
114 | 114 |
def first_asc? |
115 |
@criteria.first && @criteria.first.last |
|
115 |
@criteria.first && @criteria.first.last.to_s.downcase.start_with?("asc")
|
|
116 | 116 |
end |
117 | 117 |
|
118 | 118 |
def empty? |
... | ... | |
123 | 123 |
|
124 | 124 |
def normalize! |
125 | 125 |
@criteria ||= [] |
126 |
@criteria = @criteria.collect {|s| s = Array(s); [s.first, (s.last == false || s.last == 'desc') ? false : true]}
|
|
127 |
@criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
|
|
126 |
@criteria = @criteria.collect { |s| s = Array(s); [s.first, s[1].nil? ? "asc" : s.last] }
|
|
127 |
@criteria = @criteria.select { |k, o| @available_criteria.has_key?(k) } if @available_criteria
|
|
128 | 128 |
@criteria.slice!(3) |
129 | 129 |
self |
130 | 130 |
end |
... | ... | |
134 | 134 |
if criterion =~ / (asc|desc)$/i |
135 | 135 |
criterion |
136 | 136 |
else |
137 |
"#{criterion} #{order}" |
|
137 |
if (order =~ /nulls/i).present? |
|
138 |
t = order.to_s.split(' ') |
|
139 |
sort_order = t.first |
|
140 |
nulls_last = t.last.eql?("last") |
|
141 |
if Redmine::Database.mysql? |
|
142 |
"ISNULL(#{criterion}) #{nulls_last ? "ASC" : "DESC"}, #{criterion} #{sort_order}" |
|
143 |
elsif Redmine::Database.sqlserver? || Redmine::Database.sqlite3? |
|
144 |
"#{criterion} IS NULL #{nulls_last ? "ASC" : "DESC"}, #{criterion} #{sort_order}" |
|
145 |
else |
|
146 |
"#{criterion} #{order}" |
|
147 |
end |
|
148 |
else |
|
149 |
"#{criterion} #{order}" |
|
150 |
end |
|
138 | 151 |
end |
139 | 152 |
end |
140 | 153 |
|
... | ... | |
158 | 171 |
# |
159 | 172 |
def sort_init(*args) |
160 | 173 |
case args.size |
161 |
when 1 |
|
162 |
@sort_default = args.first.is_a?(Array) ? args.first : [[args.first]] |
|
163 |
when 2 |
|
164 |
@sort_default = [[args.first, args.last]] |
|
165 |
else |
|
166 |
raise ArgumentError |
|
174 |
when 1
|
|
175 |
@sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
|
|
176 |
when 2
|
|
177 |
@sort_default = [[args.first, args.last]]
|
|
178 |
else
|
|
179 |
raise ArgumentError
|
|
167 | 180 |
end |
168 | 181 |
end |
169 | 182 |
|
... | ... | |
217 | 230 |
end |
218 | 231 |
caption = column.to_s.humanize unless caption |
219 | 232 |
|
220 |
sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
|
|
233 |
sort_options = {:sort => @sort_criteria.add(column.to_s, order).to_param}
|
|
221 | 234 |
link_to(caption, {:params => request.query_parameters.merge(sort_options)}, :class => css) |
222 | 235 |
end |
223 | 236 |
|
lib/redmine/field_format.rb (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
293 | 293 |
# objects by their value of the custom field. |
294 | 294 |
# Returns nil if the custom field can not be used for sorting. |
295 | 295 |
def order_statement(custom_field) |
296 |
# COALESCE is here to make sure that blank and NULL values are sorted equally |
|
297 |
"COALESCE(#{join_alias custom_field}.value, '')" |
|
296 |
"#{join_alias custom_field}.value" #COALESCE removed to allow null placement on order |
|
298 | 297 |
end |
299 | 298 |
|
300 | 299 |
# Returns a GROUP BY clause that can used to group by custom value |
app/views/queries/_form.html.erb (revision c0ef6de058bb128515d1a4085c592da75e82188b) | ||
---|---|---|
63 | 63 |
<%= label_tag "query_sort_criteria_direction_" + i.to_s, |
64 | 64 |
l(:description_query_sort_criteria_direction), :class => "hidden-for-sighted" %> |
65 | 65 |
<%= select_tag("query[sort_criteria][#{i}][]", |
66 |
options_for_select([[], [l(:label_ascending), 'asc'], [l(:label_descending), 'desc']], @query.sort_criteria_order(i)), |
|
66 |
options_for_select([[], [l(:label_ascending), 'asc'], [l(:label_descending), 'desc'], [l(:label_ascending_nulls_last), 'asc nulls last'], [l(:label_descending_nulls_first), 'desc nulls first']], @query.sort_criteria_order(i)),
|
|
67 | 67 |
:id => "query_sort_criteria_direction_" + i.to_s) %> |
68 | 68 |
<br /> |
69 | 69 |
<% end %> |