aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2010-10-12 15:57:26 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2010-10-26 13:44:07 -0700
commitcc468d3ec81d6f1298fca91c0549584b36dafcc6 (patch)
tree02c6e8949a86127d7ad9c08aef18be9c7a966063 /activerecord
parent6ceffb8178b6d419d4453e1e24d4138215c35217 (diff)
downloadrails-cc468d3ec81d6f1298fca91c0549584b36dafcc6.tar.gz
rails-cc468d3ec81d6f1298fca91c0549584b36dafcc6.tar.bz2
rails-cc468d3ec81d6f1298fca91c0549584b36dafcc6.zip
exec returns an AR::Result
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/base.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb26
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/result.rb30
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb35
-rw-r--r--activerecord/test/cases/helper.rb8
8 files changed, 104 insertions, 8 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 053b796991..db7f9b7974 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -448,8 +448,8 @@ module ActiveRecord #:nodoc:
# # You can use the same string replacement techniques as you can with ActiveRecord#find
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
# > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
- def find_by_sql(sql)
- connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
+ def find_by_sql(sql, bind_values = [])
+ connection.select_all(sanitize_sql(sql), "#{name} Load", bind_values).collect! { |record| instantiate(record) }
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index cf67185b83..0e4f68e85c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -3,8 +3,8 @@ module ActiveRecord
module DatabaseStatements
# Returns an array of record hashes with the column names as keys and
# column values as values.
- def select_all(sql, name = nil)
- select(sql, name)
+ def select_all(sql, name = nil, bind_values = [])
+ select(sql, name, bind_values)
end
# Returns a record hash with the column names as keys and column values
@@ -260,7 +260,7 @@ module ActiveRecord
protected
# Returns an array of record hashes with the column names as keys and
# column values as values.
- def select(sql, name = nil)
+ def select(sql, name = nil, bind_values = [])
end
undef_method :select
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index bcbef526bd..a36a182a9e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -12,6 +12,7 @@ require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
require 'active_record/connection_adapters/abstract/database_limits'
+require 'active_record/result'
module ActiveRecord
module ConnectionAdapters # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 69bf2c0dcd..75bf579764 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -50,6 +50,7 @@ module ActiveRecord
def initialize(connection, logger, config)
super(connection, logger)
+ @statements = {}
@config = config
end
@@ -131,6 +132,27 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
+ def exec(sql, name = nil, bind_values = [])
+ log(sql, name) do
+
+ # Don't cache statements without bind values
+ if bind_values.empty?
+ stmt = @connection.prepare(sql)
+ cols = stmt.columns
+ else
+ cache = @statements[sql] ||= {
+ :stmt => @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
+ cols = cache[:cols] ||= stmt.columns
+ stmt.reset!
+ stmt.bind_params bind_values.map { |col, val| val }
+ end
+
+ ActiveRecord::Result.new(cols, stmt.to_a)
+ end
+ end
+
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
@@ -280,8 +302,8 @@ module ActiveRecord
end
protected
- def select(sql, name = nil) #:nodoc:
- execute(sql, name).map do |row|
+ def select(sql, name = nil, bind_values = []) #:nodoc:
+ exec(sql, name, bind_values).map do |row|
record = {}
row.each do |key, value|
record[key.sub(/^"?\w+"?\./, '')] = value if key.is_a?(String)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 4c75dff891..58f7b74198 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -61,7 +61,7 @@ module ActiveRecord
def to_a
return @records if loaded?
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
preload = @preload_values
preload += @includes_values unless eager_loading?
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
new file mode 100644
index 0000000000..8deff1478f
--- /dev/null
+++ b/activerecord/lib/active_record/result.rb
@@ -0,0 +1,30 @@
+module ActiveRecord
+ ###
+ # This class encapsulates a Result returned from calling +exec+ on any
+ # database connection adapter. For example:
+ #
+ # x = ActiveRecord::Base.connection.exec('SELECT * FROM foo')
+ # x # => #<ActiveRecord::Result:0xdeadbeef>
+ class Result
+ include Enumerable
+
+ attr_reader :columns, :rows
+
+ def initialize(columns, rows)
+ @columns = columns
+ @rows = rows
+ @hash_rows = nil
+ end
+
+ def each
+ hash_rows.each { |row| yield row }
+ end
+
+ private
+ def hash_rows
+ @hash_rows ||= @rows.map { |row|
+ ActiveSupport::OrderedHash[@columns.zip(row)]
+ }
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index f4a856e42a..52630c5ad4 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -60,6 +60,41 @@ module ActiveRecord
bind_param = conn.substitute_for('foo', [])
assert_equal Arel.sql('?'), bind_param
end
+
+ def test_exec_no_binds
+ conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+
+ conn.exec('create table ex(id int, data string)')
+ result = conn.exec('SELECT id, data FROM ex')
+ assert_equal 0, result.rows.length
+ assert_equal 2, result.columns.length
+ assert_equal %w{ id data }, result.columns
+
+ conn.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = conn.exec('SELECT id, data FROM ex')
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
+ def test_exec_with_binds
+ conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+
+ conn.exec('create table ex(id int, data string)')
+ conn.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = conn.exec(
+ 'SELECT id, data FROM ex WHERE id = ?', nil, [[nil, 1]])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
end
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 2d3047c875..f6ef155d66 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -55,6 +55,14 @@ ActiveRecord::Base.connection.class.class_eval do
end
alias_method_chain :execute, :query_record
+
+ def exec_with_query_record(sql, name = nil, binds = [], &block)
+ $queries_executed ||= []
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
+ exec_without_query_record(sql, name, binds, &block)
+ end
+
+ alias_method_chain :exec, :query_record
end
ActiveRecord::Base.connection.class.class_eval {