Skip to content
This repository
Browse code

Added the option for sanitizing find_by_sql and the offset parts in r…

…egular finds [Sam Stephenson]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@75 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit a775cb190312edba8ef3feb6345ac446d7e8f113 1 parent 7a29764
David Heinemeier Hansson authored December 07, 2004
5  activerecord/CHANGELOG
... ...
@@ -1,5 +1,10 @@
1 1
 *CVS*
2 2
 
  3
+* Added the option for sanitizing find_by_sql and the offset parts in regular finds [Sam Stephenson]. Examples:
  4
+
  5
+    Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
  6
+    Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
  7
+
3 8
 * Fixed value quoting in all generated SQL statements, so that integers are not surrounded in quotes and that all sanitation are happening
4 9
   through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer
5 10
   columns.
13  activerecord/lib/active_record/base.rb
@@ -274,21 +274,25 @@ def find_on_conditions(id, conditions)
274 274
       # Returns an array of all the objects that could be instantiated from the associated
275 275
       # table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
276 276
       # such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
277  
-      # such as by "last_name, first_name DESC". A maximum of returned objects can be specified in +limit+. Example:
  277
+      # such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in 
  278
+      # +limit+ (LIMIT...OFFSET-part). Examples:
278 279
       #   Project.find_all "category = 'accounts'", "last_accessed DESC", 15
  280
+      #   Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
279 281
       def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil)
280 282
         sql  = "SELECT * FROM #{table_name} " 
281 283
         sql << "#{joins} " if joins
282 284
         add_conditions!(sql, conditions)
283 285
         sql << "ORDER BY #{orderings} " unless orderings.nil?
284  
-        sql << "LIMIT #{limit} " unless limit.nil?
  286
+        sql << "LIMIT #{sanitize_conditions(limit)} " unless limit.nil?
285 287
     
286 288
         find_by_sql(sql)
287 289
       end
288 290
   
289  
-      # Works like find_all, but requires a complete SQL string. Example:
  291
+      # Works like find_all, but requires a complete SQL string. Examples:
290 292
       #   Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
  293
+      #   Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
291 294
       def find_by_sql(sql)
  295
+        sql = sanitize_conditions(sql)
292 296
         connection.select_all(sql, "#{name} Load").inject([]) { |objects, record| objects << instantiate(record) }
293 297
       end
294 298
       
@@ -360,6 +364,7 @@ def count(conditions = nil)
360 364
       # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
361 365
       #   Product.count "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
362 366
       def count_by_sql(sql)
  367
+        sql = sanitize_conditions(sql)
363 368
         count = connection.select_one(sql, "#{name} Count").values.first
364 369
         return count ? count.to_i : 0
365 370
       end
@@ -1073,4 +1078,4 @@ def has_yaml_encoding_header?(string)
1073 1078
         string[0..3] == "--- "
1074 1079
       end
1075 1080
   end
1076  
-end
  1081
+end
37  activerecord/test/finder_test.rb
... ...
@@ -1,11 +1,13 @@
1 1
 require 'abstract_unit'
2 2
 require 'fixtures/company'
3 3
 require 'fixtures/topic'
  4
+require 'fixtures/entrant'
4 5
 
5 6
 class FinderTest < Test::Unit::TestCase
6 7
   def setup
7 8
     @company_fixtures = create_fixtures("companies")
8 9
     @topic_fixtures   = create_fixtures("topics")
  10
+    @entrant_fixtures = create_fixtures("entrants")
9 11
   end
10 12
   
11 13
   def test_find
@@ -23,6 +25,20 @@ def test_find_by_ids_missing_one
23 25
     }
24 26
   end
25 27
   
  28
+  def test_find_all_with_limit
  29
+    entrants = Entrant.find_all nil, "id ASC", 2
  30
+    
  31
+    assert_equal(2, entrants.size)
  32
+    assert_equal(@entrant_fixtures["first"]["name"], entrants.first.name)
  33
+  end
  34
+
  35
+  def test_find_all_with_prepared_limit_and_offset
  36
+    entrants = Entrant.find_all nil, "id ASC", ["? OFFSET ?", 2, 1]
  37
+    
  38
+    assert_equal(2, entrants.size)
  39
+    assert_equal(@entrant_fixtures["second"]["name"], entrants.first.name)
  40
+  end
  41
+
26 42
   def test_find_with_entire_select_statement
27 43
     topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
28 44
     
@@ -30,6 +46,13 @@ def test_find_with_entire_select_statement
30 46
     assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
31 47
   end
32 48
   
  49
+  def test_find_with_prepared_select_statement
  50
+    topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
  51
+    
  52
+    assert_equal(1, topics.size)
  53
+    assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
  54
+  end
  55
+  
33 56
   def test_find_first
34 57
     first = Topic.find_first "title = 'The First Topic'"
35 58
     assert_equal(@topic_fixtures["first"]["title"], first.title)
@@ -71,4 +94,16 @@ def test_string_sanitation
71 94
     assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
72 95
     assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
73 96
   end
74  
-end
  97
+
  98
+  def test_count
  99
+    assert_equal(0, Entrant.count("id > 3"))
  100
+    assert_equal(1, Entrant.count(["id > ?", 2]))
  101
+    assert_equal(2, Entrant.count(["id > ?", 1]))
  102
+  end
  103
+
  104
+  def test_count_by_sql
  105
+    assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
  106
+    assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
  107
+    assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
  108
+  end
  109
+end

0 notes on commit a775cb1

Please sign in to comment.
Something went wrong with that request. Please try again.