Project

General

Profile

Patch #25066 » Added_null_placement_at_order_statement.patch

Andrey Lobanov (RedSoft), 2017-02-14 13:05

View differences:

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 %>
(1-1/3)