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 %> |