activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|