activerecord-jdbc-adapter 0.6

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 (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