activerecord-jdbc-adapter 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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