1 require 'active_record/connection_adapters/abstract_adapter'
3 require 'active_record/connection_adapters/jdbc_adapter_spec'
4 require 'jdbc_adapter/jdbc_adapter_internal'
8 require 'jdbc_adapter/rake_tasks'
10 end if defined?(RAILS_ROOT)
13 module ConnectionAdapters # :nodoc:
14 module SchemaStatements
15 # The original implementation of this had a bug, which modifies native_database_types.
16 # This version allows us to cache that value.
17 def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
18 native = native_database_types[type.to_s.downcase.to_sym]
19 column_type_sql = native.is_a?(Hash) ? native[:name] : native
20 if type == :decimal # ignore limit, use precison and scale
21 precision ||= native[:precision]
22 scale ||= native[:scale]
25 column_type_sql += "(#{precision},#{scale})"
27 column_type_sql += "(#{precision})"
30 raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
34 limit ||= native[:limit]
35 column_type_sql += "(#{limit})" if limit
44 module ActiveRecordExtensions
45 def jdbc_connection(config)
46 connection = ::ActiveRecord::ConnectionAdapters::JdbcConnection.new(config)
47 ::ActiveRecord::ConnectionAdapters::JdbcAdapter.new(connection, logger, config)
49 alias jndi_connection jdbc_connection
51 def embedded_driver(config)
52 config[:username] ||= "sa"
53 config[:password] ||= ""
54 jdbc_connection(config)
61 extend JdbcSpec::ActiveRecordExtensions
63 alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
64 def attributes_with_quotes(include_primary_key = true, *args) #:nodoc:
65 aq = attributes_with_quotes_pre_oracle(include_primary_key, *args)
66 if connection.class == ConnectionAdapters::JdbcAdapter && (connection.is_a?(JdbcSpec::Oracle) || connection.is_a?(JdbcSpec::Mimer))
67 aq[self.class.primary_key] = "?" if include_primary_key && aq[self.class.primary_key].nil?
73 module ConnectionAdapters
75 Class = java.lang.Class
77 URLClassLoader = java.net.URLClassLoader
81 Mutex = java.lang.Object.new
82 DriverManager = java.sql.DriverManager
83 Statement = java.sql.Statement
84 Types = java.sql.Types
86 # some symbolic constants for the benefit of the JDBC-based
87 # JdbcConnection#indexes method
102 module PrimaryKeyMetaData
108 # I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
109 # use for ActiveRecord's Adapter#native_database_types in a database-independent way,
110 # but apparently a database driver can return multiple types for a given
111 # java.sql.Types constant. So this type converter uses some heuristics to try to pick
112 # the best (most common) type to use. It's not great, it would be better to just
113 # delegate to each database's existin AR adapter's native_database_types method, but I
114 # wanted to try to do this in a way that didn't pull in all the other adapters as
115 # dependencies. Suggestions appreciated.
116 class JdbcTypeConverter
117 # The basic ActiveRecord types, mapped to an array of procs that are used to #select
118 # the best type. The procs are used as selectors in order until there is only one
119 # type left. If all the selectors are applied and there is still more than one
120 # type, an exception will be raised.
122 :string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
123 lambda {|r| r['type_name'] =~ /^varchar/i},
124 lambda {|r| r['type_name'] =~ /^varchar$/i},
125 lambda {|r| r['type_name'] =~ /varying/i}],
126 :text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
127 lambda {|r| r['type_name'] =~ /^text$/i}, # For Informix
128 lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
129 lambda {|r| r['type_name'] =~ /^character large object$/i},
130 lambda {|r| r['sql_data_type'] == 2005}],
131 :integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
132 lambda {|r| r['type_name'] =~ /^integer$/i},
133 lambda {|r| r['type_name'] =~ /^int4$/i},
134 lambda {|r| r['type_name'] =~ /^int$/i}],
135 :decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
136 lambda {|r| r['type_name'] =~ /^decimal$/i},
137 lambda {|r| r['type_name'] =~ /^numeric$/i},
138 lambda {|r| r['type_name'] =~ /^number$/i},
139 lambda {|r| r['type_name'] =~ /^real$/i},
140 lambda {|r| r['precision'] == '38'},
141 lambda {|r| r['data_type'] == '2'}],
142 :float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE, Jdbc::Types::REAL].include?(r['data_type'].to_i)},
143 lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
144 lambda {|r| r['type_name'] =~ /^float/i},
145 lambda {|r| r['type_name'] =~ /^double$/i},
146 lambda {|r| r['type_name'] =~ /^real$/i},
147 lambda {|r| r['precision'] == '15'}],
148 :datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
149 lambda {|r| r['type_name'] =~ /^datetime$/i},
150 lambda {|r| r['type_name'] =~ /^timestamp$/i},
151 lambda {|r| r['type_name'] =~ /^date/i},
152 lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
153 :timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
154 lambda {|r| r['type_name'] =~ /^timestamp$/i},
155 lambda {|r| r['type_name'] =~ /^datetime/i},
156 lambda {|r| r['type_name'] =~ /^date/i},
157 lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
158 :time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
159 lambda {|r| r['type_name'] =~ /^time$/i},
160 lambda {|r| r['type_name'] =~ /^datetime/i}, # For Informix
161 lambda {|r| r['type_name'] =~ /^date/i},
162 lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
163 :date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
164 lambda {|r| r['type_name'] =~ /^date$/i},
165 lambda {|r| r['type_name'] =~ /^date/i},
166 lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver3
167 :binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
168 lambda {|r| r['type_name'] =~ /^blob/i},
169 lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
170 lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
171 lambda {|r| r['type_name'] =~ /^binary$/i}, ],
172 :boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'].to_i)},
173 lambda {|r| r['type_name'] =~ /^bool/i},
174 lambda {|r| r['data_type'] == '-7'},
175 lambda {|r| r['type_name'] =~ /^tinyint$/i},
176 lambda {|r| r['type_name'] =~ /^decimal$/i},
177 lambda {|r| r['type_name'] =~ /^integer$/i}]
180 def initialize(types)
182 @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
185 def choose_best_types
188 name = row['type_name'].downcase
190 type_map[k] = { :name => name }
191 type_map[k][:limit] = row['precision'].to_i if row['precision']
194 AR_TO_JDBC_TYPES.keys.each do |k|
195 typerow = choose_type(k)
196 type_map[k] = { :name => typerow['type_name'].downcase }
198 when :integer, :string, :decimal
199 type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
201 type_map[k][:limit] = 1
207 def choose_type(ar_type)
208 procs = AR_TO_JDBC_TYPES[ar_type]
211 new_types = types.select(&p)
212 new_types = new_types.inject([]) do |typs,t|
213 typs << t unless typs.detect {|el| el['type_name'] == t['type_name']}
216 return new_types.first if new_types.length == 1
217 types = new_types if new_types.length > 0
219 raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
229 @driver_class ||= begin
230 driver_class_const = (@name[0...1].capitalize + @name[1..@name.length]).gsub(/\./, '_')
231 Jdbc::Mutex.synchronized do
232 unless Jdbc.const_defined?(driver_class_const)
233 driver_class_name = @name
235 include_class(driver_class_name) { driver_class_const }
239 driver_class = Jdbc.const_get(driver_class_const)
240 raise "You specify a driver for your JDBC connection" unless driver_class
246 Jdbc::DriverManager.registerDriver(create)
249 def connection(url, user, pass)
250 Jdbc::DriverManager.getConnection(url, user, pass)
252 # bypass DriverManager to get around problem with dynamically loaded jdbc drivers
253 props = java.util.Properties.new
254 props.setProperty("user", user)
255 props.setProperty("password", pass)
256 create.connect(url, props)
264 class JdbcColumn < Column
265 attr_writer :limit, :precision
267 COLUMN_TYPES = ::JdbcSpec.constants.map{|c|
268 ::JdbcSpec.const_get c }.select{ |c|
269 c.respond_to? :column_selector }.map{|c|
270 c.column_selector }.inject({}) { |h,val|
271 h[val[0]] = val[1]; h }
273 def initialize(config, name, default, *args)
274 dialect = config[:dialect] || config[:driver]
275 for reg, func in COLUMN_TYPES
276 if reg === dialect.to_s
277 func.call(config,self)
280 super(name,default_value(default),*args)
281 init_column(name, default, *args)
284 def init_column(*args)
287 def default_value(val)
292 include_class "jdbc_adapter.JdbcConnectionFactory"
295 attr_reader :adapter, :connection_factory
297 def initialize(config)
298 @config = config.symbolize_keys!
299 @config[:retry_count] ||= 5
300 @config[:connection_alive_sql] ||= "select 1"
305 warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
311 connection # force the connection to load
312 set_native_database_types
314 rescue Exception => e
315 raise "The driver encountered an error: #{e}"
321 @native_types.each_pair {|k,v| @tps[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => (kv.last.dup rescue kv.last)})}}
322 adapt.modify_types(@tps)
325 # Default JDBC introspection for index metadata on the JdbcConnection.
326 # This is currently used for migrations by JdbcSpec::HSQDLB and JdbcSpec::Derby
327 # indexes with a little filtering tacked on.
329 # JDBC index metadata is denormalized (multiple rows may be returned for
330 # one index, one row per column in the index), so a simple block-based
331 # filter like that used for tables doesn't really work here. Callers
332 # should filter the return from this method instead.
333 def indexes(table_name, name = nil, schema_name = nil)
334 with_connection_retry_guard do |conn|
335 metadata = conn.getMetaData
337 unless String === table_name
338 table_name = table_name.to_s
340 table_name = table_name.dup
342 table_name.upcase! if metadata.storesUpperCaseIdentifiers
343 table_name.downcase! if metadata.storesLowerCaseIdentifiers
344 resultset = metadata.getIndexInfo(nil, schema_name, table_name, false, false)
345 primary_keys = primary_keys(table_name)
349 index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME)
350 next unless index_name
352 column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
354 next if primary_keys.include? column_name
356 # We are working on a new index
357 if current_index != index_name
358 current_index = index_name
359 table_name = resultset.get_string(Jdbc::IndexMetaData::TABLE_NAME).downcase
360 non_unique = resultset.get_boolean(Jdbc::IndexMetaData::NON_UNIQUE)
362 # empty list for column names, we'll add to that in just a bit
363 indexes << IndexDefinition.new(table_name, index_name, !non_unique, [])
366 # One or more columns can be associated with an index
367 indexes.last.columns << column_name
372 metadata.close rescue nil
383 jndi = @config[:jndi].to_s
384 ctx = javax.naming.InitialContext.new
385 ds = ctx.lookup(jndi)
386 @connection_factory = JdbcConnectionFactory.impl do
389 unless @config[:driver]
390 @config[:driver] = connection.meta_data.connection.java_class.name
392 @jndi_connection = true
396 driver = @config[:driver].to_s
397 user = @config[:username].to_s
398 pass = @config[:password].to_s
399 url = @config[:url].to_s
402 raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
405 if driver =~ /mysql/i && url !~ /#{Regexp.quote(JdbcSpec::MySQL::URL_OPTIONS)}/
406 div = url =~ /\?/ ? '&' : '?'
407 url = "#{url}#{div}#{JdbcSpec::MySQL::URL_OPTIONS}"
411 jdbc_driver = JdbcDriver.new(driver)
413 @connection_factory = JdbcConnectionFactory.impl do
414 jdbc_driver.connection(url, user, pass)
419 class JdbcAdapter < AbstractAdapter
420 module ShadowCoreMethods
421 def alias_chained_method(meth, feature, target)
422 if instance_methods.include?("#{meth}_without_#{feature}")
423 alias_method "#{meth}_without_#{feature}".to_sym, target
425 alias_method meth, target
430 module CompatibilityMethods
431 def self.needed?(base)
432 !base.instance_methods.include?("quote_table_name")
435 def quote_table_name(name)
436 quote_column_name(name)
440 module ConnectionPoolCallbacks
441 def self.included(base)
442 base.checkin :on_checkin
443 base.checkout :on_checkout
447 ActiveRecord::Base.respond_to?(:connection_pool)
451 # default implementation does nothing
455 # default implementation does nothing
459 module JndiConnectionPoolCallbacks
460 def self.prepare(adapter, conn)
461 if ActiveRecord::Base.respond_to?(:connection_pool) && conn.jndi_connection?
463 conn.disconnect! # disconnect initial connection in JdbcConnection#initialize
476 extend ShadowCoreMethods
477 include CompatibilityMethods if CompatibilityMethods.needed?(self)
478 include ConnectionPoolCallbacks if ConnectionPoolCallbacks.needed?
482 ADAPTER_TYPES = ::JdbcSpec.constants.map{|c|
483 ::JdbcSpec.const_get c }.select{ |c|
484 c.respond_to? :adapter_selector }.map{|c|
485 c.adapter_selector }.inject({}) { |h,val|
486 h[val[0]] = val[1]; h }
488 def initialize(connection, logger, config)
489 super(connection, logger)
491 dialect = config[:dialect] || config[:driver]
492 for reg, func in ADAPTER_TYPES
493 if reg === dialect.to_s
494 func.call(@config,self)
497 connection.adapter = self
498 JndiConnectionPoolCallbacks.prepare(self, connection)
505 def adapter_name #:nodoc:
509 def supports_migrations?
513 def native_database_types #:nodoc:
514 @connection.native_database_types
517 def database_name #:nodoc:
518 @connection.database_name
521 def native_sql_to_type(tp)
522 if /^(.*?)\(([0-9]+)\)/ =~ tp
525 ntype = native_database_types
526 if ntype[:primary_key] == tp
527 return :primary_key,nil
529 ntype.each do |name,val|
530 if name == :primary_key
533 if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
540 ntype = native_database_types
541 if ntype[:primary_key] == tp
542 return :primary_key,nil
544 ntype.each do |name,val|
545 if val[:name].downcase == tname.downcase && val[:limit].nil?
557 @connection.reconnect!
562 @connection.disconnect!
565 def jdbc_select_all(sql, name = nil)
568 alias_chained_method :select_all, :query_cache, :jdbc_select_all
570 def select_rows(sql, name = nil)
572 select(sql, name).each {|row| rows << row.values }
576 def select_one(sql, name = nil)
577 select(sql, name).first
580 def execute(sql, name = nil)
586 # we need to do it this way, to allow Rails stupid tests to always work
587 # even if we define a new execute method. Instead of mixing in a new
588 # execute, an _execute should be mixed in.
589 def _execute(sql, name = nil)
590 if JdbcConnection::select?(sql)
591 @connection.execute_query(sql)
592 elsif JdbcConnection::insert?(sql)
593 @connection.execute_insert(sql)
595 @connection.execute_update(sql)
599 def jdbc_update(sql, name = nil) #:nodoc:
602 alias_chained_method :update, :query_dirty, :jdbc_update
604 def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
605 id = execute(sql, name = nil)
608 alias_chained_method :insert, :query_dirty, :jdbc_insert
610 def jdbc_columns(table_name, name = nil)
611 @connection.columns(table_name.to_s)
613 alias_chained_method :columns, :query_cache, :jdbc_columns
619 def indexes(table_name, name = nil, schema_name = nil)
620 @connection.indexes(table_name, name, schema_name)
623 def begin_db_transaction
627 def commit_db_transaction
631 def rollback_db_transaction
635 def write_large_object(*args)
636 @connection.write_large_object(*args)
640 def select(sql, name=nil)