-
Notifications
You must be signed in to change notification settings - Fork 386
/
Copy pathadapter.rb
474 lines (410 loc) · 16.6 KB
/
adapter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# frozen_string_literal: true
require 'active_record/version'
require 'active_record/connection_adapters/abstract_adapter'
require 'arjdbc/version'
require 'arjdbc/jdbc/java'
require 'arjdbc/jdbc/error'
require 'arjdbc/jdbc/connection_methods'
require 'arjdbc/jdbc/column'
require 'arjdbc/jdbc/connection'
require 'arjdbc/jdbc/callbacks'
require 'arjdbc/jdbc/extension'
require 'arjdbc/jdbc/type_converter'
require 'arjdbc/abstract/core'
require 'arjdbc/abstract/connection_management'
require 'arjdbc/abstract/database_statements'
require 'arjdbc/abstract/transaction_support'
module ActiveRecord
module ConnectionAdapters
# 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.
class JdbcAdapter < AbstractAdapter
include Jdbc::ConnectionPoolCallbacks
include ArJdbc::Abstract::Core
include ArJdbc::Abstract::ConnectionManagement
include ArJdbc::Abstract::DatabaseStatements
include ArJdbc::Abstract::TransactionSupport
attr_reader :prepared_statements
# 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.
# @see ActiveRecord::ConnectionAdapters::JdbcConnection
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
# Returns the (JDBC) `ActiveRecord` column class for this adapter.
# This is used by (database specific) spec modules to override the class.
# @see ActiveRecord::ConnectionAdapters::JdbcColumn
def jdbc_column_class
::ActiveRecord::ConnectionAdapters::JdbcColumn
end
# 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 {#initialize} unless an explicit
# `config[:adapter_spec]` is set.
# @param config the configuration to check for `:adapter_spec`
# @return [Module] the database specific module
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.with_meta_data_from_data_source_if_any(config) do
|meta_data| config[:dialect] = meta_data.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
ADAPTER_NAME = 'JDBC'
# @return [String] the 'JDBC' adapter name.
def adapter_name
ADAPTER_NAME
end
# @override
# 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).
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
# @deprecated re-implemented - no longer used
# @return [Hash] the AREL visitor to use
# 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.
def self.arel2_visitors(config)
{ 'jdbc' => ::Arel::Visitors::ToSql }
end
# @deprecated re-implemented - no longer used
# @see #arel2_visitors
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
# DB specific types are detected but adapter specs (or extenders) are
# expected to hand tune these types for concrete databases.
# @return [Hash] the native database types
# @override
def native_database_types
@native_database_types ||= begin
types = @connection.native_database_types
modify_types(types)
types
end
end
# @override introduced in AR 4.2
def valid_type?(type)
! native_database_types[type].nil?
end
# Allows for modification of the detected native types.
# @param types the resolved native database types
# @see #native_database_types
def modify_types(types)
types
end
# Abstract adapter default implementation does nothing silently.
# @override
def structure_dump
raise NotImplementedError, "structure_dump not supported"
end
# JDBC adapters support migration.
# @return [true]
# @override
def supports_migrations?
true
end
# Returns the underlying database name.
# @override
def database_name
@connection.database_name
end
# @private
def native_sql_to_type(type)
if /^(.*?)\(([0-9]+)\)/ =~ type
tname, limit = $1, $2.to_i
ntypes = native_database_types
if ntypes[:primary_key] == type
return :primary_key, nil
else
ntypes.each do |name, val|
if name == :primary_key
next
end
if val[:name].downcase == tname.downcase &&
( val[:limit].nil? || val[:limit].to_i == limit )
return name, limit
end
end
end
elsif /^(.*?)/ =~ type
tname = $1
ntypes = native_database_types
if ntypes[:primary_key] == type
return :primary_key, nil
else
ntypes.each do |name, val|
if val[:name].downcase == tname.downcase && val[:limit].nil?
return name, nil
end
end
end
else
return :string, 255
end
return nil, nil
end
def columns(table_name, name = nil)
@connection.columns(table_name.to_s)
end
# @override
def supports_views?
@connection.supports_views?
end
# 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::Result}.
# @param sql the query string (or AREL object)
# @param name logging marker for the executed SQL statement log entry
# @param binds the bind parameters
# @yield [v1, v2] depending on the row values returned from the query
# In case a block is given it will yield each row from the result set
# instead of returning mapped query results in an array.
# @return [Array] unless a block is given
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
# 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).
# @note This method does not use prepared statements.
# @note The method does not emulate various "native" `execute` results on MRI.
# @see #exec_query
# @see #exec_insert
# @see #exec_update
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
# We need to do it this way, to allow Rails stupid tests to always work
# even if we define a new `execute` method. Instead of mixing in a new
# `execute`, an `_execute` should be mixed in.
# @deprecated it was only introduced due tests
# @private
def _execute(sql, name = nil)
@connection.execute(sql)
end
private :_execute
# Kind of `execute(sql) rescue nil` but logging failures at debug level only.
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
# @override
def tables(name = nil)
@connection.tables
end
# @override
def table_exists?(name)
return false unless name
@connection.table_exists?(name) # schema_name = nil
end
# @override
def data_sources
tables
end
# @override
def data_source_exists?(name)
table_exists?(name)
end
# @override
def indexes(table_name, name = nil, schema_name = nil)
@connection.indexes(table_name, name, schema_name)
end
# @override
def pk_and_sequence_for(table)
( key = primary_key(table) ) ? [ key, nil ] : nil
end
# @override
def primary_keys(table)
@connection.primary_keys(table)
end
# @override
def foreign_keys(table_name)
@connection.foreign_keys(table_name)
end
# Does our database (+ its JDBC driver) support foreign-keys?
# @since 1.3.18
# @override
def supports_foreign_keys?
@connection.supports_foreign_keys?
end
# @deprecated Rather use {#update_lob_value} instead.
def write_large_object(*args)
@connection.write_large_object(*args)
end
# @param record the record e.g. `User.find(1)`
# @param column the model's column e.g. `User.columns_hash['photo']`
# @param value the lob value - string or (IO or Java) stream
def update_lob_value(record, column, value)
@connection.update_lob_value(record, column, value)
end
protected
# Take an id from the result of an INSERT query.
# @return [Integer, NilClass]
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
# aliasing #create_table_definition as #table_definition :
alias table_definition create_table_definition
# `TableDefinition.new native_database_types, name, temporary, options`
# and ActiveRecord 4.1 supports optional `as` argument (which defaults
# to nil) to provide the SQL to use to generate the table:
# `TableDefinition.new native_database_types, name, temporary, options, as`
# @private
def create_table_definition(*args)
table_definition(*args)
end
# @note AR-4x arguments expected: `(name, temporary, options)`
# @private documented bellow
def new_table_definition(table_definition, *args)
table_definition.new(*args)
end
private :new_table_definition
# @private
def new_index_definition(table, name, unique, columns, lengths,
orders = nil, where = nil, type = nil, using = nil)
IndexDefinition.new(table, name, unique, columns, lengths, orders, where, type, using)
end
private :new_index_definition
#
# Provides backwards-compatibility on ActiveRecord 4.1 for DB adapters
# that override this and than call super expecting to work.
# @note This method is available in 4.0 but won't be in 4.1
# @private
def add_column_options!(sql, options)
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
# must explicitly check for :null to allow change_column to work on migrations
sql << " NOT NULL" if options[:null] == false
sql << " AUTO_INCREMENT" if options[:auto_increment] == true
end
public :add_column_options!
# @return whether `:prepared_statements` are to be used
def prepared_statements?
return @prepared_statements unless (@prepared_statements ||= nil).nil?
@prepared_statements = self.class.prepared_statements?(config)
end
# Allows changing the prepared statements setting for this connection.
# @see #prepared_statements?
#def prepared_statements=(statements)
# @prepared_statements = statements
#end
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
private
# Helper useful during {#quote} since AREL might pass in it's literals
# to be quoted, fixed since AREL 4.0.0.beta1 : https://github.com/rails/arel/commit/9c514f3
def sql_literal?(value); ::Arel::Nodes::SqlLiteral === value; end
# Helper to get local/UTC time (based on `ActiveRecord::default_timezone`).
def get_time(value)
get = ::ActiveRecord.default_timezone == :utc ? :getutc : :getlocal
value.respond_to?(get) ? value.send(get) : value
end
# @return whether the given SQL string is a 'SELECT' like
# query (returning a result set)
def self.select?(sql)
JdbcConnection::select?(sql)
end
# @return whether the given SQL string is an 'INSERT' query
def self.insert?(sql)
JdbcConnection::insert?(sql)
end
# @return whether the given SQL string is an 'UPDATE' (or 'DELETE') query
def self.update?(sql)
! select?(sql) && ! insert?(sql)
end
unless defined? AbstractAdapter.type_cast_config_to_integer
# @private
def self.type_cast_config_to_integer(config)
config =~ /\A\d+\z/ ? config.to_i : config
end
end
# @private
def self.type_cast_config_to_boolean(config)
config == 'false' ? false : (config == 'true' ? true : config)
end
end
end
end