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.
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