Skip to content

Commit e3a2fae

Browse files
committed
Add add_limit_offset! to adapters.
1 parent ed21f0c commit e3a2fae

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,29 @@ def commit_db_transaction() end
181181
# done if the transaction block raises an exception or returns false.
182182
def rollback_db_transaction() end
183183

184+
# Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
185+
# fragment that has the same semantics as LIMIT and OFFSET.
186+
#
187+
# +options+ must be a Hash which contains a +:limit+ option
188+
# and an +:offset+ option.
189+
#
190+
# This method *modifies* the +sql+ parameter.
191+
#
192+
# ===== Examples
193+
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
194+
# generates
195+
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
196+
197+
def add_limit_offset!(sql, options)
198+
if limit = options[:limit]
199+
sql << " LIMIT #{sanitize_limit(limit)}"
200+
end
201+
if offset = options[:offset]
202+
sql << " OFFSET #{offset.to_i}"
203+
end
204+
sql
205+
end
206+
184207
def default_sequence_name(table, column)
185208
nil
186209
end

activerecord/lib/active_record/connection_adapters/mysql_adapter.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,18 @@ def release_savepoint
375375
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
376376
end
377377

378+
def add_limit_offset!(sql, options) #:nodoc:
379+
limit, offset = options[:limit], options[:offset]
380+
if limit && offset
381+
sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
382+
elsif limit
383+
sql << " LIMIT #{sanitize_limit(limit)}"
384+
elsif offset
385+
sql << " OFFSET #{offset.to_i}"
386+
end
387+
sql
388+
end
389+
378390
# SCHEMA STATEMENTS ========================================
379391

380392
def structure_dump #:nodoc:

activerecord/test/cases/adapter_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,25 @@ def test_foreign_key_violations_are_translated_to_specific_exception
142142
end
143143
end
144144
end
145+
146+
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
147+
sql_inject = "1 select * from schema"
148+
assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit => sql_inject)
149+
if current_adapter?(:MysqlAdapter)
150+
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7)
151+
else
152+
assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7)
153+
end
154+
end
155+
156+
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
157+
sql_inject = "1, 7 procedure help()"
158+
if current_adapter?(:MysqlAdapter)
159+
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit => sql_inject)
160+
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit => '1 ; DROP TABLE USERS', :offset => 7)
161+
else
162+
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit => sql_inject)
163+
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7)
164+
end
165+
end
145166
end

0 commit comments

Comments
 (0)