activerecord-jdbc-adapter 0.6 → 0.7

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.
@@ -1,61 +1,80 @@
1
+ == 0.7
2
+
3
+ - PLEASE NOTE: This release is not compatible with JRuby releases earlier than
4
+ 1.0.3 or 1.1b2. If you must use JRuby 1.0.2 or earlier, please install the
5
+ 0.6 release.
6
+ - Release coincides with JRuby 1.0.3 and JRuby 1.1b2 releases
7
+ - Simultaneous support for JRuby trunk and 1.0 branch
8
+ - Get rid of log_no_bench method, so we time SQL execution again.
9
+ - Implement #select_rows
10
+ - MySQL migration and quoting updates
11
+
1
12
  == 0.6
2
13
 
3
- - Gem is renamed to "activerecord-jdbc-adapter" to follow new conventions introduced in Rails 2.0 for third-party adapters. Rails 2.0 compatibility is introduced.
14
+ - Gem is renamed to "activerecord-jdbc-adapter" to follow new conventions
15
+ introduced in Rails 2.0 for third-party adapters. Rails 2.0 compatibility is
16
+ introduced.
4
17
  - Add dependency on ActiveRecord >= 1.14 (from the Rails 1.1.x release)
5
- - New drivers (jdbc-XXX) and adapter (activerecord-jdbcXXX-adapter) gems available separately. See the README.txt file for details.
6
- - Plain "jdbc" driver is still available if you want to use the full driver/url way of specifying the driver.
18
+ - New drivers (jdbc-XXX) and adapter (activerecord-jdbcXXX-adapter) gems
19
+ available separately. See the README.txt file for details.
20
+ - Plain "jdbc" driver is still available if you want to use the full
21
+ driver/url way of specifying the driver.
7
22
  - More bugfixes to Oracle and SQLServer courtesy of Ola & ThoughtWorks
8
23
 
9
24
  == 0.5
10
25
 
11
26
  - Release coincides with JRuby 1.0.1 release
12
- - It is no longer necessary to specify :driver and :url configuration parameters for the mysql,
13
- postgresql, oracle, derby, hsqldb, and h2 adapters. The previous configuration is still
14
- valid and compatible, but for new applications, this makes it possible to use the exact same
15
- database.yml configuration as Rails applications running under native Ruby.
16
- - JDBC drivers can now be dynamically loaded by Ruby code, without being on the classpath prior to
17
- launching JRuby. Simply use "require 'jdbc-driver.jar'" in JRuby code to add it to the runtime
18
- classpath.
27
+ - It is no longer necessary to specify :driver and :url configuration
28
+ parameters for the mysql, postgresql, oracle, derby, hsqldb, and h2
29
+ adapters. The previous configuration is still valid and compatible, but for
30
+ new applications, this makes it possible to use the exact same database.yml
31
+ configuration as Rails applications running under native Ruby.
32
+ - JDBC drivers can now be dynamically loaded by Ruby code, without being on
33
+ the classpath prior to launching JRuby. Simply use "require
34
+ 'jdbc-driver.jar'" in JRuby code to add it to the runtime classpath.
19
35
  - Updates to HSQL, MS SQLServer, Postgres, Oracle and Derby adapters
20
36
 
21
37
  == 0.4
22
38
 
23
39
  - Release coincides with JRuby 1.0 release
24
- - Shoring up PostgreSQL (courtesy Dudley Flanders) and HSQL (courtesy Matthew Williams)
40
+ - Shoring up PostgreSQL (courtesy Dudley Flanders) and HSQL (courtesy Matthew
41
+ Williams)
25
42
  - Fix timestamps on Oracle to use DATE (as everything else)
26
- - Derby fixes: Fix for open result set issue, better structure dump, quoting, column type changing
43
+ - Derby fixes: Fix for open result set issue, better structure dump, quoting,
44
+ column type changing
27
45
  - Sybase type recognition fix (courtesy Dean Mao)
28
46
 
29
47
  == 0.3.1
30
48
 
31
- * Derby critical fixes shortly after 0.3
49
+ - Derby critical fixes shortly after 0.3
32
50
 
33
51
  == 0.3
34
52
 
35
- * Release coincides with JRuby 1.0.0RC1 release
36
- * Improvements for Derby, Postgres, and Oracle, all of which are running
53
+ - Release coincides with JRuby 1.0.0RC1 release
54
+ - Improvements for Derby, Postgres, and Oracle, all of which are running
37
55
  > 95% of AR tests
38
56
 
39
57
  == 0.2.4
40
58
 
41
- * Release coincides with JRuby 0.9.9 release
42
- * JRuby 0.9.9 is required
43
- * MySQL close to 100% working
44
- * Derby improvements
45
- * DECIMAL/NUMERIC/FLOAT/REAL bugs fixed with type recognition for Oracle, Postgres, etc.
46
- * HSQLDB has regressed this release and may not be functioning; we'll get it fixed for the
47
- next one
59
+ - Release coincides with JRuby 0.9.9 release
60
+ - JRuby 0.9.9 is required
61
+ - MySQL close to 100% working
62
+ - Derby improvements
63
+ - DECIMAL/NUMERIC/FLOAT/REAL bugs fixed with type recognition for Oracle,
64
+ Postgres, etc.
65
+ - HSQLDB has regressed this release and may not be functioning; we'll get it
66
+ fixed for the next one
48
67
 
49
68
  == 0.2.3
50
69
 
51
- * Release coincides (and compatible) with JRuby 0.9.8 release
52
- * 8 bugs fixed: see http://rubyurl.com/0Da
53
- * Improvements and compatibility fixes for Rails 1.2.x
70
+ - Release coincides (and compatible) with JRuby 0.9.8 release
71
+ - 8 bugs fixed: see http://rubyurl.com/0Da
72
+ - Improvements and compatibility fixes for Rails 1.2.x
54
73
 
55
74
  == 0.2.1, 0.2.2
56
75
 
57
- * Early releases, added better support for multiple databases
76
+ - Early releases, added better support for multiple databases
58
77
 
59
78
  == 0.0.1
60
79
 
61
- * Initial, very alpha release
80
+ - Initial, very alpha release
@@ -24,7 +24,7 @@ lib/jdbc_adapter/jdbc_postgre.rb
24
24
  lib/jdbc_adapter/missing_functionality_helper.rb
25
25
  lib/jdbc_adapter/version.rb
26
26
  lib/jdbc_adapter.rb
27
- lib/jdbc_adapter_internal.jar
27
+ lib/jdbc_adapter/jdbc_adapter_internal.jar
28
28
  test/activerecord/connection_adapters/type_conversion_test.rb
29
29
  test/activerecord/connections/native_jdbc_mysql/connection.rb
30
30
  test/db/derby.rb
@@ -39,6 +39,7 @@ test/derby_multibyte_test.rb
39
39
  test/derby_simple_test.rb
40
40
  test/generic_jdbc_connection_test.rb
41
41
  test/h2_simple_test.rb
42
+ test/has_many_through.rb
42
43
  test/hsqldb_simple_test.rb
43
44
  test/jdbc_adapter/jdbc_db2_test.rb
44
45
  test/jdbc_common.rb
@@ -59,6 +60,8 @@ test/mysql_simple_test.rb
59
60
  test/postgres_simple_test.rb
60
61
  test/simple.rb
61
62
  lib/tasks/jdbc_databases.rake
62
- src/java/JdbcAdapterInternalService.java
63
- src/java/JDBCDerbySpec.java
64
- src/java/JDBCMySQLSpec.java
63
+ src/java/jdbc_adapter/JdbcAdapterInternalService.java
64
+ src/java/jdbc_adapter/JdbcConnectionFactory.java
65
+ src/java/jdbc_adapter/JdbcDerbySpec.java
66
+ src/java/jdbc_adapter/JdbcMySQLSpec.java
67
+ src/java/jdbc_adapter/SQLBlock.java
data/Rakefile CHANGED
@@ -13,22 +13,23 @@ def java_classpath_arg # myriad of ways to discover JRuby classpath
13
13
  jruby_cpath = ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] &&
14
14
  FileList["#{ENV['JRUBY_HOME']}/lib/*.jar"].join(File::PATH_SEPARATOR)
15
15
  end
16
- cpath_arg = jruby_cpath ? "-cp #{jruby_cpath}" : ""
16
+ jruby_cpath ? "-cp #{jruby_cpath}" : ""
17
17
  end
18
18
 
19
19
  desc "Compile the native Java code."
20
20
  task :java_compile do
21
21
  mkdir_p "pkg/classes"
22
22
  sh "javac -target 1.4 -source 1.4 -d pkg/classes #{java_classpath_arg} #{FileList['src/java/**/*.java'].join(' ')}"
23
- sh "jar cf lib/jdbc_adapter_internal.jar -C pkg/classes/ ."
23
+ sh "jar cf lib/jdbc_adapter/jdbc_adapter_internal.jar -C pkg/classes/ ."
24
24
  end
25
- file "lib/jdbc_adapter_internal.jar" => :java_compile
25
+ file "lib/jdbc_adapter/jdbc_adapter_internal.jar" => :java_compile
26
26
 
27
27
  task :more_clean do
28
28
  rm_rf FileList['derby*']
29
29
  rm_rf FileList['test.db.*']
30
30
  rm_rf "test/reports"
31
- rm_f FileList['lib/*.jar']
31
+ rm_f FileList['lib/**/*.jar']
32
+ rm_f "manifest.mf"
32
33
  end
33
34
 
34
35
  task :clean => :more_clean
@@ -72,7 +73,7 @@ task :test_postgresql => [:test_postgres]
72
73
  task :test_pgsql => [:test_postgres]
73
74
 
74
75
  MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt",
75
- "Rakefile", "LICENSE", "lib/**/*.rb", "lib/jdbc_adapter_internal.jar", "test/**/*.rb",
76
+ "Rakefile", "LICENSE", "lib/**/*.rb", "lib/jdbc_adapter/jdbc_adapter_internal.jar", "test/**/*.rb",
76
77
  "lib/**/*.rake", "src/**/*.java"]
77
78
 
78
79
  file "Manifest.txt" => :manifest
@@ -1,7 +1,7 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
2
  require 'java'
3
3
  require 'active_record/connection_adapters/jdbc_adapter_spec'
4
- require 'jdbc_adapter_internal'
4
+ require 'jdbc_adapter/jdbc_adapter_internal'
5
5
  require 'bigdecimal'
6
6
 
7
7
  module ActiveRecord
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  # The original implementation of this had a bug, which modifies native_database_types.
11
11
  # This version allows us to cache that value.
12
12
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
13
- native = native_database_types[type]
13
+ native = native_database_types[type.to_s.downcase.to_sym]
14
14
  column_type_sql = native.is_a?(Hash) ? native[:name] : native
15
15
  if type == :decimal # ignore limit, use precison and scale
16
16
  precision ||= native[:precision]
@@ -56,10 +56,10 @@ module ActiveRecord
56
56
  extend JdbcSpec::ActiveRecordExtensions
57
57
 
58
58
  alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
59
- def attributes_with_quotes(include_primary_key = true) #:nodoc:
60
- aq = attributes_with_quotes_pre_oracle(include_primary_key)
59
+ def attributes_with_quotes(*args) #:nodoc:
60
+ aq = attributes_with_quotes_pre_oracle(*args)
61
61
  if connection.class == ConnectionAdapters::JdbcAdapter && (connection.is_a?(JdbcSpec::Oracle) || connection.is_a?(JdbcSpec::Mimer))
62
- aq[self.class.primary_key] = "?" if include_primary_key && aq[self.class.primary_key].nil?
62
+ aq[self.class.primary_key] = "?" if args.first && aq[self.class.primary_key].nil?
63
63
  end
64
64
  aq
65
65
  end
@@ -171,7 +171,14 @@ module ActiveRecord
171
171
 
172
172
  def choose_best_types
173
173
  type_map = {}
174
- AR_TO_JDBC_TYPES.each_key do |k|
174
+ @types.each do |row|
175
+ name = row['type_name'].downcase
176
+ k = name.to_sym
177
+ type_map[k] = { :name => name }
178
+ type_map[k][:limit] = row['precision'].to_i if row['precision']
179
+ end
180
+
181
+ AR_TO_JDBC_TYPES.keys.each do |k|
175
182
  typerow = choose_type(k)
176
183
  type_map[k] = { :name => typerow['type_name'].downcase }
177
184
  case k
@@ -203,19 +210,19 @@ module ActiveRecord
203
210
 
204
211
  def driver_class
205
212
  @driver_class ||= begin
206
- driver_class_const = (@name[0...1].capitalize + @name[1..@name.length]).gsub(/\./, '_')
207
- Jdbc::Mutex.synchronized do
208
- unless Jdbc.const_defined?(driver_class_const)
209
- driver_class_name = @name
210
- Jdbc.module_eval do
211
- include_class(driver_class_name) { driver_class_const }
212
- end
213
- end
214
- end
215
- driver_class = Jdbc.const_get(driver_class_const)
216
- raise "You specify a driver for your JDBC connection" unless driver_class
217
- driver_class
218
- end
213
+ driver_class_const = (@name[0...1].capitalize + @name[1..@name.length]).gsub(/\./, '_')
214
+ Jdbc::Mutex.synchronized do
215
+ unless Jdbc.const_defined?(driver_class_const)
216
+ driver_class_name = @name
217
+ Jdbc.module_eval do
218
+ include_class(driver_class_name) { driver_class_const }
219
+ end
220
+ end
221
+ end
222
+ driver_class = Jdbc.const_get(driver_class_const)
223
+ raise "You specify a driver for your JDBC connection" unless driver_class
224
+ driver_class
225
+ end
219
226
  end
220
227
 
221
228
  def load
@@ -265,11 +272,15 @@ module ActiveRecord
265
272
  end
266
273
  end
267
274
 
275
+ include_class "jdbc_adapter.JdbcConnectionFactory"
276
+
268
277
  class JdbcConnection
269
- attr_reader :adapter, :connection
278
+ attr_reader :adapter, :connection_factory
270
279
 
271
280
  def initialize(config)
272
281
  @config = config.symbolize_keys!
282
+ @config[:retry_count] ||= 5
283
+ @config[:connection_alive_sql] ||= "select 1"
273
284
  if @config[:jndi]
274
285
  begin
275
286
  configure_jndi
@@ -280,16 +291,13 @@ module ActiveRecord
280
291
  else
281
292
  configure_jdbc
282
293
  end
294
+ connection # force the connection to load
283
295
  set_native_database_types
284
296
  @stmts = {}
285
297
  rescue Exception => e
286
298
  raise "The driver encountered an error: #{e}"
287
299
  end
288
300
 
289
- def reconnect!
290
- self.adapter.reconnect!
291
- end
292
-
293
301
  def adapter=(adapt)
294
302
  @adapter = adapt
295
303
  @tps = {}
@@ -305,71 +313,60 @@ module ActiveRecord
305
313
  # one index, one row per column in the index), so a simple block-based
306
314
  # filter like that used for tables doesn't really work here. Callers
307
315
  # should filter the return from this method instead.
308
- #
309
- # TODO: fix to use reconnect correctly
310
316
  def indexes(table_name, name = nil, schema_name = nil)
311
- metadata = @connection.getMetaData
312
- unless String === table_name
313
- table_name = table_name.to_s
314
- else
315
- table_name = table_name.dup
316
- end
317
- table_name.upcase! if metadata.storesUpperCaseIdentifiers
318
- table_name.downcase! if metadata.storesLowerCaseIdentifiers
319
- resultset = metadata.getIndexInfo(nil, schema_name, table_name, false, false)
320
- primary_keys = primary_keys(table_name)
321
- indexes = []
322
- current_index = nil
323
- while resultset.next
324
- index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME)
325
- next unless index_name
326
- index_name.downcase!
327
- column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
328
-
329
- next if primary_keys.include? column_name
330
-
331
- # We are working on a new index
332
- if current_index != index_name
333
- current_index = index_name
334
- table_name = resultset.get_string(Jdbc::IndexMetaData::TABLE_NAME).downcase
335
- non_unique = resultset.get_boolean(Jdbc::IndexMetaData::NON_UNIQUE)
336
-
337
- # empty list for column names, we'll add to that in just a bit
338
- indexes << IndexDefinition.new(table_name, index_name, !non_unique, [])
317
+ with_connection_retry_guard do |conn|
318
+ metadata = conn.getMetaData
319
+ begin
320
+ unless String === table_name
321
+ table_name = table_name.to_s
322
+ else
323
+ table_name = table_name.dup
324
+ end
325
+ table_name.upcase! if metadata.storesUpperCaseIdentifiers
326
+ table_name.downcase! if metadata.storesLowerCaseIdentifiers
327
+ resultset = metadata.getIndexInfo(nil, schema_name, table_name, false, false)
328
+ primary_keys = primary_keys(table_name)
329
+ indexes = []
330
+ current_index = nil
331
+ while resultset.next
332
+ index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME)
333
+ next unless index_name
334
+ index_name.downcase!
335
+ column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
336
+
337
+ next if primary_keys.include? column_name
338
+
339
+ # We are working on a new index
340
+ if current_index != index_name
341
+ current_index = index_name
342
+ table_name = resultset.get_string(Jdbc::IndexMetaData::TABLE_NAME).downcase
343
+ non_unique = resultset.get_boolean(Jdbc::IndexMetaData::NON_UNIQUE)
344
+
345
+ # empty list for column names, we'll add to that in just a bit
346
+ indexes << IndexDefinition.new(table_name, index_name, !non_unique, [])
347
+ end
348
+
349
+ # One or more columns can be associated with an index
350
+ indexes.last.columns << column_name
351
+ end
352
+ resultset.close
353
+ indexes
354
+ ensure
355
+ metadata.close rescue nil
339
356
  end
340
-
341
- # One or more columns can be associated with an index
342
- indexes.last.columns << column_name
343
357
  end
344
- resultset.close
345
- indexes
346
- rescue
347
- if @connection.is_closed
348
- reconnect!
349
- retry
350
- else
351
- raise
352
- end
353
- ensure
354
- metadata.close rescue nil
355
358
  end
356
359
 
357
- # def self.insert?(sql)
358
- # /\A\s*insert/i =~ sql
359
- # end
360
-
361
- # def self.select?(sql)
362
- # /\A\s*\(?\s*(select|show)/i =~ sql
363
- # end
364
-
365
360
  private
366
361
  def configure_jndi
367
362
  jndi = @config[:jndi].to_s
368
363
  ctx = javax.naming.InitialContext.new
369
364
  ds = ctx.lookup(jndi)
370
- set_connection ds.connection
365
+ @connection_factory = JdbcConnectionFactory.impl do
366
+ ds.connection
367
+ end
371
368
  unless @config[:driver]
372
- @config[:driver] = @connection.meta_data.connection.java_class.name
369
+ @config[:driver] = connection.meta_data.connection.java_class.name
373
370
  end
374
371
  end
375
372
 
@@ -380,19 +377,20 @@ module ActiveRecord
380
377
  url = @config[:url].to_s
381
378
 
382
379
  unless driver && url
383
- raise ArgumentError, "jdbc adapter requires driver class and url"
380
+ raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
384
381
  end
385
382
 
386
- if driver =~ /mysql/i
383
+ if driver =~ /mysql/i && url !~ /#{Regexp.quote(JdbcSpec::MySQL::URL_OPTIONS)}/
387
384
  div = url =~ /\?/ ? '&' : '?'
388
- url = "#{url}#{div}zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding=utf8"
385
+ url = "#{url}#{div}#{JdbcSpec::MySQL::URL_OPTIONS}"
389
386
  @config[:url] = url
390
387
  end
391
388
 
392
389
  jdbc_driver = JdbcDriver.new(driver)
393
390
  jdbc_driver.load
394
- connection = jdbc_driver.connection(url, user, pass)
395
- set_connection connection
391
+ @connection_factory = JdbcConnectionFactory.impl do
392
+ jdbc_driver.connection(url, user, pass)
393
+ end
396
394
  end
397
395
 
398
396
  end
@@ -474,22 +472,30 @@ module ActiveRecord
474
472
  end
475
473
 
476
474
  def reconnect!
477
- @connection.close rescue nil
478
- @connection = JdbcConnection.new(@config)
479
- @connection.adapter = self
475
+ @connection.reconnect!
480
476
  @connection
481
477
  end
478
+
479
+ def disconnect!
480
+ @connection.disconnect!
481
+ end
482
482
 
483
483
  def select_all(sql, name = nil)
484
484
  select(sql, name)
485
485
  end
486
486
 
487
+ def select_rows(sql, name = nil)
488
+ rows = []
489
+ select(sql, name).each {|row| rows << row.values }
490
+ rows
491
+ end
492
+
487
493
  def select_one(sql, name = nil)
488
494
  select(sql, name).first
489
495
  end
490
496
 
491
497
  def execute(sql, name = nil)
492
- log_no_bench(sql, name) do
498
+ log(sql, name) do
493
499
  _execute(sql,name)
494
500
  end
495
501
  end
@@ -549,27 +555,6 @@ module ActiveRecord
549
555
  def select(sql, name=nil)
550
556
  execute(sql,name)
551
557
  end
552
-
553
- def log_no_bench(sql, name)
554
- if block_given?
555
- if @logger and @logger.level <= Logger::INFO
556
- result = yield
557
- log_info(sql, name, 0)
558
- result
559
- else
560
- yield
561
- end
562
- else
563
- log_info(sql, name, 0)
564
- nil
565
- end
566
- rescue Exception => e
567
- # Log message and raise exception.
568
- message = "#{e.class.name}: #{e.message}: #{sql}"
569
-
570
- log_info(message, name, 0)
571
- raise ActiveRecord::StatementInvalid, message
572
- end
573
558
  end
574
559
  end
575
560
  end