]> source.dussan.org Git - sonarqube.git/blob
3e0a6d3b941730e523b71e2e14d074109ff03eef
[sonarqube.git] /
1 require 'active_record/connection_adapters/abstract_adapter'
2 require 'java'
3 require 'active_record/connection_adapters/jdbc_adapter_spec'
4 require 'jdbc_adapter/jdbc_adapter_internal'
5 require 'bigdecimal'
6
7 begin
8   require 'jdbc_adapter/rake_tasks'
9 rescue LoadError
10 end if defined?(RAILS_ROOT)
11
12 module ActiveRecord
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]
23           if precision
24             if scale
25               column_type_sql += "(#{precision},#{scale})"
26             else
27               column_type_sql += "(#{precision})"
28             end
29           else
30             raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
31           end
32           column_type_sql
33         else
34           limit ||= native[:limit]
35           column_type_sql += "(#{limit})" if limit
36           column_type_sql
37         end
38       end
39     end
40   end
41 end
42
43 module JdbcSpec
44   module ActiveRecordExtensions
45     def jdbc_connection(config)
46       connection = ::ActiveRecord::ConnectionAdapters::JdbcConnection.new(config)
47       ::ActiveRecord::ConnectionAdapters::JdbcAdapter.new(connection, logger, config)
48     end
49     alias jndi_connection jdbc_connection
50
51     def embedded_driver(config)
52       config[:username] ||= "sa"
53       config[:password] ||= ""
54       jdbc_connection(config)
55     end
56   end
57 end
58
59 module ActiveRecord
60   class Base
61     extend JdbcSpec::ActiveRecordExtensions
62
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?
68       end
69       aq
70     end
71   end
72
73   module ConnectionAdapters
74     module Java
75       Class = java.lang.Class
76       URL = java.net.URL
77       URLClassLoader = java.net.URLClassLoader
78     end
79
80     module Jdbc
81       Mutex = java.lang.Object.new
82       DriverManager = java.sql.DriverManager
83       Statement = java.sql.Statement
84       Types = java.sql.Types
85
86       # some symbolic constants for the benefit of the JDBC-based
87       # JdbcConnection#indexes method
88       module IndexMetaData
89         INDEX_NAME  = 6
90         NON_UNIQUE  = 4
91         TABLE_NAME  = 3
92         COLUMN_NAME = 9
93       end
94
95       module TableMetaData
96         TABLE_CAT   = 1
97         TABLE_SCHEM = 2
98         TABLE_NAME  = 3
99         TABLE_TYPE  = 4
100       end
101
102       module PrimaryKeyMetaData
103         COLUMN_NAME = 4
104       end
105
106     end
107
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.
121       AR_TO_JDBC_TYPES = {
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}]
178       }
179
180       def initialize(types)
181         @types = types
182         @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
183       end
184
185       def choose_best_types
186         type_map = {}
187         @types.each do |row|
188           name = row['type_name'].downcase
189           k = name.to_sym
190           type_map[k] = { :name => name }
191           type_map[k][:limit] = row['precision'].to_i if row['precision']
192         end
193
194         AR_TO_JDBC_TYPES.keys.each do |k|
195           typerow = choose_type(k)
196           type_map[k] = { :name => typerow['type_name'].downcase }
197           case k
198           when :integer, :string, :decimal
199             type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
200           when :boolean
201             type_map[k][:limit] = 1
202           end
203         end
204         type_map
205       end
206
207       def choose_type(ar_type)
208         procs = AR_TO_JDBC_TYPES[ar_type]
209         types = @types
210         procs.each do |p|
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']}
214             typs
215           end
216           return new_types.first if new_types.length == 1
217           types = new_types if new_types.length > 0
218         end
219         raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
220       end
221     end
222
223     class JdbcDriver
224       def initialize(name)
225         @name = name
226       end
227
228       def driver_class
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
234               Jdbc.module_eval do
235                 include_class(driver_class_name) { driver_class_const }
236               end
237             end
238           end
239           driver_class = Jdbc.const_get(driver_class_const)
240           raise "You specify a driver for your JDBC connection" unless driver_class
241           driver_class
242         end
243       end
244
245       def load
246         Jdbc::DriverManager.registerDriver(create)
247       end
248
249       def connection(url, user, pass)
250         Jdbc::DriverManager.getConnection(url, user, pass)
251       rescue
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)
257       end
258
259       def create
260         driver_class.new
261       end
262     end
263
264     class JdbcColumn < Column
265       attr_writer :limit, :precision
266
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 }
272
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)
278           end
279         end
280         super(name,default_value(default),*args)
281         init_column(name, default, *args)
282       end
283
284       def init_column(*args)
285       end
286
287       def default_value(val)
288         val
289       end
290     end
291
292     include_class "jdbc_adapter.JdbcConnectionFactory"
293
294     class JdbcConnection
295       attr_reader :adapter, :connection_factory
296
297       def initialize(config)
298         @config = config.symbolize_keys!
299         @config[:retry_count] ||= 5
300         @config[:connection_alive_sql] ||= "select 1"
301         if @config[:jndi]
302           begin
303             configure_jndi
304           rescue => e
305             warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
306             configure_jdbc
307           end
308         else
309           configure_jdbc
310         end
311         connection # force the connection to load
312         set_native_database_types
313         @stmts = {}
314       rescue Exception => e
315         raise "The driver encountered an error: #{e}"
316       end
317
318       def adapter=(adapt)
319         @adapter = adapt
320         @tps = {}
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)
323       end
324
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.
328       #
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
336           begin
337             unless String === table_name
338               table_name = table_name.to_s
339             else
340               table_name = table_name.dup
341             end
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)
346             indexes = []
347             current_index = nil
348             while resultset.next
349               index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME)
350               next unless index_name
351               index_name.downcase!
352               column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
353
354               next if primary_keys.include? column_name
355
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)
361
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, [])
364               end
365
366               # One or more columns can be associated with an index
367               indexes.last.columns << column_name
368             end
369             resultset.close
370             indexes
371           ensure
372             metadata.close rescue nil
373           end
374         end
375       end
376
377       def jndi_connection?
378         @jndi_connection
379       end
380
381       private
382       def configure_jndi
383         jndi = @config[:jndi].to_s
384         ctx = javax.naming.InitialContext.new
385         ds = ctx.lookup(jndi)
386         @connection_factory = JdbcConnectionFactory.impl do
387           ds.connection
388         end
389         unless @config[:driver]
390           @config[:driver] = connection.meta_data.connection.java_class.name
391         end
392         @jndi_connection = true
393       end
394
395       def configure_jdbc
396         driver = @config[:driver].to_s
397         user   = @config[:username].to_s
398         pass   = @config[:password].to_s
399         url    = @config[:url].to_s
400
401         unless driver && url
402           raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
403         end
404
405         if driver =~ /mysql/i && url !~ /#{Regexp.quote(JdbcSpec::MySQL::URL_OPTIONS)}/
406           div = url =~ /\?/ ? '&' : '?'
407           url = "#{url}#{div}#{JdbcSpec::MySQL::URL_OPTIONS}"
408           @config[:url] = url
409         end
410
411         jdbc_driver = JdbcDriver.new(driver)
412         jdbc_driver.load
413         @connection_factory = JdbcConnectionFactory.impl do
414           jdbc_driver.connection(url, user, pass)
415         end
416       end
417     end
418
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
424           else
425             alias_method meth, target
426           end
427         end
428       end
429
430       module CompatibilityMethods
431         def self.needed?(base)
432           !base.instance_methods.include?("quote_table_name")
433         end
434
435         def quote_table_name(name)
436           quote_column_name(name)
437         end
438       end
439
440       module ConnectionPoolCallbacks
441         def self.included(base)
442           base.checkin :on_checkin
443           base.checkout :on_checkout
444         end
445
446         def self.needed?
447           ActiveRecord::Base.respond_to?(:connection_pool)
448         end
449
450         def on_checkin
451           # default implementation does nothing
452         end
453
454         def on_checkout
455           # default implementation does nothing
456         end
457       end
458
459       module JndiConnectionPoolCallbacks
460         def self.prepare(adapter, conn)
461           if ActiveRecord::Base.respond_to?(:connection_pool) && conn.jndi_connection?
462             adapter.extend self
463             conn.disconnect! # disconnect initial connection in JdbcConnection#initialize
464           end
465         end
466
467         def on_checkin
468           disconnect!
469         end
470
471         def on_checkout
472           reconnect!
473         end
474       end
475
476       extend ShadowCoreMethods
477       include CompatibilityMethods if CompatibilityMethods.needed?(self)
478       include ConnectionPoolCallbacks if ConnectionPoolCallbacks.needed?
479
480       attr_reader :config
481
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 }
487
488       def initialize(connection, logger, config)
489         super(connection, logger)
490         @config = config
491         dialect = config[:dialect] || config[:driver]
492         for reg, func in ADAPTER_TYPES
493           if reg === dialect.to_s
494             func.call(@config,self)
495           end
496         end
497         connection.adapter = self
498         JndiConnectionPoolCallbacks.prepare(self, connection)
499       end
500
501       def modify_types(tp)
502         tp
503       end
504
505       def adapter_name #:nodoc:
506         'JDBC'
507       end
508
509       def supports_migrations?
510         true
511       end
512
513       def native_database_types #:nodoc:
514         @connection.native_database_types
515       end
516
517       def database_name #:nodoc:
518         @connection.database_name
519       end
520
521       def native_sql_to_type(tp)
522         if /^(.*?)\(([0-9]+)\)/ =~ tp
523           tname = $1
524           limit = $2.to_i
525           ntype = native_database_types
526           if ntype[:primary_key] == tp
527             return :primary_key,nil
528           else
529             ntype.each do |name,val|
530               if name == :primary_key
531                 next
532               end
533               if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
534                 return name,limit
535               end
536             end
537           end
538         elsif /^(.*?)/ =~ tp
539           tname = $1
540           ntype = native_database_types
541           if ntype[:primary_key] == tp
542             return :primary_key,nil
543           else
544             ntype.each do |name,val|
545               if val[:name].downcase == tname.downcase && val[:limit].nil?
546                 return name,nil
547               end
548             end
549           end
550         else
551           return :string,255
552         end
553         return nil,nil
554       end
555
556       def reconnect!
557         @connection.reconnect!
558         @connection
559       end
560
561       def disconnect!
562         @connection.disconnect!
563       end
564
565       def jdbc_select_all(sql, name = nil)
566         select(sql, name)
567       end
568       alias_chained_method :select_all, :query_cache, :jdbc_select_all
569
570       def select_rows(sql, name = nil)
571         rows = []
572         select(sql, name).each {|row| rows << row.values }
573         rows
574       end
575
576       def select_one(sql, name = nil)
577         select(sql, name).first
578       end
579
580       def execute(sql, name = nil)
581         log(sql, name) do
582           _execute(sql,name)
583         end
584       end
585
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)
594         else
595           @connection.execute_update(sql)
596         end
597       end
598
599       def jdbc_update(sql, name = nil) #:nodoc:
600         execute(sql, name)
601       end
602       alias_chained_method :update, :query_dirty, :jdbc_update
603
604       def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
605         id = execute(sql, name = nil)
606         id_value || id
607       end
608       alias_chained_method :insert, :query_dirty, :jdbc_insert
609
610       def jdbc_columns(table_name, name = nil)
611         @connection.columns(table_name.to_s)
612       end
613       alias_chained_method :columns, :query_cache, :jdbc_columns
614
615       def tables
616         @connection.tables
617       end
618
619       def indexes(table_name, name = nil, schema_name = nil)
620         @connection.indexes(table_name, name, schema_name)
621       end
622
623       def begin_db_transaction
624         @connection.begin
625       end
626
627       def commit_db_transaction
628         @connection.commit
629       end
630
631       def rollback_db_transaction
632         @connection.rollback
633       end
634
635       def write_large_object(*args)
636         @connection.write_large_object(*args)
637       end
638
639       private
640       def select(sql, name=nil)
641         execute(sql,name)
642       end
643     end
644   end
645 end