X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/3bd5f6c3f353059c15eeea51b16b2fdee694d504..6c91320c3a53ddbe1b81bee8ed6322c20acbd047:/services/api/lib/record_filters.rb diff --git a/services/api/lib/record_filters.rb b/services/api/lib/record_filters.rb index d7e556b197..f31875b47d 100644 --- a/services/api/lib/record_filters.rb +++ b/services/api/lib/record_filters.rb @@ -8,7 +8,7 @@ module RecordFilters # Input: - # +filters+ Arvados filters as list of lists. + # +filters+ array of conditions, each being [column, operator, operand] # +ar_table_name+ name of SQL table # # Output: @@ -29,26 +29,52 @@ module RecordFilters raise ArgumentError.new("Invalid attribute '#{attr}' in filter") end case operator.downcase - when '=', '<', '<=', '>', '>=', 'like' + when '=', '<', '<=', '>', '>=', '!=', 'like' + attr_type = model_class.attribute_column(attr).type + operator = '<>' if operator == '!=' if operand.is_a? String + if attr_type == :boolean + if not ['=', '<>'].include?(operator) + raise ArgumentError.new("Invalid operator '#{operator}' for " \ + "boolean attribute '#{attr}'") + end + case operand.downcase + when '1', 't', 'true', 'y', 'yes' + operand = true + when '0', 'f', 'false', 'n', 'no' + operand = false + else + raise ArgumentError("Invalid operand '#{operand}' for " \ + "boolean attribute '#{attr}'") + end + end cond_out << "#{ar_table_name}.#{attr} #{operator} ?" if (# any operator that operates on value rather than # representation: - operator.match(/[<=>]/) and - model_class.attribute_column(attr).type == :datetime) + operator.match(/[<=>]/) and (attr_type == :datetime)) operand = Time.parse operand end param_out << operand elsif operand.nil? and operator == '=' cond_out << "#{ar_table_name}.#{attr} is null" + elsif operand.nil? and operator == '<>' + cond_out << "#{ar_table_name}.#{attr} is not null" + elsif (attr_type == :boolean) and ['=', '<>'].include?(operator) and + [true, false].include?(operand) + cond_out << "#{ar_table_name}.#{attr} #{operator} ?" + param_out << operand else raise ArgumentError.new("Invalid operand type '#{operand.class}' "\ "for '#{operator}' operator in filters") end - when 'in' + when 'in', 'not in' if operand.is_a? Array - cond_out << "#{ar_table_name}.#{attr} IN (?)" + cond_out << "#{ar_table_name}.#{attr} #{operator} (?)" param_out << operand + if operator == 'not in' and not operand.include?(nil) + # explicitly allow NULL + cond_out[-1] = "(#{cond_out[-1]} OR #{ar_table_name}.#{attr} IS NULL)" + end else raise ArgumentError.new("Invalid operand type '#{operand.class}' "\ "for '#{operator}' operator in filters")