Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

SQL Like escaping helper method. [Rob Gilson & Yves Senn]

Closes #14222.

This is a follow up to #6104

This does not have the backwards compatibility issues brought up in
implementation to break.
  • Loading branch information...
commit fe4b0eee05f59831e1468ed50f55fbad0ce11e1d 1 parent d46771b
@D1plo1d D1plo1d authored senny committed
View
16 activerecord/CHANGELOG.md
@@ -1,3 +1,19 @@
+* `sanitize_sql_like` helper method to escape a string for safe use in a SQL
+ LIKE statement.
+
+ Example:
+
+ class Article
+ def self.search(term)
+ where("title LIKE ?", sanitize_sql_like(term))
+ end
+ end
+
+ Article.search("20% _reduction_")
+ # => Query looks like "... title LIKE '20\% \_reduction\_' ..."
+
+ *Rob Gilson*, *Yves Senn*
+
* Do not quote uuid default value on `change_column`.
Fixes #14604.
View
6 activerecord/lib/active_record/sanitization.rb
@@ -107,6 +107,12 @@ def sanitize_sql_hash_for_assignment(attrs, table)
end.join(', ')
end
+ # Sanitizes a +string+ so that it is safe to use within a sql
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
+ def sanitize_sql_like(string, escape_character = "\\")
+ string.gsub(/[\\_%]/) { |x| [escape_character, x].join }
+ end
+
# Accepts an array of conditions. The array has each value
# sanitized and interpolated into the SQL statement.
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
View
26 activerecord/test/cases/sanitize_test.rb
@@ -51,4 +51,30 @@ def test_sanitize_sql_array_handles_empty_statement
select_author_sql = Post.send(:sanitize_sql_array, [''])
assert_equal('', select_author_sql)
end
+
+ def test_sanitize_sql_like
+ assert_equal '100\%', Binary.send(:sanitize_sql_like, '100%')
+ assert_equal 'snake\_cased\_string', Binary.send(:sanitize_sql_like, 'snake_cased_string')
+ assert_equal 'C:\\\\Programs\\\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint')
+ assert_equal 'normal string 42', Binary.send(:sanitize_sql_like, 'normal string 42')
+ end
+
+ def test_sanitize_sql_like_with_custom_escape_character
+ assert_equal '100!%', Binary.send(:sanitize_sql_like, '100%', '!')
+ assert_equal 'snake!_cased!_string', Binary.send(:sanitize_sql_like, 'snake_cased_string', '!')
+ assert_equal 'C:!\\Programs!\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint', '!')
+ assert_equal 'normal string 42', Binary.send(:sanitize_sql_like, 'normal string 42', '!')
+ end
+
+ def test_sanitize_sql_like_example_use_case
+ searchable_post = Class.new(Post) do
+ def self.search(term)
+ where("title LIKE ?", sanitize_sql_like(term))
+ end
+ end
+
+ assert_sql /LIKE '20\\% \\_reduction\\_'/ do
+ searchable_post.search("20% _reduction_").to_a
+ end
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.