activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +390 -21
- data/README.md +35 -8
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
- data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
- data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
- data/lib/arel/visitors/oracle.rb +221 -0
- data/lib/arel/visitors/oracle12.rb +128 -0
- data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
- data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
- data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
- data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
- data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
- data/spec/spec_config.yaml.template +2 -2
- data/spec/spec_helper.rb +13 -2
- metadata +52 -30
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -30,6 +30,9 @@
|
|
30
30
|
# contribution.
|
31
31
|
# portions Copyright 2005 Graham Jenkins
|
32
32
|
|
33
|
+
require "arel/visitors/oracle"
|
34
|
+
require "arel/visitors/oracle12"
|
35
|
+
require "active_record/connection_adapters"
|
33
36
|
require "active_record/connection_adapters/abstract_adapter"
|
34
37
|
require "active_record/connection_adapters/statement_pool"
|
35
38
|
require "active_record/connection_adapters/oracle_enhanced/connection"
|
@@ -38,7 +41,6 @@ require "active_record/connection_adapters/oracle_enhanced/schema_creation"
|
|
38
41
|
require "active_record/connection_adapters/oracle_enhanced/schema_definitions"
|
39
42
|
require "active_record/connection_adapters/oracle_enhanced/schema_dumper"
|
40
43
|
require "active_record/connection_adapters/oracle_enhanced/schema_statements"
|
41
|
-
require "active_record/connection_adapters/oracle_enhanced/schema_statements_ext"
|
42
44
|
require "active_record/connection_adapters/oracle_enhanced/context_index"
|
43
45
|
require "active_record/connection_adapters/oracle_enhanced/column"
|
44
46
|
require "active_record/connection_adapters/oracle_enhanced/quoting"
|
@@ -58,11 +60,12 @@ require "active_record/type/oracle_enhanced/boolean"
|
|
58
60
|
require "active_record/type/oracle_enhanced/json"
|
59
61
|
require "active_record/type/oracle_enhanced/timestamptz"
|
60
62
|
require "active_record/type/oracle_enhanced/timestampltz"
|
63
|
+
require "active_record/type/oracle_enhanced/character_string"
|
61
64
|
|
62
65
|
module ActiveRecord
|
63
|
-
module ConnectionHandling
|
66
|
+
module ConnectionHandling # :nodoc:
|
64
67
|
# Establishes a connection to the database that's used by all Active Record objects.
|
65
|
-
def oracle_enhanced_connection(config)
|
68
|
+
def oracle_enhanced_connection(config) # :nodoc:
|
66
69
|
if config[:emulate_oracle_adapter] == true
|
67
70
|
# allows the enhanced adapter to look like the OracleAdapter. Useful to pick up
|
68
71
|
# conditionals in the rails activerecord test suite
|
@@ -76,7 +79,7 @@ module ActiveRecord
|
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
79
|
-
module ConnectionAdapters
|
82
|
+
module ConnectionAdapters # :nodoc:
|
80
83
|
# Oracle enhanced adapter will work with both
|
81
84
|
# CRuby ruby-oci8 gem (which provides interface to Oracle OCI client)
|
82
85
|
# or with JRuby and Oracle JDBC driver.
|
@@ -117,14 +120,13 @@ module ActiveRecord
|
|
117
120
|
# * <tt>:schema</tt> - database schema which holds schema objects.
|
118
121
|
# * <tt>:tcp_keepalive</tt> - TCP keepalive is enabled for OCI client, defaults to true
|
119
122
|
# * <tt>:tcp_keepalive_time</tt> - TCP keepalive time for OCI client, defaults to 600
|
123
|
+
# * <tt>:jdbc_statement_cache_size</tt> - number of cached SQL cursors to keep open, disabled per default (for unpooled JDBC only)
|
120
124
|
#
|
121
125
|
# Optionals NLS parameters:
|
122
126
|
#
|
123
127
|
# * <tt>:nls_calendar</tt>
|
124
128
|
# * <tt>:nls_comp</tt>
|
125
129
|
# * <tt>:nls_currency</tt>
|
126
|
-
# * <tt>:nls_date_format</tt> - format for :date columns, defaults to <tt>YYYY-MM-DD HH24:MI:SS</tt>
|
127
|
-
# (changing `nls_date_format` parameter value is not supported. Use the default value.)
|
128
130
|
# * <tt>:nls_date_language</tt>
|
129
131
|
# * <tt>:nls_dual_currency</tt>
|
130
132
|
# * <tt>:nls_iso_currency</tt>
|
@@ -135,16 +137,18 @@ module ActiveRecord
|
|
135
137
|
# * <tt>:nls_numeric_characters</tt>
|
136
138
|
# * <tt>:nls_sort</tt>
|
137
139
|
# * <tt>:nls_territory</tt>
|
138
|
-
# * <tt>:nls_timestamp_format</tt> - format for :timestamp columns, defaults to <tt>YYYY-MM-DD HH24:MI:SS:FF6</tt>
|
139
|
-
# (changing `nls_timestamp_format` parameter value is not supported. Use the default value.)
|
140
140
|
# * <tt>:nls_timestamp_tz_format</tt>
|
141
141
|
# * <tt>:nls_time_format</tt>
|
142
142
|
# * <tt>:nls_time_tz_format</tt>
|
143
143
|
#
|
144
|
+
# Fixed NLS values (not overridable):
|
145
|
+
#
|
146
|
+
# * <tt>:nls_date_format</tt> - format for :date columns is <tt>YYYY-MM-DD HH24:MI:SS</tt>
|
147
|
+
# * <tt>:nls_timestamp_format</tt> - format for :timestamp columns is <tt>YYYY-MM-DD HH24:MI:SS:FF6</tt>
|
148
|
+
#
|
144
149
|
class OracleEnhancedAdapter < AbstractAdapter
|
145
150
|
include OracleEnhanced::DatabaseStatements
|
146
151
|
include OracleEnhanced::SchemaStatements
|
147
|
-
include OracleEnhanced::SchemaStatementsExt
|
148
152
|
include OracleEnhanced::ContextIndex
|
149
153
|
include OracleEnhanced::Quoting
|
150
154
|
include OracleEnhanced::DatabaseLimits
|
@@ -196,15 +200,41 @@ module ActiveRecord
|
|
196
200
|
|
197
201
|
##
|
198
202
|
# :singleton-method:
|
199
|
-
# Specify default sequence start with value (by default
|
203
|
+
# Specify default sequence start with value (by default 1 if not explicitly set), e.g.:
|
200
204
|
#
|
201
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value =
|
205
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 10000
|
202
206
|
cattr_accessor :default_sequence_start_value
|
203
|
-
self.default_sequence_start_value =
|
207
|
+
self.default_sequence_start_value = 1
|
208
|
+
|
209
|
+
##
|
210
|
+
# :singleton-method:
|
211
|
+
# By default, OracleEnhanced adapter will use longer 128 bytes identifier
|
212
|
+
# if database version is Oracle 12.2 or higher.
|
213
|
+
# If you wish to use shorter 30 byte identifier with Oracle Database supporting longer identifier
|
214
|
+
# you can add the following line to your initializer file:
|
215
|
+
#
|
216
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.use_shorter_identifier = true
|
217
|
+
cattr_accessor :use_shorter_identifier
|
218
|
+
self.use_shorter_identifier = false
|
219
|
+
|
220
|
+
##
|
221
|
+
# :singleton-method:
|
222
|
+
# By default, OracleEnhanced adapter will grant unlimited tablespace, create session, create table, create view,
|
223
|
+
# and create sequence when running the rake task db:create.
|
224
|
+
#
|
225
|
+
# If you wish to change these permissions you can add the following line to your initializer file:
|
226
|
+
#
|
227
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.permissions =
|
228
|
+
# ["create session", "create table", "create view", "create sequence", "create trigger", "ctxapp"]
|
229
|
+
cattr_accessor :permissions
|
230
|
+
self.permissions = ["unlimited tablespace", "create session", "create table", "create view", "create sequence"]
|
231
|
+
|
232
|
+
##
|
233
|
+
# :singleton-method:
|
234
|
+
# Specify default sequence start with value (by default 1 if not explicitly set), e.g.:
|
204
235
|
|
205
236
|
class StatementPool < ConnectionAdapters::StatementPool
|
206
237
|
private
|
207
|
-
|
208
238
|
def dealloc(stmt)
|
209
239
|
stmt.close
|
210
240
|
end
|
@@ -212,18 +242,25 @@ module ActiveRecord
|
|
212
242
|
|
213
243
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
214
244
|
super(connection, logger, config)
|
215
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
216
245
|
@enable_dbms_output = false
|
217
246
|
@do_not_prefetch_primary_key = {}
|
218
247
|
@columns_cache = {}
|
219
248
|
end
|
220
249
|
|
221
|
-
ADAPTER_NAME = "OracleEnhanced"
|
250
|
+
ADAPTER_NAME = "OracleEnhanced"
|
222
251
|
|
223
|
-
def adapter_name
|
252
|
+
def adapter_name # :nodoc:
|
224
253
|
ADAPTER_NAME
|
225
254
|
end
|
226
255
|
|
256
|
+
# Oracle enhanced adapter has no implementation because
|
257
|
+
# Oracle Database cannot detect `NoDatabaseError`.
|
258
|
+
# Please refer to the following discussion for details.
|
259
|
+
# https://github.com/rsim/oracle-enhanced/pull/1900
|
260
|
+
def self.database_exists?(config)
|
261
|
+
raise NotImplementedError
|
262
|
+
end
|
263
|
+
|
227
264
|
def arel_visitor # :nodoc:
|
228
265
|
if supports_fetch_first_n_rows_and_offset?
|
229
266
|
Arel::Visitors::Oracle12.new(self)
|
@@ -232,11 +269,15 @@ module ActiveRecord
|
|
232
269
|
end
|
233
270
|
end
|
234
271
|
|
235
|
-
def
|
272
|
+
def build_statement_pool
|
273
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
274
|
+
end
|
275
|
+
|
276
|
+
def supports_savepoints? # :nodoc:
|
236
277
|
true
|
237
278
|
end
|
238
279
|
|
239
|
-
def supports_transaction_isolation?
|
280
|
+
def supports_transaction_isolation? # :nodoc:
|
240
281
|
true
|
241
282
|
end
|
242
283
|
|
@@ -244,8 +285,12 @@ module ActiveRecord
|
|
244
285
|
true
|
245
286
|
end
|
246
287
|
|
247
|
-
def
|
248
|
-
|
288
|
+
def supports_optimizer_hints?
|
289
|
+
true
|
290
|
+
end
|
291
|
+
|
292
|
+
def supports_common_table_expressions?
|
293
|
+
true
|
249
294
|
end
|
250
295
|
|
251
296
|
def supports_views?
|
@@ -253,7 +298,7 @@ module ActiveRecord
|
|
253
298
|
end
|
254
299
|
|
255
300
|
def supports_fetch_first_n_rows_and_offset?
|
256
|
-
if !use_old_oracle_visitor &&
|
301
|
+
if !use_old_oracle_visitor && database_version.first >= 12
|
257
302
|
true
|
258
303
|
else
|
259
304
|
false
|
@@ -269,11 +314,11 @@ module ActiveRecord
|
|
269
314
|
end
|
270
315
|
|
271
316
|
def supports_multi_insert?
|
272
|
-
|
317
|
+
database_version.to_s >= [11, 2].to_s
|
273
318
|
end
|
274
319
|
|
275
320
|
def supports_virtual_columns?
|
276
|
-
|
321
|
+
database_version.first >= 11
|
277
322
|
end
|
278
323
|
|
279
324
|
def supports_json?
|
@@ -307,12 +352,19 @@ module ActiveRecord
|
|
307
352
|
false
|
308
353
|
end
|
309
354
|
|
310
|
-
|
355
|
+
def supports_longer_identifier?
|
356
|
+
if !use_shorter_identifier && database_version.to_s >= [12, 2].to_s
|
357
|
+
true
|
358
|
+
else
|
359
|
+
false
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# :stopdoc:
|
311
364
|
DEFAULT_NLS_PARAMETERS = {
|
312
365
|
nls_calendar: nil,
|
313
366
|
nls_comp: nil,
|
314
367
|
nls_currency: nil,
|
315
|
-
nls_date_format: "YYYY-MM-DD HH24:MI:SS",
|
316
368
|
nls_date_language: nil,
|
317
369
|
nls_dual_currency: nil,
|
318
370
|
nls_iso_currency: nil,
|
@@ -322,13 +374,18 @@ module ActiveRecord
|
|
322
374
|
nls_numeric_characters: nil,
|
323
375
|
nls_sort: nil,
|
324
376
|
nls_territory: nil,
|
325
|
-
nls_timestamp_format: "YYYY-MM-DD HH24:MI:SS:FF6",
|
326
377
|
nls_timestamp_tz_format: nil,
|
327
378
|
nls_time_format: nil,
|
328
379
|
nls_time_tz_format: nil
|
329
380
|
}
|
330
381
|
|
331
|
-
|
382
|
+
# :stopdoc:
|
383
|
+
FIXED_NLS_PARAMETERS = {
|
384
|
+
nls_date_format: "YYYY-MM-DD HH24:MI:SS",
|
385
|
+
nls_timestamp_format: "YYYY-MM-DD HH24:MI:SS:FF6"
|
386
|
+
}
|
387
|
+
|
388
|
+
# :stopdoc:
|
332
389
|
NATIVE_DATABASE_TYPES = {
|
333
390
|
primary_key: "NUMBER(38) NOT NULL PRIMARY KEY",
|
334
391
|
string: { name: "VARCHAR2", limit: 255 },
|
@@ -336,7 +393,7 @@ module ActiveRecord
|
|
336
393
|
ntext: { name: "NCLOB" },
|
337
394
|
integer: { name: "NUMBER", limit: 38 },
|
338
395
|
float: { name: "BINARY_FLOAT" },
|
339
|
-
decimal: { name: "
|
396
|
+
decimal: { name: "NUMBER" },
|
340
397
|
datetime: { name: "TIMESTAMP" },
|
341
398
|
timestamp: { name: "TIMESTAMP" },
|
342
399
|
timestamptz: { name: "TIMESTAMP WITH TIME ZONE" },
|
@@ -352,9 +409,9 @@ module ActiveRecord
|
|
352
409
|
NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS = NATIVE_DATABASE_TYPES.dup.merge(
|
353
410
|
boolean: { name: "VARCHAR2", limit: 1 }
|
354
411
|
)
|
355
|
-
|
412
|
+
# :startdoc:
|
356
413
|
|
357
|
-
def native_database_types
|
414
|
+
def native_database_types # :nodoc:
|
358
415
|
emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES
|
359
416
|
end
|
360
417
|
|
@@ -364,10 +421,10 @@ module ActiveRecord
|
|
364
421
|
# If SQL statement fails due to lost connection then reconnect
|
365
422
|
# and retry SQL statement if autocommit mode is enabled.
|
366
423
|
# By default this functionality is disabled.
|
367
|
-
attr_reader :auto_retry
|
424
|
+
attr_reader :auto_retry # :nodoc:
|
368
425
|
@auto_retry = false
|
369
426
|
|
370
|
-
def auto_retry=(value)
|
427
|
+
def auto_retry=(value) # :nodoc:
|
371
428
|
@auto_retry = value
|
372
429
|
@connection.auto_retry = value if @connection
|
373
430
|
end
|
@@ -378,7 +435,7 @@ module ActiveRecord
|
|
378
435
|
end
|
379
436
|
|
380
437
|
# Returns true if the connection is active.
|
381
|
-
def active?
|
438
|
+
def active? # :nodoc:
|
382
439
|
# Pings the connection to check if it's still good. Note that an
|
383
440
|
# #active? method is also available, but that simply returns the
|
384
441
|
# last known state, which isn't good enough if the connection has
|
@@ -389,7 +446,7 @@ module ActiveRecord
|
|
389
446
|
end
|
390
447
|
|
391
448
|
# Reconnects to the database.
|
392
|
-
def reconnect!
|
449
|
+
def reconnect! # :nodoc:
|
393
450
|
super
|
394
451
|
@connection.reset!
|
395
452
|
rescue OracleEnhanced::ConnectionException => e
|
@@ -402,26 +459,27 @@ module ActiveRecord
|
|
402
459
|
end
|
403
460
|
|
404
461
|
# Disconnects from the database.
|
405
|
-
def disconnect!
|
462
|
+
def disconnect! # :nodoc:
|
406
463
|
super
|
407
464
|
@connection.logoff rescue nil
|
408
465
|
end
|
409
466
|
|
410
467
|
def discard!
|
468
|
+
super
|
411
469
|
@connection = nil
|
412
470
|
end
|
413
471
|
|
414
472
|
# use in set_sequence_name to avoid fetching primary key value from sequence
|
415
|
-
AUTOGENERATED_SEQUENCE_NAME = "autogenerated"
|
473
|
+
AUTOGENERATED_SEQUENCE_NAME = "autogenerated"
|
416
474
|
|
417
475
|
# Returns the next sequence value from a sequence generator. Not generally
|
418
476
|
# called directly; used by ActiveRecord to get the next primary key value
|
419
477
|
# when inserting a new database record (see #prefetch_primary_key?).
|
420
478
|
def next_sequence_value(sequence_name)
|
421
479
|
# if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
|
422
|
-
|
480
|
+
raise ArgumentError.new "Trigger based primary key is not supported" if sequence_name == AUTOGENERATED_SEQUENCE_NAME
|
423
481
|
# call directly connection method to avoid prepared statement which causes fetching of next sequence value twice
|
424
|
-
select_value(
|
482
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
425
483
|
SELECT #{quote_table_name(sequence_name)}.NEXTVAL FROM dual
|
426
484
|
SQL
|
427
485
|
end
|
@@ -433,13 +491,13 @@ module ActiveRecord
|
|
433
491
|
table_name = table_name.to_s
|
434
492
|
do_not_prefetch = @do_not_prefetch_primary_key[table_name]
|
435
493
|
if do_not_prefetch.nil?
|
436
|
-
owner, desc_table_name
|
437
|
-
@do_not_prefetch_primary_key
|
494
|
+
owner, desc_table_name = @connection.describe(table_name)
|
495
|
+
@do_not_prefetch_primary_key[table_name] = do_not_prefetch = !has_primary_key?(table_name, owner, desc_table_name)
|
438
496
|
end
|
439
497
|
!do_not_prefetch
|
440
498
|
end
|
441
499
|
|
442
|
-
def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil)
|
500
|
+
def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil) # :nodoc:
|
443
501
|
return nil unless data_source_exists?(table_name)
|
444
502
|
unless primary_key && sequence_name
|
445
503
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
@@ -459,7 +517,7 @@ module ActiveRecord
|
|
459
517
|
end
|
460
518
|
|
461
519
|
if primary_key && sequence_name
|
462
|
-
new_start_value = select_value(
|
520
|
+
new_start_value = select_value(<<~SQL.squish, "SCHEMA")
|
463
521
|
select NVL(max(#{quote_column_name(primary_key)}),0) + 1 from #{quote_table_name(table_name)}
|
464
522
|
SQL
|
465
523
|
|
@@ -470,59 +528,42 @@ module ActiveRecord
|
|
470
528
|
|
471
529
|
# Current database name
|
472
530
|
def current_database
|
473
|
-
select_value(
|
531
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
474
532
|
SELECT SYS_CONTEXT('userenv', 'con_name') FROM dual
|
475
533
|
SQL
|
476
534
|
rescue ActiveRecord::StatementInvalid
|
477
|
-
select_value(
|
535
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
478
536
|
SELECT SYS_CONTEXT('userenv', 'db_name') FROM dual
|
479
537
|
SQL
|
480
538
|
end
|
481
539
|
|
482
540
|
# Current database session user
|
483
541
|
def current_user
|
484
|
-
select_value(
|
542
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
485
543
|
SELECT SYS_CONTEXT('userenv', 'session_user') FROM dual
|
486
544
|
SQL
|
487
545
|
end
|
488
546
|
|
489
547
|
# Current database session schema
|
490
548
|
def current_schema
|
491
|
-
select_value(
|
549
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
492
550
|
SELECT SYS_CONTEXT('userenv', 'current_schema') FROM dual
|
493
551
|
SQL
|
494
552
|
end
|
495
553
|
|
496
554
|
# Default tablespace name of current user
|
497
555
|
def default_tablespace
|
498
|
-
select_value(
|
499
|
-
SELECT LOWER(default_tablespace) FROM user_users
|
556
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
557
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ LOWER(default_tablespace) FROM user_users
|
500
558
|
WHERE username = SYS_CONTEXT('userenv', 'current_schema')
|
501
559
|
SQL
|
502
560
|
end
|
503
561
|
|
504
|
-
# check if table has primary key trigger with _pkt suffix
|
505
|
-
def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
506
|
-
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
507
|
-
|
508
|
-
trigger_name = default_trigger_name(table_name).upcase
|
509
|
-
|
510
|
-
!!select_value(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key Trigger", [bind_string("owner", owner), bind_string("trigger_name", trigger_name), bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
511
|
-
SELECT trigger_name
|
512
|
-
FROM all_triggers#{db_link}
|
513
|
-
WHERE owner = :owner
|
514
|
-
AND trigger_name = :trigger_name
|
515
|
-
AND table_owner = :owner
|
516
|
-
AND table_name = :table_name
|
517
|
-
AND status = 'ENABLED'
|
518
|
-
SQL
|
519
|
-
end
|
520
|
-
|
521
562
|
def column_definitions(table_name)
|
522
|
-
(owner, desc_table_name
|
563
|
+
(owner, desc_table_name) = @connection.describe(table_name)
|
523
564
|
|
524
|
-
select_all(
|
525
|
-
SELECT cols.column_name AS name, cols.data_type AS sql_type,
|
565
|
+
select_all(<<~SQL.squish, "SCHEMA", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
566
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cols.column_name AS name, cols.data_type AS sql_type,
|
526
567
|
cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
|
527
568
|
cols.data_type_owner AS sql_type_owner,
|
528
569
|
DECODE(cols.data_type, 'NUMBER', data_precision,
|
@@ -533,9 +574,9 @@ module ActiveRecord
|
|
533
574
|
NULL) AS limit,
|
534
575
|
DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale,
|
535
576
|
comments.comments as column_comment
|
536
|
-
FROM all_tab_cols
|
537
|
-
WHERE cols.owner =
|
538
|
-
AND cols.table_name =
|
577
|
+
FROM all_tab_cols cols, all_col_comments comments
|
578
|
+
WHERE cols.owner = :owner
|
579
|
+
AND cols.table_name = :table_name
|
539
580
|
AND cols.hidden_column = 'NO'
|
540
581
|
AND cols.owner = comments.owner
|
541
582
|
AND cols.table_name = comments.table_name
|
@@ -550,28 +591,28 @@ module ActiveRecord
|
|
550
591
|
|
551
592
|
# Find a table's primary key and sequence.
|
552
593
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
553
|
-
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil
|
554
|
-
(owner, desc_table_name
|
555
|
-
|
556
|
-
seqs =
|
557
|
-
select us.sequence_name
|
558
|
-
from all_sequences
|
559
|
-
where us.sequence_owner =
|
560
|
-
and us.sequence_name = upper(
|
594
|
+
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil) # :nodoc:
|
595
|
+
(owner, desc_table_name) = @connection.describe(table_name)
|
596
|
+
|
597
|
+
seqs = select_values_forcing_binds(<<~SQL.squish, "SCHEMA", [bind_string("owner", owner), bind_string("sequence_name", default_sequence_name(desc_table_name))])
|
598
|
+
select /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ us.sequence_name
|
599
|
+
from all_sequences us
|
600
|
+
where us.sequence_owner = :owner
|
601
|
+
and us.sequence_name = upper(:sequence_name)
|
561
602
|
SQL
|
562
603
|
|
563
604
|
# changed back from user_constraints to all_constraints for consistency
|
564
|
-
pks =
|
565
|
-
SELECT cc.column_name
|
566
|
-
FROM all_constraints
|
567
|
-
WHERE c.owner =
|
568
|
-
AND c.table_name =
|
605
|
+
pks = select_values_forcing_binds(<<~SQL.squish, "SCHEMA", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
606
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cc.column_name
|
607
|
+
FROM all_constraints c, all_cons_columns cc
|
608
|
+
WHERE c.owner = :owner
|
609
|
+
AND c.table_name = :table_name
|
569
610
|
AND c.constraint_type = 'P'
|
570
611
|
AND cc.owner = c.owner
|
571
612
|
AND cc.constraint_name = c.constraint_name
|
572
613
|
SQL
|
573
614
|
|
574
|
-
warn
|
615
|
+
warn <<~WARNING if pks.count > 1
|
575
616
|
WARNING: Active Record does not support composite primary key.
|
576
617
|
|
577
618
|
#{table_name} has composite primary key. Composite primary key is ignored.
|
@@ -588,17 +629,17 @@ module ActiveRecord
|
|
588
629
|
pk_and_sequence && pk_and_sequence.first
|
589
630
|
end
|
590
631
|
|
591
|
-
def has_primary_key?(table_name, owner = nil, desc_table_name = nil
|
592
|
-
!pk_and_sequence_for(table_name, owner, desc_table_name
|
632
|
+
def has_primary_key?(table_name, owner = nil, desc_table_name = nil) # :nodoc:
|
633
|
+
!pk_and_sequence_for(table_name, owner, desc_table_name).nil?
|
593
634
|
end
|
594
635
|
|
595
636
|
def primary_keys(table_name) # :nodoc:
|
596
|
-
(
|
637
|
+
(_owner, desc_table_name) = @connection.describe(table_name)
|
597
638
|
|
598
|
-
pks =
|
599
|
-
SELECT cc.column_name
|
600
|
-
FROM all_constraints
|
601
|
-
WHERE c.owner =
|
639
|
+
pks = select_values_forcing_binds(<<~SQL.squish, "SCHEMA", [bind_string("table_name", desc_table_name)])
|
640
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cc.column_name
|
641
|
+
FROM all_constraints c, all_cons_columns cc
|
642
|
+
WHERE c.owner = SYS_CONTEXT('userenv', 'current_schema')
|
602
643
|
AND c.table_name = :table_name
|
603
644
|
AND c.constraint_type = 'P'
|
604
645
|
AND cc.owner = c.owner
|
@@ -608,103 +649,147 @@ module ActiveRecord
|
|
608
649
|
pks.map { |pk| oracle_downcase(pk) }
|
609
650
|
end
|
610
651
|
|
611
|
-
def columns_for_distinct(columns, orders)
|
652
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
612
653
|
# construct a valid columns name for DISTINCT clause,
|
613
654
|
# ie. one that includes the ORDER BY columns, using FIRST_VALUE such that
|
614
655
|
# the inclusion of these columns doesn't invalidate the DISTINCT
|
615
656
|
#
|
616
657
|
# It does not construct DISTINCT clause. Just return column names for distinct.
|
617
658
|
order_columns = orders.reject(&:blank?).map { |s|
|
618
|
-
s = s
|
659
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
619
660
|
# remove any ASC/DESC modifiers
|
620
661
|
s.gsub(/\s+(ASC|DESC)\s*?/i, "")
|
621
662
|
}.reject(&:blank?).map.with_index { |column, i|
|
622
663
|
"FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__"
|
623
664
|
}
|
624
|
-
|
665
|
+
(order_columns << super).join(", ")
|
625
666
|
end
|
626
667
|
|
627
|
-
def temporary_table?(table_name)
|
628
|
-
|
629
|
-
SELECT
|
668
|
+
def temporary_table?(table_name) # :nodoc:
|
669
|
+
select_value_forcing_binds(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name.upcase)]) == "Y"
|
670
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
|
671
|
+
temporary FROM all_tables WHERE table_name = :table_name and owner = SYS_CONTEXT('userenv', 'current_schema')
|
630
672
|
SQL
|
631
673
|
end
|
632
674
|
|
633
|
-
|
675
|
+
def max_identifier_length
|
676
|
+
supports_longer_identifier? ? 128 : 30
|
677
|
+
end
|
678
|
+
alias table_alias_length max_identifier_length
|
679
|
+
alias index_name_length max_identifier_length
|
634
680
|
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
register_class_with_precision m, %r(WITH TIME ZONE)i, Type::OracleEnhanced::TimestampTz
|
639
|
-
register_class_with_precision m, %r(WITH LOCAL TIME ZONE)i, Type::OracleEnhanced::TimestampLtz
|
640
|
-
register_class_with_limit m, %r(raw)i, Type::OracleEnhanced::Raw
|
641
|
-
register_class_with_limit m, %r(char)i, Type::OracleEnhanced::String
|
642
|
-
register_class_with_limit m, %r(clob)i, Type::OracleEnhanced::Text
|
643
|
-
register_class_with_limit m, %r(nclob)i, Type::OracleEnhanced::NationalCharacterText
|
644
|
-
|
645
|
-
m.register_type "NCHAR", Type::OracleEnhanced::NationalCharacterString.new
|
646
|
-
m.alias_type %r(NVARCHAR2)i, "NCHAR"
|
647
|
-
|
648
|
-
m.register_type(%r(NUMBER)i) do |sql_type|
|
649
|
-
scale = extract_scale(sql_type)
|
650
|
-
precision = extract_precision(sql_type)
|
651
|
-
limit = extract_limit(sql_type)
|
652
|
-
if scale == 0
|
653
|
-
Type::OracleEnhanced::Integer.new(precision: precision, limit: limit)
|
654
|
-
else
|
655
|
-
Type::Decimal.new(precision: precision, scale: scale)
|
656
|
-
end
|
657
|
-
end
|
681
|
+
def get_database_version
|
682
|
+
@connection.database_version
|
683
|
+
end
|
658
684
|
|
659
|
-
|
660
|
-
|
661
|
-
end
|
662
|
-
end
|
685
|
+
def check_version
|
686
|
+
version = get_database_version.join(".").to_f
|
663
687
|
|
664
|
-
|
665
|
-
|
666
|
-
when String
|
667
|
-
default.gsub("''", "'")
|
668
|
-
else
|
669
|
-
default
|
670
|
-
end
|
688
|
+
if version < 10
|
689
|
+
raise "Your version of Oracle (#{version}) is too old. Active Record Oracle enhanced adapter supports Oracle >= 10g."
|
671
690
|
end
|
691
|
+
end
|
672
692
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
693
|
+
class << self
|
694
|
+
private
|
695
|
+
def initialize_type_map(m)
|
696
|
+
super
|
697
|
+
# oracle
|
698
|
+
register_class_with_precision m, %r(WITH TIME ZONE)i, Type::OracleEnhanced::TimestampTz
|
699
|
+
register_class_with_precision m, %r(WITH LOCAL TIME ZONE)i, Type::OracleEnhanced::TimestampLtz
|
700
|
+
register_class_with_limit m, %r(raw)i, Type::OracleEnhanced::Raw
|
701
|
+
register_class_with_limit m, %r{^(char)}i, Type::OracleEnhanced::CharacterString
|
702
|
+
register_class_with_limit m, %r{^(nchar)}i, Type::OracleEnhanced::String
|
703
|
+
register_class_with_limit m, %r(varchar)i, Type::OracleEnhanced::String
|
704
|
+
register_class_with_limit m, %r(clob)i, Type::OracleEnhanced::Text
|
705
|
+
register_class_with_limit m, %r(nclob)i, Type::OracleEnhanced::NationalCharacterText
|
706
|
+
|
707
|
+
m.register_type "NCHAR", Type::OracleEnhanced::NationalCharacterString.new
|
708
|
+
m.alias_type %r(NVARCHAR2)i, "NCHAR"
|
709
|
+
|
710
|
+
m.register_type(%r(NUMBER)i) do |sql_type|
|
711
|
+
scale = extract_scale(sql_type)
|
712
|
+
precision = extract_precision(sql_type)
|
713
|
+
limit = extract_limit(sql_type)
|
714
|
+
if scale == 0
|
715
|
+
Type::OracleEnhanced::Integer.new(precision: precision, limit: limit)
|
716
|
+
else
|
717
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
if OracleEnhancedAdapter.emulate_booleans
|
722
|
+
m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
|
723
|
+
end
|
679
724
|
end
|
725
|
+
end
|
726
|
+
|
727
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
728
|
+
|
729
|
+
def type_map
|
730
|
+
TYPE_MAP
|
731
|
+
end
|
732
|
+
|
733
|
+
def extract_value_from_default(default)
|
734
|
+
case default
|
735
|
+
when String
|
736
|
+
default.gsub("''", "'")
|
737
|
+
else
|
738
|
+
default
|
680
739
|
end
|
740
|
+
end
|
681
741
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
when 900, 904, 942, 955, 1418, 2289, 2449, 17008
|
689
|
-
ActiveRecord::StatementInvalid.new(message)
|
690
|
-
when 1400
|
691
|
-
ActiveRecord::NotNullViolation.new(message)
|
692
|
-
when 2291
|
693
|
-
InvalidForeignKey.new(message)
|
694
|
-
when 12899
|
695
|
-
ValueTooLong.new(message)
|
696
|
-
else
|
697
|
-
super
|
698
|
-
end
|
742
|
+
def extract_limit(sql_type) # :nodoc:
|
743
|
+
case sql_type
|
744
|
+
when /^bigint/i
|
745
|
+
19
|
746
|
+
when /\((.*)\)/
|
747
|
+
$1.to_i
|
699
748
|
end
|
749
|
+
end
|
700
750
|
|
701
|
-
|
702
|
-
|
703
|
-
|
751
|
+
def translate_exception(exception, message:, sql:, binds:) # :nodoc:
|
752
|
+
case @connection.error_code(exception)
|
753
|
+
when 1
|
754
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
755
|
+
when 60
|
756
|
+
Deadlocked.new(message)
|
757
|
+
when 900, 904, 942, 955, 1418, 2289, 2449, 17008
|
758
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
759
|
+
when 1400
|
760
|
+
ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
761
|
+
when 2291, 2292
|
762
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
763
|
+
when 12899
|
764
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
765
|
+
else
|
766
|
+
super
|
704
767
|
end
|
768
|
+
end
|
769
|
+
|
770
|
+
# create bind object for type String
|
771
|
+
def bind_string(name, value)
|
772
|
+
ActiveRecord::Relation::QueryAttribute.new(name, value, Type::OracleEnhanced::String.new)
|
773
|
+
end
|
774
|
+
|
775
|
+
# call select_values using binds even if surrounding SQL preparation/execution is done + # with conn.unprepared_statement (like AR.to_sql)
|
776
|
+
def select_values_forcing_binds(arel, name, binds)
|
777
|
+
# remove possible force of unprepared SQL during dictionary access
|
778
|
+
unprepared_statement_forced = prepared_statements_disabled_cache.include?(object_id)
|
779
|
+
prepared_statements_disabled_cache.delete(object_id) if unprepared_statement_forced
|
780
|
+
|
781
|
+
select_values(arel, name, binds)
|
782
|
+
ensure
|
783
|
+
# Restore unprepared_statement setting for surrounding SQL
|
784
|
+
prepared_statements_disabled_cache.add(object_id) if unprepared_statement_forced
|
785
|
+
end
|
786
|
+
|
787
|
+
def select_value_forcing_binds(arel, name, binds)
|
788
|
+
single_value_from_rows(select_values_forcing_binds(arel, name, binds))
|
789
|
+
end
|
705
790
|
|
706
|
-
|
707
|
-
|
791
|
+
ActiveRecord::Type.register(:boolean, Type::OracleEnhanced::Boolean, adapter: :oracle_enhanced)
|
792
|
+
ActiveRecord::Type.register(:json, Type::OracleEnhanced::Json, adapter: :oracle_enhanced)
|
708
793
|
end
|
709
794
|
end
|
710
795
|
end
|
@@ -714,3 +799,18 @@ require "active_record/connection_adapters/oracle_enhanced/version"
|
|
714
799
|
module ActiveRecord
|
715
800
|
autoload :OracleEnhancedProcedures, "active_record/connection_adapters/oracle_enhanced/procedures"
|
716
801
|
end
|
802
|
+
|
803
|
+
# Workaround for https://github.com/jruby/jruby/issues/6267
|
804
|
+
if RUBY_ENGINE == "jruby"
|
805
|
+
require "jruby"
|
806
|
+
|
807
|
+
class org.jruby::RubyObjectSpace::WeakMap
|
808
|
+
field_reader :map
|
809
|
+
end
|
810
|
+
|
811
|
+
class ObjectSpace::WeakMap
|
812
|
+
def values
|
813
|
+
JRuby.ref(self).map.values.reject(&:nil?)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|