Class: ActiveRecord::ConnectionAdapters::JdbcAdapter

Inherits:
AbstractAdapter
  • Object
show all
Includes:
ActiveRecord::ConnectionAdapters::Jdbc::ConnectionPoolCallbacks, ArJdbc::Abstract::ConnectionManagement, ArJdbc::Abstract::Core, ArJdbc::Abstract::DatabaseStatements, ArJdbc::Abstract::TransactionSupport
Defined in:
lib/arjdbc/jdbc/adapter.rb

Overview

Built on top of ActiveRecord::ConnectionAdapters::AbstractAdapter which provides the abstract interface for database-specific functionality, this class serves 2 purposes in AR-JDBC :

  • as a base class for sub-classes
  • usable standalone (or with a mixed in adapter spec module)

Historically this class is mostly been used standalone and that's still a valid use-case esp. since (with it's arjdbc.jdbc.RubyJdbcConnectionClass) JDBC provides a unified interface for all databases in Java it tries to do it's best implementing all ActiveRecord functionality on top of that. This might no be perfect that's why it checks for a config[:adapter_spec] module (or tries to resolve one from the JDBC driver's meta-data) and if the database has "extended" AR-JDBC support mixes in the given module for each adapter instance. This is sufficient for most database specific specs we support, but for compatibility with native (MRI) adapters it's perfectly fine to sub-class the adapter and override some of its API methods.

Direct Known Subclasses

H2Adapter, HsqldbAdapter, OracleAdapter

Constant Summary collapse

ADAPTER_NAME =
'JDBC'

Constants included from ArJdbc::Abstract::DatabaseStatements

ArJdbc::Abstract::DatabaseStatements::NO_BINDS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ArJdbc::Abstract::TransactionSupport

#begin_db_transaction, #begin_isolated_db_transaction, #commit_db_transaction, #create_savepoint, #exec_rollback_db_transaction, #exec_rollback_to_savepoint, #release_savepoint, #supports_savepoints?, #supports_transaction_isolation?

Methods included from ArJdbc::Abstract::DatabaseStatements

#exec_insert, #exec_update, #internal_exec_query, #select_all

Methods included from ArJdbc::Abstract::ConnectionManagement

#active?, #disconnect!, #really_valid?

Methods included from ArJdbc::Abstract::Core

#initialize, #jdbc_connection

Methods included from ActiveRecord::ConnectionAdapters::Jdbc::ConnectionPoolCallbacks

#on_checkin, #on_checkout

Instance Attribute Details

#prepared_statementsObject (readonly)

Returns the value of attribute prepared_statements.



47
48
49
# File 'lib/arjdbc/jdbc/adapter.rb', line 47

def prepared_statements
  @prepared_statements
end

Class Method Details

.arel2_visitors(config) ⇒ Hash

Deprecated.

re-implemented - no longer used

If there's a self.arel2_visitors(config) method on the adapter spec than it is preferred and will be used instead of this one.



126
127
128
# File 'lib/arjdbc/jdbc/adapter.rb', line 126

def self.arel2_visitors(config)
  { 'jdbc' => ::Arel::Visitors::ToSql }
end

.configure_arel2_visitors(config) ⇒ Object

Deprecated.

re-implemented - no longer used

See Also:

  • #arel2_visitors


132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/arjdbc/jdbc/adapter.rb', line 132

def self.configure_arel2_visitors(config)
  visitors = ::Arel::Visitors::VISITORS
  klass = config[:adapter_spec]
  klass = self unless klass.respond_to?(:arel2_visitors)
  visitor = nil
  klass.arel2_visitors(config).each do |name, arel|
    visitors[name] = ( visitor = arel )
  end
  if visitor && config[:adapter] =~ /^(jdbc|jndi)$/
    visitors[ config[:adapter] ] = visitor
  end
  visitor
end

.prepared_statements?(config) ⇒ Boolean (protected)

Allows changing the prepared statements setting for this connection. def prepared_statements=(statements) @prepared_statements = statements end



424
425
426
427
428
# File 'lib/arjdbc/jdbc/adapter.rb', line 424

def self.prepared_statements?(config)
  config.key?(:prepared_statements) ?
    type_cast_config_to_boolean(config.fetch(:prepared_statements)) :
      false # off by default - NOTE: on AR 4.x it's on by default !?
end

Instance Method Details

#adapter_nameString



101
102
103
# File 'lib/arjdbc/jdbc/adapter.rb', line 101

def adapter_name
  ADAPTER_NAME
end

#adapter_spec(config) ⇒ Module

Locate the specialized (database specific) adapter specification module if one exists based on provided configuration data. This module will than extend an instance of the adapter (unless an :adapter_class provided).

This method is called during ArJdbc::Abstract::Core#initialize unless an explicit config[:adapter_spec] is set.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/arjdbc/jdbc/adapter.rb', line 73

def adapter_spec(config)
  dialect = (config[:dialect] || config[:driver]).to_s
  ::ArJdbc.modules.each do |constant| # e.g. ArJdbc::MySQL
    if constant.respond_to?(:adapter_matcher)
      spec = constant.adapter_matcher(dialect, config)
      return spec if spec
    end
  end

  unless config.key?(:dialect)
    begin # does nothing unless config[:jndi] || config[:data_source]
      dialect = ::ArJdbc.(config) do
        || config[:dialect] = .getDatabaseProductName
      end
      return adapter_spec(config) if dialect # re-try matching with :dialect
    rescue => e
      ::ArJdbc.warn("failed to set :dialect from database meda-data: #{e.inspect}")
    else
      return adapter_spec(config) # re-try matching a spec with set config[:dialect]
    end
  end

  nil
end

#columns(table_name, name = nil) ⇒ Object



225
226
227
# File 'lib/arjdbc/jdbc/adapter.rb', line 225

def columns(table_name, name = nil)
  @connection.columns(table_name.to_s)
end

#data_source_exists?(name) ⇒ Boolean



313
314
315
# File 'lib/arjdbc/jdbc/adapter.rb', line 313

def data_source_exists?(name)
  table_exists?(name)
end

#data_sourcesObject



308
309
310
# File 'lib/arjdbc/jdbc/adapter.rb', line 308

def data_sources
  tables
end

#database_nameObject

Returns the underlying database name.



185
186
187
# File 'lib/arjdbc/jdbc/adapter.rb', line 185

def database_name
  @connection.database_name
end

#exec_query_raw(sql, name = 'SQL', binds = []) {|v1, v2| ... } ⇒ Array

Similar to #exec_query except it returns "raw" results in an array where each rows is a hash with keys as columns (just like Rails used to do up until 3.0) instead of wrapping them in a ActiveRecord::ConnectionAdapters::JdbcAdapter#ActiveRecord#ActiveRecord::Result. In case a block is given it will yield each row from the result set instead of returning mapped query results in an array.

Yields:

  • (v1, v2)

    depending on the row values returned from the query



244
245
246
247
248
249
250
251
252
# File 'lib/arjdbc/jdbc/adapter.rb', line 244

def exec_query_raw(sql, name = 'SQL', binds = [], &block)
  sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)

  if prepared_statements?
    log(sql, name, binds) { @connection.execute_query_raw(sql, binds, &block) }
  else
    log(sql, name) { @connection.execute_query_raw(sql, &block) }
  end
end

#execute(sql, name = nil, binds = nil) ⇒ Object

Note:

This method does not use prepared statements.

Note:

The method does not emulate various "native" execute results on MRI.

Executes the SQL statement in the context of this connection. The return value from this method depends on the SQL type (whether it's a SELECT, INSERT etc.). For INSERTs a generated id might get returned while for UPDATE statements the affected row count. Please note that this method returns "raw" results (in an array) for statements that return a result set, while #exec_query is expected to return a ActiveRecord::Result (since AR 3.1).



266
267
268
269
270
271
272
273
# File 'lib/arjdbc/jdbc/adapter.rb', line 266

def execute(sql, name = nil, binds = nil)
  sql = to_sql(sql, binds) if binds
  if name == :skip_logging
    _execute(sql, name)
  else
    log(sql, name) { _execute(sql, name) }
  end
end

#execute_quietly(sql, name = 'SQL') ⇒ Object

Kind of execute(sql) rescue nil but logging failures at debug level only.



286
287
288
289
290
291
292
293
294
# File 'lib/arjdbc/jdbc/adapter.rb', line 286

def execute_quietly(sql, name = 'SQL')
  log(sql, name) do
    begin
      _execute(sql)
    rescue => e
      logger.debug("#{e.class}: #{e.message}: #{sql}")
    end
  end
end

#foreign_keys(table_name) ⇒ Object



333
334
335
# File 'lib/arjdbc/jdbc/adapter.rb', line 333

def foreign_keys(table_name)
  @connection.foreign_keys(table_name)
end

#indexes(table_name, name = nil, schema_name = nil) ⇒ Object



318
319
320
# File 'lib/arjdbc/jdbc/adapter.rb', line 318

def indexes(table_name, name = nil, schema_name = nil)
  @connection.indexes(table_name, name, schema_name)
end

#is_a?(klass) ⇒ Boolean

Will return true even when native adapter classes passed in e.g. jdbc_adapter.is_a? ConnectionAdapter::PostgresqlAdapter

This is only necessary (for built-in adapters) when config[:adapter_class] is forced to nil and the :adapter_spec module is used to extend the JdbcAdapter, otherwise we replace the class constants for built-in adapters (MySQL, PostgreSQL and SQLite3).



113
114
115
116
117
118
119
120
# File 'lib/arjdbc/jdbc/adapter.rb', line 113

def is_a?(klass)
  # This is to fake out current_adapter? conditional logic in AR tests
  if klass.is_a?(Class) && klass.name =~ /#{adapter_name}Adapter$/i
    true
  else
    super
  end
end

#jdbc_column_classObject

Returns the (JDBC) ActiveRecord column class for this adapter. This is used by (database specific) spec modules to override the class.



61
62
63
# File 'lib/arjdbc/jdbc/adapter.rb', line 61

def jdbc_column_class
  ::ActiveRecord::ConnectionAdapters::JdbcColumn
end

#jdbc_connection_class(spec) ⇒ Object

Returns the (JDBC) connection class to be used for this adapter. This is used by (database specific) spec modules to override the class used assuming some of the available methods have been re-defined.



53
54
55
56
# File 'lib/arjdbc/jdbc/adapter.rb', line 53

def jdbc_connection_class(spec)
  connection_class = spec.jdbc_connection_class if spec && spec.respond_to?(:jdbc_connection_class)
  connection_class ? connection_class : ::ActiveRecord::ConnectionAdapters::JdbcConnection
end

#last_inserted_id(result) ⇒ Integer, NilClass (protected)

Take an id from the result of an INSERT query.



360
361
362
363
364
365
366
367
368
369
370
# File 'lib/arjdbc/jdbc/adapter.rb', line 360

def last_inserted_id(result)
  if result.is_a?(Hash) || result.is_a?(ActiveRecord::Result)
    # If table does not have primary key defined
    return nil if result.first.blank?


    result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
  else
    result
  end
end

#modify_types(types) ⇒ Object

Allows for modification of the detected native types.



166
167
168
# File 'lib/arjdbc/jdbc/adapter.rb', line 166

def modify_types(types)
  types
end

#native_database_typesHash

DB specific types are detected but adapter specs (or extenders) are expected to hand tune these types for concrete databases.



150
151
152
153
154
155
156
# File 'lib/arjdbc/jdbc/adapter.rb', line 150

def native_database_types
  @native_database_types ||= begin
    types = @connection.native_database_types
    modify_types(types)
    types
  end
end

#pk_and_sequence_for(table) ⇒ Object



323
324
325
# File 'lib/arjdbc/jdbc/adapter.rb', line 323

def pk_and_sequence_for(table)
  ( key = primary_key(table) ) ? [ key, nil ] : nil
end

#prepared_statements?Boolean (protected)



413
414
415
416
# File 'lib/arjdbc/jdbc/adapter.rb', line 413

def prepared_statements?
  return @prepared_statements unless (@prepared_statements ||= nil).nil?
  @prepared_statements = self.class.prepared_statements?(config)
end

#primary_keys(table) ⇒ Object



328
329
330
# File 'lib/arjdbc/jdbc/adapter.rb', line 328

def primary_keys(table)
  @connection.primary_keys(table)
end

#structure_dumpObject

Abstract adapter default implementation does nothing silently.

Raises:

  • (NotImplementedError)


172
173
174
# File 'lib/arjdbc/jdbc/adapter.rb', line 172

def structure_dump
  raise NotImplementedError, "structure_dump not supported"
end

#supports_foreign_keys?Boolean

Does our database (+ its JDBC driver) support foreign-keys?

Since:

  • 1.3.18



340
341
342
# File 'lib/arjdbc/jdbc/adapter.rb', line 340

def supports_foreign_keys?
  @connection.supports_foreign_keys?
end

#supports_migrations?true

JDBC adapters support migration.



179
180
181
# File 'lib/arjdbc/jdbc/adapter.rb', line 179

def supports_migrations?
  true
end

#supports_views?Boolean



230
231
232
# File 'lib/arjdbc/jdbc/adapter.rb', line 230

def supports_views?
  @connection.supports_views?
end

#table_definitionObject (protected)

aliasing #create_table_definition as #table_definition :



373
# File 'lib/arjdbc/jdbc/adapter.rb', line 373

alias table_definition create_table_definition

#table_exists?(name) ⇒ Boolean



302
303
304
305
# File 'lib/arjdbc/jdbc/adapter.rb', line 302

def table_exists?(name)
  return false unless name
  @connection.table_exists?(name) # schema_name = nil
end

#tables(name = nil) ⇒ Object



297
298
299
# File 'lib/arjdbc/jdbc/adapter.rb', line 297

def tables(name = nil)
  @connection.tables
end

#update_lob_value(record, column, value) ⇒ Object



352
353
354
# File 'lib/arjdbc/jdbc/adapter.rb', line 352

def update_lob_value(record, column, value)
  @connection.update_lob_value(record, column, value)
end

#valid_type?(type) ⇒ Boolean



159
160
161
# File 'lib/arjdbc/jdbc/adapter.rb', line 159

def valid_type?(type)
  ! native_database_types[type].nil?
end

#write_large_object(*args) ⇒ Object

Deprecated.

Rather use #update_lob_value instead.



345
346
347
# File 'lib/arjdbc/jdbc/adapter.rb', line 345

def write_large_object(*args)
  @connection.write_large_object(*args)
end