activerecord-jdbc-adapter 0.9.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/History.txt +248 -0
  2. data/LICENSE.txt +21 -0
  3. data/Manifest.txt +125 -0
  4. data/README.txt +218 -0
  5. data/Rakefile +10 -0
  6. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  7. data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
  8. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
  9. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
  10. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  11. data/lib/active_record/connection_adapters/jdbc_adapter.rb +640 -0
  12. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +26 -0
  13. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  14. data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
  15. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  16. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  17. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  18. data/lib/generators/jdbc/jdbc_generator.rb +9 -0
  19. data/lib/jdbc_adapter.rb +27 -0
  20. data/lib/jdbc_adapter/jdbc.rake +121 -0
  21. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  22. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  23. data/lib/jdbc_adapter/jdbc_db2.rb +203 -0
  24. data/lib/jdbc_adapter/jdbc_derby.rb +430 -0
  25. data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
  26. data/lib/jdbc_adapter/jdbc_hsqldb.rb +218 -0
  27. data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
  28. data/lib/jdbc_adapter/jdbc_mimer.rb +141 -0
  29. data/lib/jdbc_adapter/jdbc_mssql.rb +337 -0
  30. data/lib/jdbc_adapter/jdbc_mysql.rb +236 -0
  31. data/lib/jdbc_adapter/jdbc_oracle.rb +377 -0
  32. data/lib/jdbc_adapter/jdbc_postgre.rb +498 -0
  33. data/lib/jdbc_adapter/jdbc_sqlite3.rb +384 -0
  34. data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
  35. data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
  36. data/lib/jdbc_adapter/rake_tasks.rb +10 -0
  37. data/lib/jdbc_adapter/tsql_helper.rb +60 -0
  38. data/lib/jdbc_adapter/version.rb +5 -0
  39. data/lib/pg.rb +4 -0
  40. data/rails_generators/jdbc_generator.rb +15 -0
  41. data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
  42. data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
  43. data/rakelib/compile.rake +23 -0
  44. data/rakelib/package.rake +90 -0
  45. data/rakelib/rails.rake +41 -0
  46. data/rakelib/test.rake +76 -0
  47. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
  48. data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
  49. data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
  50. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +134 -0
  51. data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
  52. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
  53. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1162 -0
  54. data/src/java/jdbc_adapter/SQLBlock.java +27 -0
  55. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  56. data/test/abstract_db_create.rb +107 -0
  57. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  58. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  59. data/test/cachedb_simple_test.rb +6 -0
  60. data/test/db/cachedb.rb +9 -0
  61. data/test/db/db2.rb +9 -0
  62. data/test/db/derby.rb +14 -0
  63. data/test/db/h2.rb +11 -0
  64. data/test/db/hsqldb.rb +12 -0
  65. data/test/db/informix.rb +11 -0
  66. data/test/db/jdbc.rb +11 -0
  67. data/test/db/jndi_config.rb +30 -0
  68. data/test/db/logger.rb +3 -0
  69. data/test/db/mssql.rb +9 -0
  70. data/test/db/mysql.rb +10 -0
  71. data/test/db/oracle.rb +34 -0
  72. data/test/db/postgres.rb +9 -0
  73. data/test/db/sqlite3.rb +15 -0
  74. data/test/db2_simple_test.rb +10 -0
  75. data/test/derby_migration_test.rb +21 -0
  76. data/test/derby_multibyte_test.rb +12 -0
  77. data/test/derby_simple_test.rb +21 -0
  78. data/test/generic_jdbc_connection_test.rb +9 -0
  79. data/test/h2_simple_test.rb +6 -0
  80. data/test/has_many_through.rb +79 -0
  81. data/test/helper.rb +5 -0
  82. data/test/hsqldb_simple_test.rb +6 -0
  83. data/test/informix_simple_test.rb +48 -0
  84. data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
  85. data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
  86. data/test/jdbc_common.rb +25 -0
  87. data/test/jndi_callbacks_test.rb +38 -0
  88. data/test/jndi_test.rb +35 -0
  89. data/test/manualTestDatabase.rb +191 -0
  90. data/test/minirunit.rb +109 -0
  91. data/test/minirunit/testConnect.rb +14 -0
  92. data/test/minirunit/testH2.rb +73 -0
  93. data/test/minirunit/testHsqldb.rb +73 -0
  94. data/test/minirunit/testLoadActiveRecord.rb +3 -0
  95. data/test/minirunit/testMysql.rb +83 -0
  96. data/test/minirunit/testRawSelect.rb +24 -0
  97. data/test/models/add_not_null_column_to_table.rb +12 -0
  98. data/test/models/auto_id.rb +18 -0
  99. data/test/models/data_types.rb +28 -0
  100. data/test/models/entry.rb +23 -0
  101. data/test/models/mixed_case.rb +20 -0
  102. data/test/models/reserved_word.rb +18 -0
  103. data/test/models/string_id.rb +18 -0
  104. data/test/models/validates_uniqueness_of_string.rb +19 -0
  105. data/test/mssql_simple_test.rb +6 -0
  106. data/test/mysql_db_create_test.rb +25 -0
  107. data/test/mysql_multibyte_test.rb +10 -0
  108. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  109. data/test/mysql_simple_test.rb +32 -0
  110. data/test/oracle_simple_test.rb +29 -0
  111. data/test/pick_rails_version.rb +3 -0
  112. data/test/postgres_db_create_test.rb +21 -0
  113. data/test/postgres_mixed_case_test.rb +19 -0
  114. data/test/postgres_nonseq_pkey_test.rb +40 -0
  115. data/test/postgres_reserved_test.rb +22 -0
  116. data/test/postgres_schema_search_path_test.rb +46 -0
  117. data/test/postgres_simple_test.rb +13 -0
  118. data/test/simple.rb +475 -0
  119. data/test/sqlite3_simple_test.rb +233 -0
  120. data/test/sybase_jtds_simple_test.rb +6 -0
  121. metadata +188 -0
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+ require 'rake/clean'
3
+ CLEAN.include 'derby*', 'test.db.*','test/reports', 'test.sqlite3','lib/**/*.jar','manifest.mf', '*.log'
4
+
5
+ task :default => [:java_compile, :test]
6
+
7
+ task :filelist do
8
+ puts FileList['pkg/**/*'].inspect
9
+ end
10
+
@@ -0,0 +1 @@
1
+ require 'active_record/connection_adapters/jdbc_adapter'
@@ -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,13 @@
1
+ tried_gem = false
2
+ begin
3
+ require "jdbc/h2"
4
+ rescue LoadError
5
+ unless tried_gem
6
+ require 'rubygems'
7
+ gem "jdbc-h2"
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,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 @@
1
+ require 'active_record/connection_adapters/jdbc_adapter'
@@ -0,0 +1,640 @@
1
+ require 'active_record/version'
2
+ require 'active_record/connection_adapters/abstract_adapter'
3
+ require 'java'
4
+ require 'active_record/connection_adapters/jdbc_adapter_spec'
5
+ require 'jdbc_adapter/jdbc_adapter_internal'
6
+ require 'bigdecimal'
7
+
8
+ # AR's 2.2 version of this method is sufficient, but we need it for
9
+ # older versions
10
+ if ActiveRecord::VERSION::MAJOR <= 2 && ActiveRecord::VERSION::MINOR < 2
11
+ module ActiveRecord
12
+ module ConnectionAdapters # :nodoc:
13
+ module SchemaStatements
14
+ # Convert the speficied column type to a SQL string.
15
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
16
+ if native = native_database_types[type]
17
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
18
+
19
+ if type == :decimal # ignore limit, use precision and scale
20
+ scale ||= native[:scale]
21
+
22
+ if precision ||= native[:precision]
23
+ if scale
24
+ column_type_sql << "(#{precision},#{scale})"
25
+ else
26
+ column_type_sql << "(#{precision})"
27
+ end
28
+ elsif scale
29
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
30
+ end
31
+
32
+ elsif limit ||= native.is_a?(Hash) && native[:limit]
33
+ column_type_sql << "(#{limit})"
34
+ end
35
+
36
+ column_type_sql
37
+ else
38
+ type
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ module JdbcSpec
47
+ module ActiveRecordExtensions
48
+ def jdbc_connection(config)
49
+ ::ActiveRecord::ConnectionAdapters::JdbcAdapter.new(nil, logger, config)
50
+ end
51
+ alias jndi_connection jdbc_connection
52
+
53
+ def embedded_driver(config)
54
+ config[:username] ||= "sa"
55
+ config[:password] ||= ""
56
+ jdbc_connection(config)
57
+ end
58
+ end
59
+ end
60
+
61
+ module ActiveRecord
62
+ class Base
63
+ extend JdbcSpec::ActiveRecordExtensions
64
+
65
+ alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
66
+ def attributes_with_quotes(include_primary_key = true, *args) #:nodoc:
67
+ aq = attributes_with_quotes_pre_oracle(include_primary_key, *args)
68
+ if connection.class == ConnectionAdapters::JdbcAdapter && (connection.is_a?(JdbcSpec::Oracle) || connection.is_a?(JdbcSpec::Mimer))
69
+ aq[self.class.primary_key] = "?" if include_primary_key && aq[self.class.primary_key].nil?
70
+ end
71
+ aq
72
+ end
73
+ end
74
+
75
+ module ConnectionAdapters
76
+ module Java
77
+ Class = java.lang.Class
78
+ URL = java.net.URL
79
+ URLClassLoader = java.net.URLClassLoader
80
+ end
81
+
82
+ module Jdbc
83
+ Mutex = java.lang.Object.new
84
+ DriverManager = java.sql.DriverManager
85
+ Statement = java.sql.Statement
86
+ Types = java.sql.Types
87
+
88
+ # some symbolic constants for the benefit of the JDBC-based
89
+ # JdbcConnection#indexes method
90
+ module IndexMetaData
91
+ INDEX_NAME = 6
92
+ NON_UNIQUE = 4
93
+ TABLE_NAME = 3
94
+ COLUMN_NAME = 9
95
+ end
96
+
97
+ module TableMetaData
98
+ TABLE_CAT = 1
99
+ TABLE_SCHEM = 2
100
+ TABLE_NAME = 3
101
+ TABLE_TYPE = 4
102
+ end
103
+
104
+ module PrimaryKeyMetaData
105
+ COLUMN_NAME = 4
106
+ end
107
+
108
+ end
109
+
110
+ # I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
111
+ # use for ActiveRecord's Adapter#native_database_types in a database-independent way,
112
+ # but apparently a database driver can return multiple types for a given
113
+ # java.sql.Types constant. So this type converter uses some heuristics to try to pick
114
+ # the best (most common) type to use. It's not great, it would be better to just
115
+ # delegate to each database's existin AR adapter's native_database_types method, but I
116
+ # wanted to try to do this in a way that didn't pull in all the other adapters as
117
+ # dependencies. Suggestions appreciated.
118
+ class JdbcTypeConverter
119
+ # The basic ActiveRecord types, mapped to an array of procs that are used to #select
120
+ # the best type. The procs are used as selectors in order until there is only one
121
+ # type left. If all the selectors are applied and there is still more than one
122
+ # type, an exception will be raised.
123
+ AR_TO_JDBC_TYPES = {
124
+ :string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
125
+ lambda {|r| r['type_name'] =~ /^varchar/i},
126
+ lambda {|r| r['type_name'] =~ /^varchar$/i},
127
+ lambda {|r| r['type_name'] =~ /varying/i}],
128
+ :text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
129
+ lambda {|r| r['type_name'] =~ /^text$/i}, # For Informix
130
+ lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
131
+ lambda {|r| r['type_name'] =~ /^character large object$/i},
132
+ lambda {|r| r['sql_data_type'] == 2005}],
133
+ :integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
134
+ lambda {|r| r['type_name'] =~ /^integer$/i},
135
+ lambda {|r| r['type_name'] =~ /^int4$/i},
136
+ lambda {|r| r['type_name'] =~ /^int$/i}],
137
+ :decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
138
+ lambda {|r| r['type_name'] =~ /^decimal$/i},
139
+ lambda {|r| r['type_name'] =~ /^numeric$/i},
140
+ lambda {|r| r['type_name'] =~ /^number$/i},
141
+ lambda {|r| r['type_name'] =~ /^real$/i},
142
+ lambda {|r| r['precision'] == '38'},
143
+ lambda {|r| r['data_type'] == '2'}],
144
+ :float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE, Jdbc::Types::REAL].include?(r['data_type'].to_i)},
145
+ lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
146
+ lambda {|r| r['type_name'] =~ /^float/i},
147
+ lambda {|r| r['type_name'] =~ /^double$/i},
148
+ lambda {|r| r['type_name'] =~ /^real$/i},
149
+ lambda {|r| r['precision'] == '15'}],
150
+ :datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
151
+ lambda {|r| r['type_name'] =~ /^datetime$/i},
152
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
153
+ lambda {|r| r['type_name'] =~ /^date/i},
154
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
155
+ :timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
156
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
157
+ lambda {|r| r['type_name'] =~ /^datetime/i},
158
+ lambda {|r| r['type_name'] =~ /^date/i},
159
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
160
+ :time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
161
+ lambda {|r| r['type_name'] =~ /^time$/i},
162
+ lambda {|r| r['type_name'] =~ /^datetime/i}, # For Informix
163
+ lambda {|r| r['type_name'] =~ /^date/i},
164
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
165
+ :date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
166
+ lambda {|r| r['type_name'] =~ /^date$/i},
167
+ lambda {|r| r['type_name'] =~ /^date/i},
168
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver3
169
+ :binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
170
+ lambda {|r| r['type_name'] =~ /^blob/i},
171
+ lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
172
+ lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
173
+ lambda {|r| r['type_name'] =~ /^binary$/i}, ],
174
+ :boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'].to_i)},
175
+ lambda {|r| r['type_name'] =~ /^bool/i},
176
+ lambda {|r| r['data_type'] == '-7'},
177
+ lambda {|r| r['type_name'] =~ /^tinyint$/i},
178
+ lambda {|r| r['type_name'] =~ /^decimal$/i},
179
+ lambda {|r| r['type_name'] =~ /^integer$/i}]
180
+ }
181
+
182
+ def initialize(types)
183
+ @types = types
184
+ @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
185
+ end
186
+
187
+ def choose_best_types
188
+ type_map = {}
189
+ @types.each do |row|
190
+ name = row['type_name'].downcase
191
+ k = name.to_sym
192
+ type_map[k] = { :name => name }
193
+ type_map[k][:limit] = row['precision'].to_i if row['precision']
194
+ end
195
+
196
+ AR_TO_JDBC_TYPES.keys.each do |k|
197
+ typerow = choose_type(k)
198
+ type_map[k] = { :name => typerow['type_name'].downcase }
199
+ case k
200
+ when :integer, :string, :decimal
201
+ type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
202
+ when :boolean
203
+ type_map[k][:limit] = 1
204
+ end
205
+ end
206
+ type_map
207
+ end
208
+
209
+ def choose_type(ar_type)
210
+ procs = AR_TO_JDBC_TYPES[ar_type]
211
+ types = @types
212
+ procs.each do |p|
213
+ new_types = types.reject {|r| r["data_type"].to_i == Jdbc::Types::OTHER}
214
+ new_types = new_types.select(&p)
215
+ new_types = new_types.inject([]) do |typs,t|
216
+ typs << t unless typs.detect {|el| el['type_name'] == t['type_name']}
217
+ typs
218
+ end
219
+ return new_types.first if new_types.length == 1
220
+ types = new_types if new_types.length > 0
221
+ end
222
+ raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
223
+ end
224
+ end
225
+
226
+ class JdbcDriver
227
+ def initialize(name)
228
+ @name = name
229
+ end
230
+
231
+ def driver_class
232
+ @driver_class ||= begin
233
+ driver_class_const = (@name[0...1].capitalize + @name[1..@name.length]).gsub(/\./, '_')
234
+ Jdbc::Mutex.synchronized do
235
+ unless Jdbc.const_defined?(driver_class_const)
236
+ driver_class_name = @name
237
+ Jdbc.module_eval do
238
+ include_class(driver_class_name) { driver_class_const }
239
+ end
240
+ end
241
+ end
242
+ driver_class = Jdbc.const_get(driver_class_const)
243
+ raise "You specify a driver for your JDBC connection" unless driver_class
244
+ driver_class
245
+ end
246
+ end
247
+
248
+ def load
249
+ Jdbc::DriverManager.registerDriver(create)
250
+ end
251
+
252
+ def connection(url, user, pass)
253
+ Jdbc::DriverManager.getConnection(url, user, pass)
254
+ rescue
255
+ # bypass DriverManager to get around problem with dynamically loaded jdbc drivers
256
+ props = java.util.Properties.new
257
+ props.setProperty("user", user)
258
+ props.setProperty("password", pass)
259
+ create.connect(url, props)
260
+ end
261
+
262
+ def create
263
+ driver_class.new
264
+ end
265
+ end
266
+
267
+ class JdbcColumn < Column
268
+ attr_writer :limit, :precision
269
+
270
+ COLUMN_TYPES = ::JdbcSpec.constants.map{|c|
271
+ ::JdbcSpec.const_get c }.select{ |c|
272
+ c.respond_to? :column_selector }.map{|c|
273
+ c.column_selector }.inject({}) { |h,val|
274
+ h[val[0]] = val[1]; h }
275
+
276
+ def initialize(config, name, default, *args)
277
+ dialect = config[:dialect] || config[:driver]
278
+ for reg, func in COLUMN_TYPES
279
+ if reg === dialect.to_s
280
+ func.call(config,self)
281
+ end
282
+ end
283
+ super(name,default_value(default),*args)
284
+ init_column(name, default, *args)
285
+ end
286
+
287
+ def init_column(*args)
288
+ end
289
+
290
+ def default_value(val)
291
+ val
292
+ end
293
+ end
294
+
295
+ include_class "jdbc_adapter.JdbcConnectionFactory"
296
+
297
+ class JdbcConnection
298
+ attr_reader :adapter, :connection_factory
299
+
300
+ # @native_database_types - setup properly by adapter= versus set_native_database_types.
301
+ # This contains type information for the adapter. Individual adapters can make tweaks
302
+ # by defined modify_types
303
+ #
304
+ # @native_types - This is the default type settings sans any modifications by the
305
+ # individual adapter. My guess is that if we loaded two adapters of different types
306
+ # then this is used as a base to be tweaked by each adapter to create @native_database_types
307
+
308
+ def initialize(config)
309
+ @config = config.symbolize_keys!
310
+ @config[:retry_count] ||= 5
311
+ @config[:connection_alive_sql] ||= "select 1"
312
+ if @config[:jndi]
313
+ begin
314
+ configure_jndi
315
+ rescue => e
316
+ warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
317
+ configure_jdbc
318
+ end
319
+ else
320
+ configure_jdbc
321
+ end
322
+ connection # force the connection to load
323
+ set_native_database_types
324
+ @stmts = {}
325
+ rescue Exception => e
326
+ raise "The driver encountered an error: #{e}"
327
+ end
328
+
329
+ def adapter=(adapter)
330
+ @adapter = adapter
331
+ @native_database_types = dup_native_types
332
+ @adapter.modify_types(@native_database_types)
333
+ end
334
+
335
+ # Duplicate all native types into new hash structure so it can be modified
336
+ # without destroying original structure.
337
+ def dup_native_types
338
+ types = {}
339
+ @native_types.each_pair do |k, v|
340
+ types[k] = v.inject({}) do |memo, kv|
341
+ memo[kv.first] = begin kv.last.dup rescue kv.last end
342
+ memo
343
+ end
344
+ end
345
+ types
346
+ end
347
+ private :dup_native_types
348
+
349
+ def jndi_connection?
350
+ @jndi_connection
351
+ end
352
+
353
+ private
354
+ def configure_jndi
355
+ jndi = @config[:jndi].to_s
356
+ ctx = javax.naming.InitialContext.new
357
+ ds = ctx.lookup(jndi)
358
+ @connection_factory = JdbcConnectionFactory.impl do
359
+ ds.connection
360
+ end
361
+ unless @config[:driver]
362
+ @config[:driver] = connection.meta_data.connection.java_class.name
363
+ end
364
+ @jndi_connection = true
365
+ end
366
+
367
+ def configure_jdbc
368
+ driver = @config[:driver].to_s
369
+ user = @config[:username].to_s
370
+ pass = @config[:password].to_s
371
+ url = @config[:url].to_s
372
+
373
+ unless driver && url
374
+ raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
375
+ end
376
+
377
+ jdbc_driver = JdbcDriver.new(driver)
378
+ jdbc_driver.load
379
+ @connection_factory = JdbcConnectionFactory.impl do
380
+ jdbc_driver.connection(url, user, pass)
381
+ end
382
+ end
383
+ end
384
+
385
+ class JdbcAdapter < AbstractAdapter
386
+ module ShadowCoreMethods
387
+ def alias_chained_method(meth, feature, target)
388
+ if instance_methods.include?("#{meth}_without_#{feature}")
389
+ alias_method "#{meth}_without_#{feature}".to_sym, target
390
+ else
391
+ alias_method meth, target if meth != target
392
+ end
393
+ end
394
+ end
395
+
396
+ module CompatibilityMethods
397
+ def self.needed?(base)
398
+ !base.instance_methods.include?("quote_table_name")
399
+ end
400
+
401
+ def quote_table_name(name)
402
+ quote_column_name(name)
403
+ end
404
+ end
405
+
406
+ module ConnectionPoolCallbacks
407
+ def self.included(base)
408
+ if base.respond_to?(:set_callback) # Rails 3 callbacks
409
+ base.set_callback :checkin, :after, :on_checkin
410
+ base.set_callback :checkout, :before, :on_checkout
411
+ else
412
+ base.checkin :on_checkin
413
+ base.checkout :on_checkout
414
+ end
415
+ end
416
+
417
+ def self.needed?
418
+ ActiveRecord::Base.respond_to?(:connection_pool)
419
+ end
420
+
421
+ def on_checkin
422
+ # default implementation does nothing
423
+ end
424
+
425
+ def on_checkout
426
+ # default implementation does nothing
427
+ end
428
+ end
429
+
430
+ module JndiConnectionPoolCallbacks
431
+ def self.prepare(adapter, conn)
432
+ if ActiveRecord::Base.respond_to?(:connection_pool) && conn.jndi_connection?
433
+ adapter.extend self
434
+ conn.disconnect! # disconnect initial connection in JdbcConnection#initialize
435
+ end
436
+ end
437
+
438
+ def on_checkin
439
+ disconnect!
440
+ end
441
+
442
+ def on_checkout
443
+ reconnect!
444
+ end
445
+ end
446
+
447
+ extend ShadowCoreMethods
448
+ include CompatibilityMethods if CompatibilityMethods.needed?(self)
449
+ include ConnectionPoolCallbacks if ConnectionPoolCallbacks.needed?
450
+
451
+ attr_reader :config
452
+
453
+ def initialize(connection, logger, config)
454
+ @config = config
455
+ spec = adapter_spec config
456
+ unless connection
457
+ connection_class = jdbc_connection_class spec
458
+ connection = connection_class.new config
459
+ end
460
+ super(connection, logger)
461
+ extend spec if spec
462
+ connection.adapter = self
463
+ JndiConnectionPoolCallbacks.prepare(self, connection)
464
+ end
465
+
466
+ def jdbc_connection_class(spec)
467
+ connection_class = spec.jdbc_connection_class if spec && spec.respond_to?(:jdbc_connection_class)
468
+ connection_class = ::ActiveRecord::ConnectionAdapters::JdbcConnection unless connection_class
469
+ connection_class
470
+ end
471
+
472
+ # Locate specialized adapter specification if one exists based on config data
473
+ def adapter_spec(config)
474
+ dialect = (config[:dialect] || config[:driver]).to_s
475
+ ::JdbcSpec.constants.map { |name| ::JdbcSpec.const_get name }.each do |constant|
476
+ if constant.respond_to? :adapter_matcher
477
+ spec = constant.adapter_matcher(dialect, config)
478
+ return spec if spec
479
+ end
480
+ end
481
+ nil
482
+ end
483
+
484
+ def modify_types(tp)
485
+ tp
486
+ end
487
+
488
+ def adapter_name #:nodoc:
489
+ 'JDBC'
490
+ end
491
+
492
+ def supports_migrations?
493
+ true
494
+ end
495
+
496
+ def native_database_types #:nodoc:
497
+ @connection.native_database_types
498
+ end
499
+
500
+ def database_name #:nodoc:
501
+ @connection.database_name
502
+ end
503
+
504
+ def native_sql_to_type(tp)
505
+ if /^(.*?)\(([0-9]+)\)/ =~ tp
506
+ tname = $1
507
+ limit = $2.to_i
508
+ ntype = native_database_types
509
+ if ntype[:primary_key] == tp
510
+ return :primary_key,nil
511
+ else
512
+ ntype.each do |name,val|
513
+ if name == :primary_key
514
+ next
515
+ end
516
+ if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
517
+ return name,limit
518
+ end
519
+ end
520
+ end
521
+ elsif /^(.*?)/ =~ tp
522
+ tname = $1
523
+ ntype = native_database_types
524
+ if ntype[:primary_key] == tp
525
+ return :primary_key,nil
526
+ else
527
+ ntype.each do |name,val|
528
+ if val[:name].downcase == tname.downcase && val[:limit].nil?
529
+ return name,nil
530
+ end
531
+ end
532
+ end
533
+ else
534
+ return :string,255
535
+ end
536
+ return nil,nil
537
+ end
538
+
539
+ def reconnect!
540
+ @connection.reconnect!
541
+ @connection
542
+ end
543
+
544
+ def disconnect!
545
+ @connection.disconnect!
546
+ end
547
+
548
+ def jdbc_select_all(sql, name = nil)
549
+ select(sql, name)
550
+ end
551
+ alias_chained_method :select_all, :query_cache, :jdbc_select_all
552
+
553
+ def select_rows(sql, name = nil)
554
+ rows = []
555
+ select(sql, name).each {|row| rows << row.values }
556
+ rows
557
+ end
558
+
559
+ def select_one(sql, name = nil)
560
+ select(sql, name).first
561
+ end
562
+
563
+ def execute(sql, name = nil)
564
+ log(sql, name) do
565
+ _execute(sql,name)
566
+ end
567
+ end
568
+
569
+ # we need to do it this way, to allow Rails stupid tests to always work
570
+ # even if we define a new execute method. Instead of mixing in a new
571
+ # execute, an _execute should be mixed in.
572
+ def _execute(sql, name = nil)
573
+ if JdbcConnection::select?(sql)
574
+ @connection.execute_query(sql)
575
+ elsif JdbcConnection::insert?(sql)
576
+ @connection.execute_insert(sql)
577
+ else
578
+ @connection.execute_update(sql)
579
+ end
580
+ end
581
+
582
+ def jdbc_update(sql, name = nil) #:nodoc:
583
+ execute(sql, name)
584
+ end
585
+ alias_chained_method :update, :query_dirty, :jdbc_update
586
+
587
+ def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
588
+ id = execute(sql, name = nil)
589
+ id_value || id
590
+ end
591
+ alias_chained_method :insert, :query_dirty, :jdbc_insert
592
+
593
+ def jdbc_columns(table_name, name = nil)
594
+ @connection.columns(table_name.to_s)
595
+ end
596
+ alias_chained_method :columns, :query_cache, :jdbc_columns
597
+
598
+ def tables
599
+ @connection.tables
600
+ end
601
+
602
+ def indexes(table_name, name = nil, schema_name = nil)
603
+ @connection.indexes(table_name, name, schema_name)
604
+ end
605
+
606
+ def begin_db_transaction
607
+ @connection.begin
608
+ end
609
+
610
+ def commit_db_transaction
611
+ @connection.commit
612
+ end
613
+
614
+ def rollback_db_transaction
615
+ @connection.rollback
616
+ end
617
+
618
+ def write_large_object(*args)
619
+ @connection.write_large_object(*args)
620
+ end
621
+
622
+ def pk_and_sequence_for(table)
623
+ result_set = @connection.connection.get_meta_data.get_primary_keys(nil, nil, table)
624
+ if result_set.next
625
+ keys = [result_set.getString("COLUMN_NAME"), nil]
626
+ end
627
+ keys.blank? ? nil : keys
628
+ ensure
629
+ result_set.close
630
+ end
631
+
632
+ private
633
+ def select(sql, name=nil)
634
+ log(sql, name) do
635
+ @connection.execute_query(sql)
636
+ end
637
+ end
638
+ end
639
+ end
640
+ end