activerecord-jdbc-adapter 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +22 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +42 -0
  4. data/History.txt +10 -3
  5. data/{README.txt → README.rdoc} +30 -14
  6. data/Rakefile +49 -0
  7. data/activerecord-jdbc-adapter.gemspec +23 -0
  8. data/bench/bench_attributes.rb +13 -0
  9. data/bench/bench_attributes_new.rb +14 -0
  10. data/bench/bench_create.rb +12 -0
  11. data/bench/bench_find_all.rb +12 -0
  12. data/bench/bench_find_all_mt.rb +25 -0
  13. data/bench/bench_model.rb +85 -0
  14. data/bench/bench_new.rb +12 -0
  15. data/bench/bench_new_valid.rb +12 -0
  16. data/bench/bench_valid.rb +13 -0
  17. data/lib/activerecord-jdbc-adapter.rb +1 -1
  18. data/lib/arel/visitors/derby.rb +5 -1
  19. data/lib/arjdbc.rb +12 -17
  20. data/lib/arjdbc/db2/adapter.rb +91 -4
  21. data/lib/arjdbc/derby/adapter.rb +8 -8
  22. data/lib/arjdbc/derby/connection_methods.rb +1 -0
  23. data/lib/arjdbc/firebird/adapter.rb +4 -4
  24. data/lib/arjdbc/h2/adapter.rb +3 -2
  25. data/lib/arjdbc/h2/connection_methods.rb +1 -0
  26. data/lib/arjdbc/hsqldb/adapter.rb +5 -2
  27. data/lib/arjdbc/hsqldb/connection_methods.rb +1 -0
  28. data/lib/arjdbc/informix/adapter.rb +2 -2
  29. data/lib/arjdbc/informix/connection_methods.rb +1 -0
  30. data/lib/arjdbc/jdbc/adapter.rb +92 -22
  31. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  32. data/lib/arjdbc/mimer/adapter.rb +4 -4
  33. data/lib/arjdbc/mssql/adapter.rb +10 -8
  34. data/lib/arjdbc/mssql/connection_methods.rb +1 -0
  35. data/lib/arjdbc/mssql/limit_helpers.rb +7 -4
  36. data/lib/arjdbc/mysql/adapter.rb +30 -6
  37. data/lib/arjdbc/mysql/connection_methods.rb +1 -0
  38. data/lib/arjdbc/oracle/adapter.rb +15 -12
  39. data/lib/arjdbc/oracle/connection_methods.rb +1 -0
  40. data/lib/arjdbc/postgresql/adapter.rb +35 -16
  41. data/lib/arjdbc/postgresql/connection_methods.rb +1 -0
  42. data/lib/arjdbc/sqlite3/adapter.rb +13 -10
  43. data/lib/arjdbc/sqlite3/connection_methods.rb +1 -0
  44. data/lib/arjdbc/version.rb +1 -1
  45. data/lib/generators/jdbc/USAGE +10 -0
  46. data/pom.xml +57 -0
  47. data/rakelib/rails.rake +1 -1
  48. data/rakelib/test.rake +20 -6
  49. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4 -1
  50. data/src/java/arjdbc/jdbc/SQLBlock.java +1 -1
  51. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +74 -0
  52. data/test/abstract_db_create.rb +8 -1
  53. data/test/activerecord/jall.sh +7 -0
  54. data/test/activerecord/jtest.sh +3 -0
  55. data/test/jdbc_common.rb +6 -4
  56. data/test/models/data_types.rb +1 -1
  57. data/test/models/string_id.rb +2 -0
  58. data/test/models/thing.rb +16 -0
  59. data/test/mysql_index_length_test.rb +58 -0
  60. data/test/mysql_info_test.rb +3 -3
  61. data/test/postgres_db_create_test.rb +1 -1
  62. data/test/simple.rb +11 -3
  63. data/test/sqlite3_simple_test.rb +16 -2
  64. metadata +167 -108
  65. data/.gemtest +0 -0
  66. data/Manifest.txt +0 -187
  67. data/rakelib/package.rake +0 -92
  68. data/test/pick_rails_version.rb +0 -3
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/bench_model'
2
+
3
+ puts "Widget.new"
4
+ Benchmark.bm do |make|
5
+ TIMES.times do
6
+ make.report do
7
+ 100_000.times do
8
+ Widget.new
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/bench_model'
2
+
3
+ puts "Widget.new.valid?"
4
+ Benchmark.bm do |make|
5
+ TIMES.times do
6
+ make.report do
7
+ 100_000.times do
8
+ Widget.new.valid?
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/bench_model'
2
+
3
+ puts "Widget.new.valid?"
4
+ Benchmark.bm do |make|
5
+ TIMES.times do
6
+ make.report do
7
+ widget = Widget.new
8
+ 100_000.times do
9
+ widget.valid?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  require 'arjdbc'
2
- if ActiveRecord::VERSION::MAJOR >= 3
2
+ if defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::MAJOR >= 3
3
3
  begin
4
4
  require 'arjdbc/jdbc/railtie'
5
5
  rescue LoadError
@@ -7,12 +7,16 @@ module Arel
7
7
  [
8
8
  o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
9
9
  ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
10
- ("FETCH FIRST #{limit_for(o.limit)} ROWS ONLY" if o.limit),
10
+ (visit_Arel_Nodes_Limit(o.limit) if o.limit),
11
11
  (visit(o.offset) if o.offset),
12
12
  (visit(o.lock) if o.lock),
13
13
  ].compact.join ' '
14
14
  end
15
15
 
16
+ def visit_Arel_Nodes_Limit o
17
+ "FETCH FIRST #{limit_for(o)} ROWS ONLY"
18
+ end
19
+
16
20
  def visit_Arel_Nodes_Offset o
17
21
  "OFFSET #{visit o.value} ROWS"
18
22
  end
data/lib/arjdbc.rb CHANGED
@@ -1,26 +1,21 @@
1
1
  if defined?(JRUBY_VERSION)
2
2
  begin
3
- tried_gem ||= false
4
3
  require 'active_record/version'
5
- rescue LoadError
6
- raise if tried_gem
7
- require 'rubygems'
8
- gem 'activerecord'
9
- tried_gem = true
10
- retry
11
- end
12
- if ActiveRecord::VERSION::MAJOR < 2
13
- if defined?(RAILS_CONNECTION_ADAPTERS)
14
- RAILS_CONNECTION_ADAPTERS << %q(jdbc)
4
+ if ActiveRecord::VERSION::MAJOR < 2
5
+ if defined?(RAILS_CONNECTION_ADAPTERS)
6
+ RAILS_CONNECTION_ADAPTERS << %q(jdbc)
7
+ else
8
+ RAILS_CONNECTION_ADAPTERS = %w(jdbc)
9
+ end
10
+ if ActiveRecord::VERSION::MAJOR == 1 && ActiveRecord::VERSION::MINOR == 14
11
+ require 'arjdbc/jdbc'
12
+ end
15
13
  else
16
- RAILS_CONNECTION_ADAPTERS = %w(jdbc)
17
- end
18
- if ActiveRecord::VERSION::MAJOR == 1 && ActiveRecord::VERSION::MINOR == 14
14
+ require 'active_record'
19
15
  require 'arjdbc/jdbc'
20
16
  end
21
- else
22
- require 'active_record'
23
- require 'arjdbc/jdbc'
17
+ rescue LoadError
18
+ warn "activerecord-jdbc-adapter requires ActiveRecord at runtime"
24
19
  end
25
20
  else
26
21
  warn "activerecord-jdbc-adapter is for use with JRuby only"
@@ -1,7 +1,29 @@
1
1
  module ArJdbc
2
2
  module DB2
3
+ def self.extended(base)
4
+ if base.zos?
5
+ unless @lob_callback_added
6
+ ActiveRecord::Base.class_eval do
7
+ def after_save_with_db2zos_blob
8
+ lobfields = self.class.columns.select { |c| c.sql_type =~ /blob|clob/i }
9
+ lobfields.each do |c|
10
+ value = self[c.name]
11
+ value = value.to_yaml if unserializable_attribute?(c.name, c)
12
+ next if value.nil?
13
+ connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
14
+ end
15
+ end
16
+ end
17
+
18
+ ActiveRecord::Base.after_save :after_save_with_db2zos_blob
19
+
20
+ @lob_callback_added = true
21
+ end
22
+ end
23
+ end
24
+
3
25
  def self.column_selector
4
- [ /(db2|as400)/i,
26
+ [ /(db2|as400|zos)/i,
5
27
  lambda { |cfg, column| column.extend(::ArJdbc::DB2::Column) } ]
6
28
  end
7
29
 
@@ -147,15 +169,53 @@ module ArJdbc
147
169
  'DB2'
148
170
  end
149
171
 
150
- def arel2_visitors
172
+ def self.arel2_visitors(config)
151
173
  require 'arel/visitors/db2'
152
- {'db2' => ::Arel::Visitors::DB2, 'as400' => ::Arel::Visitors::DB2}
174
+ {}.tap {|v| %w(db2 as400).each {|a| v[a] = ::Arel::Visitors::DB2 } }
153
175
  end
154
176
 
155
177
  def add_limit_offset!(sql, options)
156
178
  replace_limit_offset!(sql, options[:limit], options[:offset])
157
179
  end
158
180
 
181
+
182
+ def create_table(name, options = {}) #:nodoc:
183
+ if zos?
184
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(self)
185
+
186
+ table_definition.primary_key(options[:primary_key] || ActiveRecord::Base.get_primary_key(name)) unless options[:id] == false
187
+
188
+ yield table_definition
189
+
190
+ # Clobs in DB2 Host have to be created after the Table with an auxiliary Table.
191
+ # First: Save them for later in Array "clobs"
192
+ clobs =table_definition.columns.select { |x| x.type == "text" }
193
+
194
+ # Second: and delete them from the original Colums-Array
195
+ table_definition.columns.delete_if { |x| x.type=="text" }
196
+
197
+ if options[:force] && table_exists?(name)
198
+ super.drop_table(name, options)
199
+ end
200
+
201
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
202
+ create_sql << "#{quote_table_name(name)} ("
203
+ create_sql << table_definition.to_sql
204
+ create_sql << ") #{options[:options]}"
205
+ create_sql << " IN #{@config[:database]}.#{@config[:tablespace]}"
206
+
207
+ execute create_sql
208
+
209
+ clobs.each do |clob_column|
210
+ execute "ALTER TABLE #{name+" ADD COLUMN "+clob_column.name.to_s+" clob"}"
211
+ execute "CREATE AUXILIARY TABLE #{name+"_"+clob_column.name.to_s+"_CD_"} IN #{@config[:database]}.#{@config[:lob_tablespaces][name.split(".")[1]]} STORES #{name} COLUMN "+clob_column.name.to_s
212
+ execute "CREATE UNIQUE INDEX #{name+"_"+clob_column.name.to_s+"_CD_"} ON #{name+"_"+clob_column.name.to_s+"_CD_"};"
213
+ end
214
+ else
215
+ super(name, options)
216
+ end
217
+ end
218
+
159
219
  def replace_limit_offset!(sql, limit, offset)
160
220
  if limit
161
221
  limit = limit.to_i
@@ -200,7 +260,11 @@ module ArJdbc
200
260
  if column && column.type == :binary
201
261
  "BLOB('#{quote_string(value)}')"
202
262
  else
203
- "'#{quote_string(value)}'"
263
+ if zos? && column.type == :text
264
+ "'if_you_see_this_value_the_after_save_hook_in_db2_zos_adapter_went_wrong'"
265
+ else
266
+ "'#{quote_string(value)}'"
267
+ end
204
268
  end
205
269
  else super
206
270
  end
@@ -228,6 +292,21 @@ module ArJdbc
228
292
  tables.each {|table| drop_table("#{db2_schema}.#{table}")}
229
293
  end
230
294
 
295
+ def add_index(table_name, column_name, options = {})
296
+ if (!zos? || (table_name.to_s == ActiveRecord::Migrator.schema_migrations_table_name.to_s))
297
+ super
298
+ else
299
+ statement ="CREATE"
300
+ statement << " UNIQUE " if options[:unique]
301
+ statement << " INDEX "+"#{ActiveRecord::Base.table_name_prefix}#{options[:name]} "
302
+
303
+ statement << " ON #{table_name}(#{column_name})"
304
+
305
+ execute statement
306
+ end
307
+ end
308
+
309
+
231
310
  def remove_index(table_name, options = { })
232
311
  execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
233
312
  end
@@ -311,6 +390,10 @@ module ArJdbc
311
390
  def columns(table_name, name = nil)
312
391
  cols = @connection.columns(table_name, name, db2_schema)
313
392
 
393
+ if zos?
394
+ # Remove the mighty db2_generated_rowid_for_lobs from the list of columns
395
+ cols = cols.reject { |col| "db2_generated_rowid_for_lobs" == col.name }
396
+ end
314
397
  # scrub out sizing info when CREATE TABLE doesn't support it
315
398
  # but JDBC reports it (doh!)
316
399
  for col in cols
@@ -398,6 +481,10 @@ module ArJdbc
398
481
  definition
399
482
  end
400
483
 
484
+ def zos?
485
+ @config[:driver] == "com.ibm.db2.jcc.DB2Driver"
486
+ end
487
+
401
488
  private
402
489
  def as400?
403
490
  @config[:url] =~ /^jdbc:as400:/
@@ -7,7 +7,7 @@ module ::ArJdbc
7
7
  end
8
8
 
9
9
  def self.monkey_rails
10
- unless @already_monkeyd
10
+ unless defined?(@already_monkeyd)
11
11
  # Needed because Rails is broken wrt to quoting of
12
12
  # some values. Most databases are nice about it,
13
13
  # but not Derby. The real issue is that you can't
@@ -50,7 +50,7 @@ module ::ArJdbc
50
50
  def default_value(value)
51
51
  # jdbc returns column default strings with actual single quotes around the value.
52
52
  return $1 if value =~ /^'(.*)'$/
53
-
53
+ return nil if value == "GENERATED_BY_DEFAULT"
54
54
  value
55
55
  end
56
56
  end
@@ -59,9 +59,9 @@ module ::ArJdbc
59
59
  'Derby'
60
60
  end
61
61
 
62
- def arel2_visitors
62
+ def self.arel2_visitors(config)
63
63
  require 'arel/visitors/derby'
64
- {'derby' => ::Arel::Visitors::Derby, 'jdbcderby' => ::Arel::Visitors::Derby}
64
+ {}.tap {|v| %w(derby jdbcderby).each {|a| v[a] = ::Arel::Visitors::Derby } }
65
65
  end
66
66
 
67
67
  include ArJdbc::MissingFunctionalityHelper
@@ -171,7 +171,8 @@ module ::ArJdbc
171
171
  execute(add_column_sql)
172
172
  end
173
173
 
174
- def execute(sql, name = nil)
174
+ def execute(sql, name = nil, binds = [])
175
+ sql = extract_sql(sql)
175
176
  if sql =~ /\A\s*(UPDATE|INSERT)/i
176
177
  i = sql =~ /\swhere\s/im
177
178
  if i
@@ -196,8 +197,7 @@ module ::ArJdbc
196
197
 
197
198
  # construct a clean list of column names from the ORDER BY clause, removing
198
199
  # any asc/desc modifiers
199
- order_columns = order_by.split(',').collect { |s| s.split.first }
200
- order_columns.delete_if(&:blank?)
200
+ order_columns = [order_by].flatten.map{|o| o.split(',').collect { |s| s.split.first } }.flatten.reject(&:blank?)
201
201
  order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
202
202
 
203
203
  # return a DISTINCT clause that's distinct on the columns we want but includes
@@ -316,7 +316,7 @@ module ::ArJdbc
316
316
  end
317
317
 
318
318
  def quote_column_name(name) #:nodoc:
319
- %Q{"#{name.to_s.upcase.gsub(/"/, '""')}"}
319
+ %Q{"#{name.to_s.upcase.gsub(/\"/, '""')}"}
320
320
  end
321
321
 
322
322
  def quoted_true
@@ -4,6 +4,7 @@ module ActiveRecord
4
4
  def derby_connection(config)
5
5
  config[:url] ||= "jdbc:derby:#{config[:database]};create=true"
6
6
  config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
7
+ config[:adapter_spec] = ::ArJdbc::Derby
7
8
  conn = embedded_driver(config)
8
9
  md = conn.jdbc_connection.meta_data
9
10
  if md.database_major_version < 10 || (md.database_major_version == 10 && md.database_minor_version < 5)
@@ -23,9 +23,9 @@ module ::ArJdbc
23
23
  'Firebird'
24
24
  end
25
25
 
26
- def arel2_visitors
26
+ def self.arel2_visitors(config)
27
27
  require 'arel/visitors/firebird'
28
- {'firebird' => ::Arel::Visitors::Firebird, 'firebirdsql' => ::Arel::Visitors::Firebird}
28
+ {}.tap {|v| %w(firebird firebirdsql).each {|a| v[a] = ::Arel::Visitors::Firebird } }
29
29
  end
30
30
 
31
31
  def modify_types(tp)
@@ -35,8 +35,8 @@ module ::ArJdbc
35
35
  tp
36
36
  end
37
37
 
38
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
39
- execute(sql, name)
38
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) # :nodoc:
39
+ execute(sql, name, binds)
40
40
  id_value
41
41
  end
42
42
 
@@ -12,8 +12,9 @@ module ArJdbc
12
12
  'H2'
13
13
  end
14
14
 
15
- def arel2_visitors
16
- super.merge 'h2' => ::Arel::Visitors::HSQLDB, 'jdbch2' => ::Arel::Visitors::HSQLDB
15
+ def self.arel2_visitors(config)
16
+ v = HSQLDB.arel2_visitors(config)
17
+ v.merge({}.tap {|v| %w(h2 jdbch2).each {|a| v[a] = ::Arel::Visitors::HSQLDB } })
17
18
  end
18
19
 
19
20
  def h2_adapter
@@ -4,6 +4,7 @@ module ActiveRecord
4
4
  def h2_connection(config)
5
5
  config[:url] ||= "jdbc:h2:#{config[:database]}"
6
6
  config[:driver] ||= "org.h2.Driver"
7
+ config[:adapter_spec] = ::ArJdbc::H2
7
8
  embedded_driver(config)
8
9
  end
9
10
  alias_method :jdbch2_connection, :h2_connection
@@ -27,6 +27,9 @@ module ::ArJdbc
27
27
 
28
28
  # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
29
29
  def default_value(value)
30
+ # H2 auto-generated key default value
31
+ return nil if value =~ /^\(NEXT VALUE FOR/i
32
+
30
33
  # jdbc returns column default strings with actual single quotes around the value.
31
34
  return $1 if value =~ /^'(.*)'$/
32
35
 
@@ -38,9 +41,9 @@ module ::ArJdbc
38
41
  'Hsqldb'
39
42
  end
40
43
 
41
- def arel2_visitors
44
+ def self.arel2_visitors(config)
42
45
  require 'arel/visitors/hsqldb'
43
- {'hsqldb' => ::Arel::Visitors::HSQLDB, 'jdbchsqldb' => ::Arel::Visitors::HSQLDB}
46
+ {}.tap {|v| %w(hsqldb jdbchsqldb).each {|a| v[a] = ::Arel::Visitors::HSQLDB } }
44
47
  end
45
48
 
46
49
  def modify_types(tp)
@@ -5,6 +5,7 @@ module ActiveRecord
5
5
  require "arjdbc/hsqldb"
6
6
  config[:url] ||= "jdbc:hsqldb:#{config[:database]}"
7
7
  config[:driver] ||= "org.hsqldb.jdbcDriver"
8
+ config[:adapter_spec] = ::ArJdbc::HSQLDB
8
9
  embedded_driver(config)
9
10
  end
10
11
 
@@ -130,9 +130,9 @@ module ::ArJdbc
130
130
  end
131
131
 
132
132
  private
133
- def select(sql, name = nil)
133
+ def select(sql, *rest)
134
134
  # Informix does not like "= NULL", "!= NULL", or "<> NULL".
135
- execute(sql.gsub(/(!=|<>)\s*null/i, "IS NOT NULL").gsub(/=\s*null/i, "IS NULL"), name)
135
+ execute(sql.gsub(/(!=|<>)\s*null/i, "IS NOT NULL").gsub(/=\s*null/i, "IS NULL"), *rest)
136
136
  end
137
137
  end # module Informix
138
138
  end # module ::ArJdbc
@@ -4,6 +4,7 @@ class ActiveRecord::Base
4
4
  config[:port] ||= 9088
5
5
  config[:url] ||= "jdbc:informix-sqli://#{config[:host]}:#{config[:port]}/#{config[:database]}:INFORMIXSERVER=#{config[:servername]}"
6
6
  config[:driver] = 'com.informix.jdbc.IfxDriver'
7
+ config[:adapter_spec] = ::ArJdbc::Informix
7
8
  jdbc_connection(config)
8
9
  end
9
10
  end
@@ -25,13 +25,16 @@ module ActiveRecord
25
25
 
26
26
  def initialize(connection, logger, config)
27
27
  @config = config
28
- spec = adapter_spec config
28
+ spec = config[:adapter_spec] || adapter_spec(config)
29
+ config[:adapter_spec] ||= spec
29
30
  unless connection
30
31
  connection_class = jdbc_connection_class spec
31
32
  connection = connection_class.new config
32
33
  end
33
34
  super(connection, logger)
34
- extend spec if spec
35
+ if spec && (config[:adapter_class].nil? || config[:adapter_class] == JdbcAdapter)
36
+ extend spec
37
+ end
35
38
  configure_arel2_visitors(config)
36
39
  connection.adapter = self
37
40
  JndiConnectionPoolCallbacks.prepare(self, connection)
@@ -89,15 +92,27 @@ module ActiveRecord
89
92
  'JDBC'
90
93
  end
91
94
 
92
- def arel2_visitors
93
- {}
95
+ def self.visitor_for(pool)
96
+ config = pool.spec.config
97
+ adapter = config[:adapter]
98
+ adapter_spec = config[:adapter_spec] || self
99
+ if adapter =~ /^(jdbc|jndi)$/
100
+ adapter_spec.arel2_visitors(config).values.first.new(pool)
101
+ else
102
+ adapter_spec.arel2_visitors(config)[adapter].new(pool)
103
+ end
104
+ end
105
+
106
+ def self.arel2_visitors(config)
107
+ { 'jdbc' => ::Arel::Visitors::ToSql }
94
108
  end
95
109
 
96
110
  def configure_arel2_visitors(config)
97
111
  if defined?(::Arel::Visitors::VISITORS)
98
112
  visitors = ::Arel::Visitors::VISITORS
99
113
  visitor = nil
100
- arel2_visitors.each do |k,v|
114
+ adapter_spec = config[:adapter_spec] || self
115
+ adapter_spec.arel2_visitors(config).each do |k,v|
101
116
  visitor = v
102
117
  visitors[k] = v
103
118
  end
@@ -176,7 +191,17 @@ module ActiveRecord
176
191
  @connection.disconnect!
177
192
  end
178
193
 
179
- def execute(sql, name = nil)
194
+ def substitute_binds(manager, binds = [])
195
+ sql = extract_sql(manager)
196
+ if binds.empty?
197
+ sql
198
+ else
199
+ sql.gsub('?') { quote(*binds.shift.reverse) }
200
+ end
201
+ end
202
+
203
+ def execute(sql, name = nil, binds = [])
204
+ sql = substitute_binds(sql, binds)
180
205
  if name == :skip_logging
181
206
  _execute(sql)
182
207
  else
@@ -191,25 +216,31 @@ module ActiveRecord
191
216
  @connection.execute(sql)
192
217
  end
193
218
 
194
- def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
195
- insert_sql(sql, name, pk, id_value, sequence_name)
219
+ def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
220
+ insert_sql(sql, name, pk, id_value, sequence_name, binds)
196
221
  end
197
222
 
198
- def jdbc_update(sql, name = nil) #:nodoc:
199
- execute(sql, name)
223
+ def jdbc_update(sql, name = nil, binds = []) #:nodoc:
224
+ execute(sql, name, binds)
200
225
  end
201
- def jdbc_select_all(sql, name = nil)
202
- select(sql, name)
226
+ def jdbc_select_all(sql, name = nil, binds = [])
227
+ select(sql, name, binds)
203
228
  end
204
229
 
205
- # Allow query caching to work even when we override alias_method_chain'd methods
206
- alias_chained_method :select_all, :query_cache, :jdbc_select_all
207
- alias_chained_method :update, :query_dirty, :jdbc_update
208
- alias_chained_method :insert, :query_dirty, :jdbc_insert
230
+ if ActiveRecord::VERSION::MAJOR < 3
231
+ # Allow query caching to work even when we override alias_method_chain'd methods
232
+ alias_chained_method :select_all, :query_cache, :jdbc_select_all
233
+ alias_chained_method :update, :query_dirty, :jdbc_update
234
+ alias_chained_method :insert, :query_dirty, :jdbc_insert
235
+
236
+ # Do we need this? Not in AR 3.
237
+ def select_one(sql, name = nil)
238
+ select(sql, name).first
239
+ end
240
+ end
209
241
 
210
- # Do we need this? Not in AR 3.
211
- def select_one(sql, name = nil)
212
- select(sql, name).first
242
+ def last_inserted_id(result)
243
+ result
213
244
  end
214
245
 
215
246
  def select_rows(sql, name = nil)
@@ -218,11 +249,41 @@ module ActiveRecord
218
249
  rows
219
250
  end
220
251
 
221
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
222
- id = execute(sql, name = nil)
252
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
253
+ id = execute(sql, name = nil, binds)
223
254
  id_value || id
224
255
  end
225
256
 
257
+ ### Rails 3.1 prepared statement support
258
+
259
+ # Executes +sql+ statement in the context of this connection using
260
+ # +binds+ as the bind substitutes. +name+ is logged along with
261
+ # the executed +sql+ statement.
262
+ def exec_query(sql, name = 'SQL', binds = [])
263
+ execute(sql, name, binds)
264
+ end
265
+
266
+ # Executes insert +sql+ statement in the context of this connection using
267
+ # +binds+ as the bind substitutes. +name+ is the logged along with
268
+ # the executed +sql+ statement.
269
+ def exec_insert(sql, name, binds)
270
+ exec_query(sql, name, binds)
271
+ end
272
+
273
+ # Executes delete +sql+ statement in the context of this connection using
274
+ # +binds+ as the bind substitutes. +name+ is the logged along with
275
+ # the executed +sql+ statement.
276
+ def exec_delete(sql, name, binds)
277
+ exec_query(sql, name, binds)
278
+ end
279
+
280
+ # Executes update +sql+ statement in the context of this connection using
281
+ # +binds+ as the bind substitutes. +name+ is the logged along with
282
+ # the executed +sql+ statement.
283
+ def exec_update(sql, name, binds)
284
+ exec_query(sql, name, binds)
285
+ end
286
+
226
287
  def jdbc_columns(table_name, name = nil)
227
288
  @connection.columns(table_name.to_s)
228
289
  end
@@ -277,7 +338,16 @@ module ActiveRecord
277
338
  puts e.backtrace if $DEBUG || ENV['DEBUG']
278
339
  super
279
340
  end
280
- protected :translate_exception
341
+
342
+ def extract_sql(obj)
343
+ if obj.respond_to? :to_sql
344
+ obj.send :to_sql
345
+ else
346
+ obj
347
+ end
348
+ end
349
+
350
+ protected :translate_exception, :extract_sql
281
351
  end
282
352
  end
283
353
  end