Feature #38423 ยป 38423.patch
app/models/query.rb | ||
---|---|---|
313 | 313 |
"!p" => :label_no_issues_in_project, |
314 | 314 |
"*o" => :label_any_open_issues, |
315 | 315 |
"!o" => :label_no_open_issues, |
316 |
"/" => :label_matches_regexp |
|
316 | 317 |
} |
317 | 318 | |
318 | 319 |
class_attribute :operators_by_filter_type |
... | ... | |
323 | 324 |
:list_subprojects => [ "*", "!*", "=", "!" ], |
324 | 325 |
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ], |
325 | 326 |
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ], |
326 |
:string => [ "~", "=", "!~", "!", "^", "$", "!*", "*" ], |
|
327 |
:text => [ "~", "!~", "^", "$", "!*", "*" ], |
|
327 |
:string => [ "~", "=", "/", "!~", "!", "^", "$", "!*", "*" ],
|
|
328 |
:text => [ "~", "/", "!~", "^", "$", "!*", "*" ],
|
|
328 | 329 |
:search => [ "~", "!~" ], |
329 | 330 |
:integer => [ "=", ">=", "<=", "><", "!*", "*" ], |
330 | 331 |
:float => [ "=", ">=", "<=", "><", "!*", "*" ], |
331 | 332 |
:relation => ["=", "!", "=p", "=!p", "!p", "*o", "!o", "!*", "*"], |
332 | 333 |
:tree => ["=", "~", "!*", "*"] |
333 | 334 |
} |
335 |
unless Redmine::Database.supports_regexp? |
|
336 |
operators_by_filter_type[:string].delete('/') |
|
337 |
operators_by_filter_type[:text].delete('/') |
|
338 |
end |
|
334 | 339 | |
335 | 340 |
class_attribute :available_columns |
336 | 341 |
self.available_columns = [] |
... | ... | |
1435 | 1440 |
sql = sql_contains("#{db_table}.#{db_field}", value.first, :starts_with => true) |
1436 | 1441 |
when "$" |
1437 | 1442 |
sql = sql_contains("#{db_table}.#{db_field}", value.first, :ends_with => true) |
1443 |
when "/" |
|
1444 |
operator = Redmine::Database.regexp_operator |
|
1445 |
unless operator.nil? |
|
1446 |
sql = queried_class.send(:sanitize_sql_for_conditions, ["#{db_table}.#{db_field} #{operator} ?", value]) |
|
1447 |
end |
|
1438 | 1448 |
else |
1439 | 1449 |
raise QueryError, "Unknown query operator #{operator}" |
1440 | 1450 |
end |
config/locales/en.yml | ||
---|---|---|
813 | 813 |
label_not_contains: doesn't contain |
814 | 814 |
label_starts_with: starts with |
815 | 815 |
label_ends_with: ends with |
816 |
label_matches_regexp: matches regexp |
|
816 | 817 |
label_any_issues_in_project: any issues in project |
817 | 818 |
label_any_issues_not_in_project: any issues not in project |
818 | 819 |
label_no_issues_in_project: no issues in project |
lib/redmine/database.rb | ||
---|---|---|
61 | 61 |
/mysql/i.match?(ActiveRecord::Base.connection.adapter_name) |
62 | 62 |
end |
63 | 63 | |
64 |
# Returns the MysQL version or nil if another DBMS is used |
|
65 |
def mysql_version |
|
66 |
mysql? ? ActiveRecord::Base.connection.select_value('SELECT VERSION()').to_s : nil |
|
67 |
end |
|
68 | ||
64 | 69 |
# Returns a SQL statement for case/accent (if possible) insensitive match |
65 | 70 |
def like(left, right, options={}) |
66 | 71 |
neg = (options[:match] == false ? 'NOT ' : '') |
... | ... | |
103 | 108 |
def reset |
104 | 109 |
@postgresql_unaccent = nil |
105 | 110 |
end |
111 | ||
112 |
# Returns true if the database supports regular expressions |
|
113 |
def supports_regexp? |
|
114 |
regexp_operator.present? |
|
115 |
end |
|
116 | ||
117 |
# Returns the regexp operator for the current database |
|
118 |
# MySQL 8.0.4+ and PostgreSQL are supported |
|
119 |
def regexp_operator |
|
120 |
@regexp_operator ||= |
|
121 |
if mysql? && Gem::Version.new(mysql_version) >= Gem::Version.new('8.0.4') |
|
122 |
'REGEXP' |
|
123 |
elsif postgresql? |
|
124 |
'~*' |
|
125 |
else |
|
126 |
nil |
|
127 |
end |
|
128 |
end |
|
106 | 129 |
end |
107 | 130 |
end |
108 | 131 |
end |
test/unit/query_test.rb | ||
---|---|---|
710 | 710 |
assert_not_include issue, result |
711 | 711 |
end |
712 | 712 | |
713 |
def test_operator_regexp |
|
714 |
skip unless Redmine::Database.supports_regexp? |
|
715 | ||
716 |
regexp_str = '(Recipes{0,1}|ingred.ents)' |
|
717 |
query = IssueQuery.new(:name => '_') |
|
718 |
query.add_filter('subject', '/', [regexp_str]) |
|
719 |
result = find_issues_with_query(query) |
|
720 |
assert_equal [1, 2, 3], result.map(&:id).sort |
|
721 |
result.each {|issue| assert issue.subject =~ /#{regexp_str}/i} |
|
722 |
end |
|
723 | ||
713 | 724 |
def test_range_for_this_week_with_week_starting_on_monday |
714 | 725 |
I18n.locale = :fr |
715 | 726 |
assert_equal '1', I18n.t(:general_first_day_of_week) |