activerecord-jdbc-adapter 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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