activerecord-jdbc-adapter 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/History.txt +61 -0
  2. data/LICENSE +21 -0
  3. data/Manifest.txt +64 -0
  4. data/README.txt +116 -0
  5. data/Rakefile +146 -0
  6. data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
  7. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  8. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
  9. data/lib/active_record/connection_adapters/jdbc_adapter.rb +575 -0
  10. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +10 -0
  11. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  12. data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
  13. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  14. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  15. data/lib/jdbc_adapter.rb +32 -0
  16. data/lib/jdbc_adapter/jdbc_db2.rb +104 -0
  17. data/lib/jdbc_adapter/jdbc_derby.rb +362 -0
  18. data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
  19. data/lib/jdbc_adapter/jdbc_hsqldb.rb +168 -0
  20. data/lib/jdbc_adapter/jdbc_mimer.rb +134 -0
  21. data/lib/jdbc_adapter/jdbc_mssql.rb +356 -0
  22. data/lib/jdbc_adapter/jdbc_mysql.rb +168 -0
  23. data/lib/jdbc_adapter/jdbc_oracle.rb +340 -0
  24. data/lib/jdbc_adapter/jdbc_postgre.rb +347 -0
  25. data/lib/jdbc_adapter/missing_functionality_helper.rb +72 -0
  26. data/lib/jdbc_adapter/version.rb +5 -0
  27. data/lib/jdbc_adapter_internal.jar +0 -0
  28. data/lib/tasks/jdbc_databases.rake +72 -0
  29. data/src/java/JDBCDerbySpec.java +323 -0
  30. data/src/java/JDBCMySQLSpec.java +89 -0
  31. data/src/java/JdbcAdapterInternalService.java +953 -0
  32. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  33. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  34. data/test/db/derby.rb +18 -0
  35. data/test/db/h2.rb +11 -0
  36. data/test/db/hsqldb.rb +15 -0
  37. data/test/db/jdbc.rb +11 -0
  38. data/test/db/jndi_config.rb +30 -0
  39. data/test/db/logger.rb +3 -0
  40. data/test/db/mysql.rb +9 -0
  41. data/test/db/postgres.rb +9 -0
  42. data/test/derby_multibyte_test.rb +12 -0
  43. data/test/derby_simple_test.rb +12 -0
  44. data/test/generic_jdbc_connection_test.rb +9 -0
  45. data/test/h2_simple_test.rb +7 -0
  46. data/test/hsqldb_simple_test.rb +6 -0
  47. data/test/jdbc_adapter/jdbc_db2_test.rb +21 -0
  48. data/test/jdbc_common.rb +6 -0
  49. data/test/jndi_test.rb +37 -0
  50. data/test/manualTestDatabase.rb +195 -0
  51. data/test/minirunit.rb +109 -0
  52. data/test/minirunit/testConnect.rb +14 -0
  53. data/test/minirunit/testH2.rb +73 -0
  54. data/test/minirunit/testHsqldb.rb +73 -0
  55. data/test/minirunit/testLoadActiveRecord.rb +3 -0
  56. data/test/minirunit/testMysql.rb +83 -0
  57. data/test/minirunit/testRawSelect.rb +24 -0
  58. data/test/models/auto_id.rb +18 -0
  59. data/test/models/data_types.rb +18 -0
  60. data/test/models/entry.rb +20 -0
  61. data/test/mysql_multibyte_test.rb +6 -0
  62. data/test/mysql_simple_test.rb +13 -0
  63. data/test/postgres_simple_test.rb +12 -0
  64. data/test/simple.rb +157 -0
  65. metadata +112 -0
@@ -0,0 +1,61 @@
1
+ == 0.6
2
+
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.
4
+ - 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.
7
+ - More bugfixes to Oracle and SQLServer courtesy of Ola & ThoughtWorks
8
+
9
+ == 0.5
10
+
11
+ - 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.
19
+ - Updates to HSQL, MS SQLServer, Postgres, Oracle and Derby adapters
20
+
21
+ == 0.4
22
+
23
+ - Release coincides with JRuby 1.0 release
24
+ - Shoring up PostgreSQL (courtesy Dudley Flanders) and HSQL (courtesy Matthew Williams)
25
+ - 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
27
+ - Sybase type recognition fix (courtesy Dean Mao)
28
+
29
+ == 0.3.1
30
+
31
+ * Derby critical fixes shortly after 0.3
32
+
33
+ == 0.3
34
+
35
+ * Release coincides with JRuby 1.0.0RC1 release
36
+ * Improvements for Derby, Postgres, and Oracle, all of which are running
37
+ > 95% of AR tests
38
+
39
+ == 0.2.4
40
+
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
48
+
49
+ == 0.2.3
50
+
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
54
+
55
+ == 0.2.1, 0.2.2
56
+
57
+ * Early releases, added better support for multiple databases
58
+
59
+ == 0.0.1
60
+
61
+ * Initial, very alpha release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2006-2007 Nick Sieger <nick@nicksieger.com>
2
+ Copyright (c) 2006-2007 Ola Bini <ola@ologix.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,64 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ LICENSE
6
+ lib/active_record/connection_adapters/derby_adapter.rb
7
+ lib/active_record/connection_adapters/h2_adapter.rb
8
+ lib/active_record/connection_adapters/hsqldb_adapter.rb
9
+ lib/active_record/connection_adapters/jdbc_adapter.rb
10
+ lib/active_record/connection_adapters/jdbc_adapter_spec.rb
11
+ lib/active_record/connection_adapters/jndi_adapter.rb
12
+ lib/active_record/connection_adapters/mysql_adapter.rb
13
+ lib/active_record/connection_adapters/oracle_adapter.rb
14
+ lib/active_record/connection_adapters/postgresql_adapter.rb
15
+ lib/jdbc_adapter/jdbc_db2.rb
16
+ lib/jdbc_adapter/jdbc_derby.rb
17
+ lib/jdbc_adapter/jdbc_firebird.rb
18
+ lib/jdbc_adapter/jdbc_hsqldb.rb
19
+ lib/jdbc_adapter/jdbc_mimer.rb
20
+ lib/jdbc_adapter/jdbc_mssql.rb
21
+ lib/jdbc_adapter/jdbc_mysql.rb
22
+ lib/jdbc_adapter/jdbc_oracle.rb
23
+ lib/jdbc_adapter/jdbc_postgre.rb
24
+ lib/jdbc_adapter/missing_functionality_helper.rb
25
+ lib/jdbc_adapter/version.rb
26
+ lib/jdbc_adapter.rb
27
+ lib/jdbc_adapter_internal.jar
28
+ test/activerecord/connection_adapters/type_conversion_test.rb
29
+ test/activerecord/connections/native_jdbc_mysql/connection.rb
30
+ test/db/derby.rb
31
+ test/db/h2.rb
32
+ test/db/hsqldb.rb
33
+ test/db/jdbc.rb
34
+ test/db/jndi_config.rb
35
+ test/db/logger.rb
36
+ test/db/mysql.rb
37
+ test/db/postgres.rb
38
+ test/derby_multibyte_test.rb
39
+ test/derby_simple_test.rb
40
+ test/generic_jdbc_connection_test.rb
41
+ test/h2_simple_test.rb
42
+ test/hsqldb_simple_test.rb
43
+ test/jdbc_adapter/jdbc_db2_test.rb
44
+ test/jdbc_common.rb
45
+ test/jndi_test.rb
46
+ test/manualTestDatabase.rb
47
+ test/minirunit/testConnect.rb
48
+ test/minirunit/testH2.rb
49
+ test/minirunit/testHsqldb.rb
50
+ test/minirunit/testLoadActiveRecord.rb
51
+ test/minirunit/testMysql.rb
52
+ test/minirunit/testRawSelect.rb
53
+ test/minirunit.rb
54
+ test/models/auto_id.rb
55
+ test/models/data_types.rb
56
+ test/models/entry.rb
57
+ test/mysql_multibyte_test.rb
58
+ test/mysql_simple_test.rb
59
+ test/postgres_simple_test.rb
60
+ test/simple.rb
61
+ lib/tasks/jdbc_databases.rake
62
+ src/java/JdbcAdapterInternalService.java
63
+ src/java/JDBCDerbySpec.java
64
+ src/java/JDBCMySQLSpec.java
@@ -0,0 +1,116 @@
1
+ ActiveRecord-JDBC is a database adapter for Rails' ActiveRecord component that can be used with JRuby[http://www.jruby.org/]. It allows use of virtually any JDBC-compliant database with your JRuby on Rails application.
2
+
3
+ ActiveRecord-JDBC is a sub-project of jruby-extras at RubyForge.
4
+
5
+ == Databases
6
+
7
+ What's there, and what is not there:
8
+
9
+ * MySQL - Complete support
10
+ * PostgreSQL - Complete support
11
+ * Oracle - Complete support
12
+ * Microsoft SQL Server - Complete support except for change_column_default
13
+ * DB2 - Complete, except for the migrations:
14
+ * change_column
15
+ * change_column_default
16
+ * remove_column
17
+ * rename_column
18
+ * add_index
19
+ * remove_index
20
+ * rename_table
21
+ * FireBird - Complete, except for change_column_default and rename_column
22
+ * Derby - Complete, except for:
23
+ * change_column
24
+ * change_column_default
25
+ * remove_column
26
+ * rename_column
27
+ * HSQLDB - Complete
28
+
29
+ Other databases will require testing and likely a custom configuration module. Please join the jruby-extras mailing-list[http://rubyforge.org/mail/?group_id=2014] to help us discover support for more databases.
30
+
31
+ == Using ActiveRecord JDBC
32
+
33
+ === Inside Rails
34
+
35
+ To use ActiveRecord-JDBC with JRuby on Rails:
36
+
37
+ 1. Choose the adapter you wish to gem install. The following pre-packaged adapters are available:
38
+
39
+ * base jdbc (<tt>activerecord-jdbc-adapter</tt>). Supports all available databases via JDBC, but requires you to download and manually install the database vendor's JDBC driver .jar file.
40
+ * mysql (<tt>activerecord-jdbcmysql-adapter</tt>)
41
+ * postgresql (<tt>activerecord-jdbcpostgresql-adapter</tt>)
42
+ * derby (<tt>activerecord-jdbcderby-adapter</tt>)
43
+ * hsqldb (<tt>activerecord-jdbchsqldb-adapter</tt>)
44
+
45
+ 2. If you're using Rails 2.0, you may skip to the next step. For Rails prior to version 2.0, you'll need to add one-time setup to your config/environment.rb file in your Rails application. Add the following lines just before the <code>Rails::Initializer</code>. (If you're using ActiveRecord-JDBC under the old gem name used in versions 0.5 and earlier, replace 'activerecord-jdbc-adapter' with 'ActiveRecord-JDBC' below.)
46
+
47
+ if RUBY_PLATFORM =~ /java/
48
+ require 'rubygems'
49
+ gem 'activerecord-jdbc-adapter'
50
+ require 'jdbc_adapter'
51
+ end
52
+
53
+ 3. Configure your database.yml to use the <code>jdbc</code> adapter.
54
+ For mysql, postgres, derby, oracle, hsqldb and h2 you can simply configure the database in the normal Rails style. If you use one of the convenience 'activerecord-jdbcXXX-adapter' adapters, be sure and put a 'jdbc' prefix in front of the databas adapter name as below.
55
+
56
+ development:
57
+ adapter: jdbcmysql
58
+ username: blog
59
+ password:
60
+ hostname: localhost
61
+ database: weblog_development
62
+
63
+ For other databases, you'll need to know the database driver class and URL. Example:
64
+
65
+ development:
66
+ adapter: jdbc
67
+ username: blog
68
+ password:
69
+ driver: com.mysql.jdbc.Driver
70
+ url: jdbc:mysql://localhost:3306/weblog_development
71
+
72
+ === Standalone, with ActiveRecord
73
+
74
+ 1. Install the gem with JRuby:
75
+
76
+ jruby -S gem install activerecord-jdbc-adapter
77
+
78
+ If you wish to use the adapter for a specific database, you can install it directly and a driver gem will be installed as well:
79
+
80
+ jruby -S gem install activerecord-jdbcderby-adapter
81
+
82
+ 2. If using ActiveRecord 2.0 (Rails 2.0) or greater, you can skip to the next step. Otherwise, ensure the following code gets executed in your script:
83
+
84
+ require 'rubygems'
85
+ gem 'activerecord-jdbc-adapter'
86
+ require 'jdbc_adapter'
87
+ require 'active_record'
88
+
89
+ 3. After this you can establish a JDBC connection like this:
90
+
91
+ ActiveRecord::Base.establish_connection(
92
+ :adapter => 'jdbcderby',
93
+ :database => "db/my-database"
94
+ )
95
+
96
+ or like this (but requires that you manually put the driver jar on the classpath):
97
+
98
+ ActiveRecord::Base.establish_connection(
99
+ :adapter => 'jdbc',
100
+ :driver => 'org.apache.derby.jdbc.EmbeddedDriver',
101
+ :url => 'jdbc:derby:test_ar;create=true'
102
+ )
103
+
104
+ == Running AR-JDBC's Tests
105
+
106
+ Drivers for 4 open-source databases are included. Provided you have MySQL installed, you can simply type <tt>jruby -S rake</tt> to run the tests. A database named <tt>weblog_development</tt> is needed beforehand with a connection user of "blog" and password empty.
107
+
108
+ == Authors
109
+
110
+ This project was written by Nick Sieger <nick@nicksieger.com> and Ola Bini <ola@ologix.com> with lots of help from the JRuby community.
111
+
112
+ == License
113
+
114
+ ActiveRecord-JDBC is released under a BSD license. See the LICENSE file included with the distribution for details.
115
+
116
+ Open-source driver gems for ActiveRecord JDBC are licensed under the same license the database's drivers are licensed. See each driver gem's LICENSE.txt file for details.
@@ -0,0 +1,146 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:java_compile, :test]
5
+
6
+ def java_classpath_arg # myriad of ways to discover JRuby classpath
7
+ begin
8
+ require 'java' # already running in a JRuby JVM
9
+ jruby_cpath = Java::java.lang.System.getProperty('java.class.path')
10
+ rescue LoadError
11
+ end
12
+ unless jruby_cpath
13
+ jruby_cpath = ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] &&
14
+ FileList["#{ENV['JRUBY_HOME']}/lib/*.jar"].join(File::PATH_SEPARATOR)
15
+ end
16
+ cpath_arg = jruby_cpath ? "-cp #{jruby_cpath}" : ""
17
+ end
18
+
19
+ desc "Compile the native Java code."
20
+ task :java_compile do
21
+ mkdir_p "pkg/classes"
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/ ."
24
+ end
25
+ file "lib/jdbc_adapter_internal.jar" => :java_compile
26
+
27
+ task :more_clean do
28
+ rm_rf FileList['derby*']
29
+ rm_rf FileList['test.db.*']
30
+ rm_rf "test/reports"
31
+ rm_f FileList['lib/*.jar']
32
+ end
33
+
34
+ task :clean => :more_clean
35
+
36
+ task :filelist do
37
+ puts FileList['pkg/**/*'].inspect
38
+ end
39
+
40
+ if RUBY_PLATFORM =~ /java/
41
+ # TODO: add more databases into the standard tests here.
42
+ task :test => [:test_mysql, :test_jdbc, :test_derby, :test_hsqldb]
43
+ else
44
+ task :test => [:test_mysql]
45
+ end
46
+
47
+ FileList['drivers/*'].each do |d|
48
+ next unless File.directory?(d)
49
+ driver = File.basename(d)
50
+ Rake::TestTask.new("test_#{driver}") do |t|
51
+ files = FileList["test/#{driver}*test.rb"]
52
+ if driver == "derby"
53
+ files << 'test/activerecord/connection_adapters/type_conversion_test.rb'
54
+ end
55
+ t.ruby_opts << "-rjdbc/#{driver}"
56
+ t.test_files = files
57
+ t.libs << "test" << "#{d}/lib"
58
+ end
59
+ end
60
+
61
+ Rake::TestTask.new(:test_jdbc) do |t|
62
+ t.test_files = FileList['test/generic_jdbc_connection_test.rb']
63
+ t.libs << 'test' << 'drivers/mysql/lib'
64
+ end
65
+
66
+ Rake::TestTask.new(:test_jndi) do |t|
67
+ t.test_files = FileList['test/jndi_test.rb']
68
+ t.libs << 'test' << 'drivers/derby/lib'
69
+ end
70
+
71
+ task :test_postgresql => [:test_postgres]
72
+ task :test_pgsql => [:test_postgres]
73
+
74
+ MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt",
75
+ "Rakefile", "LICENSE", "lib/**/*.rb", "lib/jdbc_adapter_internal.jar", "test/**/*.rb",
76
+ "lib/**/*.rake", "src/**/*.java"]
77
+
78
+ file "Manifest.txt" => :manifest
79
+ task :manifest do
80
+ File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
81
+ end
82
+ Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date list of files
83
+
84
+ require File.dirname(__FILE__) + "/lib/jdbc_adapter/version"
85
+ begin
86
+ require 'hoe'
87
+ Hoe.new("activerecord-jdbc-adapter", JdbcAdapter::Version::VERSION) do |p|
88
+ p.rubyforge_name = "jruby-extras"
89
+ p.url = "http://jruby-extras.rubyforge.org/activerecord-jdbc-adapter"
90
+ p.author = "Nick Sieger, Ola Bini and JRuby contributors"
91
+ p.email = "nick@nicksieger.com, ola.bini@gmail.com"
92
+ p.summary = "JDBC adapter for ActiveRecord, for use within JRuby on Rails."
93
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
94
+ p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
95
+ end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
96
+ rescue LoadError
97
+ puts "You really need Hoe installed to be able to package this gem"
98
+ rescue => e
99
+ puts "ignoring error while loading hoe: #{e.to_s}"
100
+ end
101
+
102
+ def rake(*args)
103
+ ruby "-S", "rake", *args
104
+ end
105
+
106
+ %w(test package install_gem release clean).each do |task|
107
+ desc "Run rake #{task} on all available adapters and drivers"
108
+ task "all:#{task}" => task
109
+ end
110
+
111
+ (Dir["drivers/*/Rakefile"] + Dir["adapters/*/Rakefile"]).each do |rakefile|
112
+ dir = File.dirname(rakefile)
113
+ prefix = dir.sub(%r{/}, ':')
114
+ tasks = %w(package install_gem debug_gem clean)
115
+ tasks << "test" if File.directory?(File.join(dir, "test"))
116
+ tasks.each do |task|
117
+ desc "Run rake #{task} on #{dir}"
118
+ task "#{prefix}:#{task}" do
119
+ Dir.chdir(dir) do
120
+ rake task
121
+ end
122
+ end
123
+ task "#{File.dirname(dir)}:#{task}" => "#{prefix}:#{task}"
124
+ task "all:#{task}" => "#{prefix}:#{task}"
125
+ end
126
+ desc "Run rake release on #{dir}"
127
+ task "#{prefix}:release" do
128
+ Dir.chdir(dir) do
129
+ version = nil
130
+ if dir =~ /adapters/
131
+ version = ENV['VERSION']
132
+ else
133
+ Dir["lib/**/*.rb"].each do |file|
134
+ version ||= File.open(file) {|f| f.read =~ /VERSION = "([^"]+)"/ && $1}
135
+ end
136
+ end
137
+ rake "release", "VERSION=#{version}"
138
+ end
139
+ end
140
+ # Only release adapters synchronously with main release. Drivers are versioned
141
+ # according to their JDBC driver versions.
142
+ if dir =~ /adapters/
143
+ task "adapters:release" => "#{prefix}:release"
144
+ task "all:release" => "#{prefix}:release"
145
+ end
146
+ end
@@ -0,0 +1,13 @@
1
+ tried_gem = false
2
+ begin
3
+ require "jdbc/derby"
4
+ rescue LoadError
5
+ unless tried_gem
6
+ require 'rubygems'
7
+ gem "jdbc-derby"
8
+ tried_gem = true
9
+ retry
10
+ end
11
+ # trust that the derby jar is already present
12
+ end
13
+ require 'active_record/connection_adapters/jdbc_adapter'
@@ -0,0 +1 @@
1
+ require 'active_record/connection_adapters/jdbc_adapter'
@@ -0,0 +1,13 @@
1
+ tried_gem = false
2
+ begin
3
+ require "jdbc/hsqldb"
4
+ rescue LoadError
5
+ unless tried_gem
6
+ require 'rubygems'
7
+ gem "jdbc-hsqldb"
8
+ tried_gem = true
9
+ retry
10
+ end
11
+ # trust that the hsqldb jar is already present
12
+ end
13
+ require 'active_record/connection_adapters/jdbc_adapter'
@@ -0,0 +1,575 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'java'
3
+ require 'active_record/connection_adapters/jdbc_adapter_spec'
4
+ require 'jdbc_adapter_internal'
5
+ require 'bigdecimal'
6
+
7
+ module ActiveRecord
8
+ module ConnectionAdapters # :nodoc:
9
+ module SchemaStatements
10
+ # The original implementation of this had a bug, which modifies native_database_types.
11
+ # This version allows us to cache that value.
12
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
13
+ native = native_database_types[type]
14
+ column_type_sql = native.is_a?(Hash) ? native[:name] : native
15
+ if type == :decimal # ignore limit, use precison and scale
16
+ precision ||= native[:precision]
17
+ scale ||= native[:scale]
18
+ if precision
19
+ if scale
20
+ column_type_sql += "(#{precision},#{scale})"
21
+ else
22
+ column_type_sql += "(#{precision})"
23
+ end
24
+ else
25
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
26
+ end
27
+ column_type_sql
28
+ else
29
+ limit ||= native[:limit]
30
+ column_type_sql += "(#{limit})" if limit
31
+ column_type_sql
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module JdbcSpec
39
+ module ActiveRecordExtensions
40
+ def jdbc_connection(config)
41
+ connection = ::ActiveRecord::ConnectionAdapters::JdbcConnection.new(config)
42
+ ::ActiveRecord::ConnectionAdapters::JdbcAdapter.new(connection, logger, config)
43
+ end
44
+ alias jndi_connection jdbc_connection
45
+
46
+ def embedded_driver(config)
47
+ config[:username] ||= "sa"
48
+ config[:password] ||= ""
49
+ jdbc_connection(config)
50
+ end
51
+ end
52
+ end
53
+
54
+ module ActiveRecord
55
+ class Base
56
+ extend JdbcSpec::ActiveRecordExtensions
57
+
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)
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?
63
+ end
64
+ aq
65
+ end
66
+ end
67
+
68
+ module ConnectionAdapters
69
+ module Java
70
+ Class = java.lang.Class
71
+ URL = java.net.URL
72
+ URLClassLoader = java.net.URLClassLoader
73
+ end
74
+
75
+ module Jdbc
76
+ Mutex = java.lang.Object.new
77
+ DriverManager = java.sql.DriverManager
78
+ Statement = java.sql.Statement
79
+ Types = java.sql.Types
80
+
81
+ # some symbolic constants for the benefit of the JDBC-based
82
+ # JdbcConnection#indexes method
83
+ module IndexMetaData
84
+ INDEX_NAME = 6
85
+ NON_UNIQUE = 4
86
+ TABLE_NAME = 3
87
+ COLUMN_NAME = 9
88
+ end
89
+
90
+ module TableMetaData
91
+ TABLE_CAT = 1
92
+ TABLE_SCHEM = 2
93
+ TABLE_NAME = 3
94
+ TABLE_TYPE = 4
95
+ end
96
+
97
+ module PrimaryKeyMetaData
98
+ COLUMN_NAME = 4
99
+ end
100
+
101
+ end
102
+
103
+ # I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
104
+ # use for ActiveRecord's Adapter#native_database_types in a database-independent way,
105
+ # but apparently a database driver can return multiple types for a given
106
+ # java.sql.Types constant. So this type converter uses some heuristics to try to pick
107
+ # the best (most common) type to use. It's not great, it would be better to just
108
+ # delegate to each database's existin AR adapter's native_database_types method, but I
109
+ # wanted to try to do this in a way that didn't pull in all the other adapters as
110
+ # dependencies. Suggestions appreciated.
111
+ class JdbcTypeConverter
112
+ # The basic ActiveRecord types, mapped to an array of procs that are used to #select
113
+ # the best type. The procs are used as selectors in order until there is only one
114
+ # type left. If all the selectors are applied and there is still more than one
115
+ # type, an exception will be raised.
116
+ AR_TO_JDBC_TYPES = {
117
+ :string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
118
+ lambda {|r| r['type_name'] =~ /^varchar/i},
119
+ lambda {|r| r['type_name'] =~ /^varchar$/i},
120
+ lambda {|r| r['type_name'] =~ /varying/i}],
121
+ :text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
122
+ lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
123
+ lambda {|r| r['type_name'] =~ /^character large object$/i},
124
+ lambda {|r| r['sql_data_type'] == 2005}],
125
+ :integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
126
+ lambda {|r| r['type_name'] =~ /^integer$/i},
127
+ lambda {|r| r['type_name'] =~ /^int4$/i},
128
+ lambda {|r| r['type_name'] =~ /^int$/i}],
129
+ :decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
130
+ lambda {|r| r['type_name'] =~ /^decimal$/i},
131
+ lambda {|r| r['type_name'] =~ /^numeric$/i},
132
+ lambda {|r| r['type_name'] =~ /^number$/i},
133
+ lambda {|r| r['precision'] == '38'},
134
+ lambda {|r| r['data_type'] == '2'}],
135
+ :float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE, Jdbc::Types::REAL].include?(r['data_type'].to_i)},
136
+ lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
137
+ lambda {|r| r['type_name'] =~ /^float/i},
138
+ lambda {|r| r['type_name'] =~ /^double$/i},
139
+ lambda {|r| r['type_name'] =~ /^real$/i},
140
+ lambda {|r| r['precision'] == '15'}],
141
+ :datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
142
+ lambda {|r| r['type_name'] =~ /^datetime$/i},
143
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
144
+ lambda {|r| r['type_name'] =~ /^date/i}],
145
+ :timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
146
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
147
+ lambda {|r| r['type_name'] =~ /^datetime/i},
148
+ lambda {|r| r['type_name'] =~ /^date/i}],
149
+ :time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
150
+ lambda {|r| r['type_name'] =~ /^time$/i},
151
+ lambda {|r| r['type_name'] =~ /^date/i}],
152
+ :date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
153
+ lambda {|r| r['type_name'] =~ /^date$/i},
154
+ lambda {|r| r['type_name'] =~ /^date/i}],
155
+ :binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
156
+ lambda {|r| r['type_name'] =~ /^blob/i},
157
+ lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
158
+ lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
159
+ lambda {|r| r['type_name'] =~ /^binary$/i}, ],
160
+ :boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'].to_i)},
161
+ lambda {|r| r['type_name'] =~ /^bool/i},
162
+ lambda {|r| r['data_type'] == '-7'},
163
+ lambda {|r| r['type_name'] =~ /^tinyint$/i},
164
+ lambda {|r| r['type_name'] =~ /^decimal$/i}]
165
+ }
166
+
167
+ def initialize(types)
168
+ @types = types
169
+ @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
170
+ end
171
+
172
+ def choose_best_types
173
+ type_map = {}
174
+ AR_TO_JDBC_TYPES.each_key do |k|
175
+ typerow = choose_type(k)
176
+ type_map[k] = { :name => typerow['type_name'].downcase }
177
+ case k
178
+ when :integer, :string, :decimal
179
+ type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
180
+ when :boolean
181
+ type_map[k][:limit] = 1
182
+ end
183
+ end
184
+ type_map
185
+ end
186
+
187
+ def choose_type(ar_type)
188
+ procs = AR_TO_JDBC_TYPES[ar_type]
189
+ types = @types
190
+ procs.each do |p|
191
+ new_types = types.select(&p)
192
+ return new_types.first if new_types.length == 1
193
+ types = new_types if new_types.length > 0
194
+ end
195
+ raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
196
+ end
197
+ end
198
+
199
+ class JdbcDriver
200
+ def initialize(name)
201
+ @name = name
202
+ end
203
+
204
+ def driver_class
205
+ @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
219
+ end
220
+
221
+ def load
222
+ Jdbc::DriverManager.registerDriver(create)
223
+ end
224
+
225
+ def connection(url, user, pass)
226
+ Jdbc::DriverManager.getConnection(url, user, pass)
227
+ rescue
228
+ # bypass DriverManager to get around problem with dynamically loaded jdbc drivers
229
+ props = java.util.Properties.new
230
+ props.setProperty("user", user)
231
+ props.setProperty("password", pass)
232
+ create.connect(url, props)
233
+ end
234
+
235
+ def create
236
+ driver_class.new
237
+ end
238
+ end
239
+
240
+ class JdbcColumn < Column
241
+ attr_writer :limit, :precision
242
+
243
+ COLUMN_TYPES = ::JdbcSpec.constants.map{|c|
244
+ ::JdbcSpec.const_get c }.select{ |c|
245
+ c.respond_to? :column_selector }.map{|c|
246
+ c.column_selector }.inject({}) { |h,val|
247
+ h[val[0]] = val[1]; h }
248
+
249
+ def initialize(config, name, default, *args)
250
+ ds = config[:driver].to_s
251
+ for reg, func in COLUMN_TYPES
252
+ if reg === ds
253
+ func.call(config,self)
254
+ end
255
+ end
256
+ super(name,default_value(default),*args)
257
+ init_column(name, default, *args)
258
+ end
259
+
260
+ def init_column(*args)
261
+ end
262
+
263
+ def default_value(val)
264
+ val
265
+ end
266
+ end
267
+
268
+ class JdbcConnection
269
+ attr_reader :adapter, :connection
270
+
271
+ def initialize(config)
272
+ @config = config.symbolize_keys!
273
+ if @config[:jndi]
274
+ begin
275
+ configure_jndi
276
+ rescue => e
277
+ warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
278
+ configure_jdbc
279
+ end
280
+ else
281
+ configure_jdbc
282
+ end
283
+ set_native_database_types
284
+ @stmts = {}
285
+ rescue Exception => e
286
+ raise "The driver encountered an error: #{e}"
287
+ end
288
+
289
+ def reconnect!
290
+ self.adapter.reconnect!
291
+ end
292
+
293
+ def adapter=(adapt)
294
+ @adapter = adapt
295
+ @tps = {}
296
+ @native_types.each_pair {|k,v| @tps[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => (kv.last.dup rescue kv.last)})}}
297
+ adapt.modify_types(@tps)
298
+ end
299
+
300
+ # Default JDBC introspection for index metadata on the JdbcConnection.
301
+ # This is currently used for migrations by JdbcSpec::HSQDLB and JdbcSpec::Derby
302
+ # indexes with a little filtering tacked on.
303
+ #
304
+ # JDBC index metadata is denormalized (multiple rows may be returned for
305
+ # one index, one row per column in the index), so a simple block-based
306
+ # filter like that used for tables doesn't really work here. Callers
307
+ # should filter the return from this method instead.
308
+ #
309
+ # TODO: fix to use reconnect correctly
310
+ 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, [])
339
+ end
340
+
341
+ # One or more columns can be associated with an index
342
+ indexes.last.columns << column_name
343
+ 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
+ end
356
+
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
+ private
366
+ def configure_jndi
367
+ jndi = @config[:jndi].to_s
368
+ ctx = javax.naming.InitialContext.new
369
+ ds = ctx.lookup(jndi)
370
+ set_connection ds.connection
371
+ unless @config[:driver]
372
+ @config[:driver] = @connection.meta_data.connection.java_class.name
373
+ end
374
+ end
375
+
376
+ def configure_jdbc
377
+ driver = @config[:driver].to_s
378
+ user = @config[:username].to_s
379
+ pass = @config[:password].to_s
380
+ url = @config[:url].to_s
381
+
382
+ unless driver && url
383
+ raise ArgumentError, "jdbc adapter requires driver class and url"
384
+ end
385
+
386
+ if driver =~ /mysql/i
387
+ div = url =~ /\?/ ? '&' : '?'
388
+ url = "#{url}#{div}zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding=utf8"
389
+ @config[:url] = url
390
+ end
391
+
392
+ jdbc_driver = JdbcDriver.new(driver)
393
+ jdbc_driver.load
394
+ connection = jdbc_driver.connection(url, user, pass)
395
+ set_connection connection
396
+ end
397
+
398
+ end
399
+
400
+ class JdbcAdapter < AbstractAdapter
401
+ attr_reader :config
402
+
403
+ ADAPTER_TYPES = ::JdbcSpec.constants.map{|c|
404
+ ::JdbcSpec.const_get c }.select{ |c|
405
+ c.respond_to? :adapter_selector }.map{|c|
406
+ c.adapter_selector }.inject({}) { |h,val|
407
+ h[val[0]] = val[1]; h }
408
+
409
+ def initialize(connection, logger, config)
410
+ super(connection, logger)
411
+ @config = config
412
+ ds = config[:driver].to_s
413
+ for reg, func in ADAPTER_TYPES
414
+ if reg === ds
415
+ func.call(@config,self)
416
+ end
417
+ end
418
+ connection.adapter = self
419
+ end
420
+
421
+ def modify_types(tp)
422
+ tp
423
+ end
424
+
425
+ def adapter_name #:nodoc:
426
+ 'JDBC'
427
+ end
428
+
429
+ def supports_migrations?
430
+ true
431
+ end
432
+
433
+ def native_database_types #:nodoc:
434
+ @connection.native_database_types
435
+ end
436
+
437
+ def database_name #:nodoc:
438
+ @connection.database_name
439
+ end
440
+
441
+ def native_sql_to_type(tp)
442
+ if /^(.*?)\(([0-9]+)\)/ =~ tp
443
+ tname = $1
444
+ limit = $2.to_i
445
+ ntype = native_database_types
446
+ if ntype[:primary_key] == tp
447
+ return :primary_key,nil
448
+ else
449
+ ntype.each do |name,val|
450
+ if name == :primary_key
451
+ next
452
+ end
453
+ if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
454
+ return name,limit
455
+ end
456
+ end
457
+ end
458
+ elsif /^(.*?)/ =~ tp
459
+ tname = $1
460
+ ntype = native_database_types
461
+ if ntype[:primary_key] == tp
462
+ return :primary_key,nil
463
+ else
464
+ ntype.each do |name,val|
465
+ if val[:name].downcase == tname.downcase && val[:limit].nil?
466
+ return name,nil
467
+ end
468
+ end
469
+ end
470
+ else
471
+ return :string,255
472
+ end
473
+ return nil,nil
474
+ end
475
+
476
+ def reconnect!
477
+ @connection.close rescue nil
478
+ @connection = JdbcConnection.new(@config)
479
+ @connection.adapter = self
480
+ @connection
481
+ end
482
+
483
+ def select_all(sql, name = nil)
484
+ select(sql, name)
485
+ end
486
+
487
+ def select_one(sql, name = nil)
488
+ select(sql, name).first
489
+ end
490
+
491
+ def execute(sql, name = nil)
492
+ log_no_bench(sql, name) do
493
+ _execute(sql,name)
494
+ end
495
+ end
496
+
497
+
498
+ # we need to do it this way, to allow Rails stupid tests to always work
499
+ # even if we define a new execute method. Instead of mixing in a new
500
+ # execute, an _execute should be mixed in.
501
+ def _execute(sql, name = nil)
502
+ if JdbcConnection::select?(sql)
503
+ @connection.execute_query(sql)
504
+ elsif JdbcConnection::insert?(sql)
505
+ @connection.execute_insert(sql)
506
+ else
507
+ @connection.execute_update(sql)
508
+ end
509
+ end
510
+
511
+ def update(sql, name = nil) #:nodoc:
512
+ execute(sql, name)
513
+ end
514
+
515
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
516
+ id = execute(sql, name = nil)
517
+ id_value || id
518
+ end
519
+
520
+ def columns(table_name, name = nil)
521
+ @connection.columns(table_name.to_s)
522
+ end
523
+
524
+ def tables
525
+ @connection.tables
526
+ end
527
+
528
+ def indexes(table_name)
529
+ @connection.indexes(table_name)
530
+ end
531
+
532
+ def begin_db_transaction
533
+ @connection.begin
534
+ end
535
+
536
+ def commit_db_transaction
537
+ @connection.commit
538
+ end
539
+
540
+ def rollback_db_transaction
541
+ @connection.rollback
542
+ end
543
+
544
+ def write_large_object(*args)
545
+ @connection.write_large_object(*args)
546
+ end
547
+
548
+ private
549
+ def select(sql, name=nil)
550
+ execute(sql,name)
551
+ 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
+ end
574
+ end
575
+ end