activerecord-jdbc-adapter 0.9.3-java

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