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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +390 -21
  3. data/README.md +35 -8
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
  24. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
  25. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
  26. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
  27. data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
  28. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  29. data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
  30. data/lib/arel/visitors/oracle.rb +221 -0
  31. data/lib/arel/visitors/oracle12.rb +128 -0
  32. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
  34. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
  35. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
  37. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
  38. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
  39. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
  40. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
  41. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
  42. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
  43. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
  44. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
  45. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
  46. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  47. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  48. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
  49. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
  50. data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
  51. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
  52. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
  53. data/spec/spec_config.yaml.template +2 -2
  54. data/spec/spec_helper.rb +13 -2
  55. metadata +52 -30
  56. 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 #:nodoc:
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) #:nodoc:
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 #:nodoc:
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 10000 if not explicitly set), e.g.:
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 = 1
205
+ # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 10000
202
206
  cattr_accessor :default_sequence_start_value
203
- self.default_sequence_start_value = 10000
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".freeze
250
+ ADAPTER_NAME = "OracleEnhanced"
222
251
 
223
- def adapter_name #:nodoc:
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 supports_savepoints? #:nodoc:
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? #:nodoc:
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 supports_foreign_keys_in_create?
248
- supports_foreign_keys?
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 && @connection.database_version.first >= 12
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
- @connection.database_version.to_s >= [11, 2].to_s
317
+ database_version.to_s >= [11, 2].to_s
273
318
  end
274
319
 
275
320
  def supports_virtual_columns?
276
- @connection.database_version.first >= 11
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
- #:stopdoc:
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
- #:stopdoc:
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: "DECIMAL" },
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
- #:startdoc:
412
+ # :startdoc:
356
413
 
357
- def native_database_types #:nodoc:
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 #:nodoc:
424
+ attr_reader :auto_retry # :nodoc:
368
425
  @auto_retry = false
369
426
 
370
- def auto_retry=(value) #:nodoc:
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? #:nodoc:
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! #:nodoc:
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! #:nodoc:
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".freeze
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
- return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
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(<<-SQL.strip.gsub(/\s+/, " "), "next sequence 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, db_link = @connection.describe(table_name)
437
- @do_not_prefetch_primary_key [table_name] = do_not_prefetch = !has_primary_key?(table_name, owner, desc_table_name, db_link) || has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
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) #:nodoc:
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(<<-SQL.strip.gsub(/\s+/, " "), "new start 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(<<-SQL.strip.gsub(/\s+/, " "), "current database on CDB ")
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(<<-SQL.strip.gsub(/\s+/, " "), "current database")
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(<<-SQL.strip.gsub(/\s+/, " "), "current user")
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(<<-SQL.strip.gsub(/\s+/, " "), "current schema")
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(<<-SQL.strip.gsub(/\s+/, " "), "default tablespace")
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, db_link) = @connection.describe(table_name)
563
+ (owner, desc_table_name) = @connection.describe(table_name)
523
564
 
524
- select_all(<<-SQL.strip.gsub(/\s+/, " "), "Column definitions")
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#{db_link} cols, all_col_comments#{db_link} comments
537
- WHERE cols.owner = '#{owner}'
538
- AND cols.table_name = #{quote(desc_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, db_link = nil) #:nodoc:
554
- (owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
555
-
556
- seqs = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Sequence")
557
- select us.sequence_name
558
- from all_sequences#{db_link} us
559
- where us.sequence_owner = '#{owner}'
560
- and us.sequence_name = upper(#{quote(default_sequence_name(desc_table_name))})
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 = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key")
565
- SELECT cc.column_name
566
- FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
567
- WHERE c.owner = '#{owner}'
568
- AND c.table_name = #{quote(desc_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 <<-WARNING.strip_heredoc if pks.count > 1
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, db_link = nil) #:nodoc:
592
- !pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
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
- (owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
637
+ (_owner, desc_table_name) = @connection.describe(table_name)
597
638
 
598
- pks = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Primary Keys", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
599
- SELECT cc.column_name
600
- FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
601
- WHERE c.owner = :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) #:nodoc:
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.to_sql unless s.is_a?(String)
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
- [super, *order_columns].join(", ")
665
+ (order_columns << super).join(", ")
625
666
  end
626
667
 
627
- def temporary_table?(table_name) #:nodoc:
628
- select_value(<<-SQL.strip.gsub(/\s+/, " "), "temporary table", [bind_string("table_name", table_name.upcase)]) == "Y"
629
- SELECT temporary FROM all_tables WHERE table_name = :table_name and owner = SYS_CONTEXT('userenv', 'session_user')
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
- private
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
- def initialize_type_map(m = type_map)
636
- super
637
- # oracle
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
- if OracleEnhancedAdapter.emulate_booleans
660
- m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
661
- end
662
- end
685
+ def check_version
686
+ version = get_database_version.join(".").to_f
663
687
 
664
- def extract_value_from_default(default)
665
- case default
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
- def extract_limit(sql_type) #:nodoc:
674
- case sql_type
675
- when /^bigint/i
676
- 19
677
- when /\((.*)\)/
678
- $1.to_i
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
- def translate_exception(exception, message) #:nodoc:
683
- case @connection.error_code(exception)
684
- when 1
685
- RecordNotUnique.new(message)
686
- when 60
687
- Deadlocked.new(message)
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
- # create bind object for type String
702
- def bind_string(name, value)
703
- ActiveRecord::Relation::QueryAttribute.new(name, value, Type::OracleEnhanced::String.new)
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
- ActiveRecord::Type.register(:boolean, Type::OracleEnhanced::Boolean, adapter: :oracleenhanced)
707
- ActiveRecord::Type.register(:json, Type::OracleEnhanced::Json, adapter: :oracleenhanced)
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