activerecord-oracle_enhanced-adapter 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +112 -0
  3. data/README.md +12 -1
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +3 -4
  6. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +0 -1
  7. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +0 -9
  8. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +64 -47
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +4 -5
  10. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +0 -1
  11. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +3 -4
  12. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +1 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +12 -7
  14. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +0 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +38 -3
  16. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +3 -4
  17. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +16 -4
  19. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +65 -59
  20. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +35 -34
  21. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +2 -1
  22. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +64 -21
  23. data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
  24. data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
  25. data/lib/arel/visitors/oracle.rb +217 -0
  26. data/lib/arel/visitors/oracle12.rb +124 -0
  27. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +35 -3
  28. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +6 -1
  29. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +0 -1
  30. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +28 -1
  31. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +2 -1
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +122 -0
  33. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +4 -2
  34. data/spec/spec_config.yaml.template +2 -2
  35. data/spec/spec_helper.rb +13 -4
  36. metadata +28 -26
@@ -8,8 +8,9 @@ module ActiveRecord #:nodoc:
8
8
  STATEMENT_TOKEN = "\n\n/\n\n"
9
9
 
10
10
  def structure_dump #:nodoc:
11
- sequences = select(<<~SQL.squish, "sequences to dump at structure dump")
12
- SELECT sequence_name, min_value, max_value, increment_by, order_flag, cycle_flag
11
+ sequences = select(<<~SQL.squish, "SCHEMA")
12
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
13
+ sequence_name, min_value, max_value, increment_by, order_flag, cycle_flag
13
14
  FROM all_sequences
14
15
  where sequence_owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
15
16
  SQL
@@ -17,8 +18,8 @@ module ActiveRecord #:nodoc:
17
18
  structure = sequences.map do |result|
18
19
  "CREATE SEQUENCE #{quote_table_name(result["sequence_name"])} MINVALUE #{result["min_value"]} MAXVALUE #{result["max_value"]} INCREMENT BY #{result["increment_by"]} #{result["order_flag"] == 'Y' ? "ORDER" : "NOORDER"} #{result["cycle_flag"] == 'Y' ? "CYCLE" : "NOCYCLE"}"
19
20
  end
20
- tables = select_values(<<~SQL.squish, "tables at structure dump")
21
- SELECT table_name FROM all_tables t
21
+ tables = select_values(<<~SQL.squish, "SCHEMA")
22
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ table_name FROM all_tables t
22
23
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
23
24
  AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
24
25
  WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
@@ -29,8 +30,8 @@ module ActiveRecord #:nodoc:
29
30
  tables.each do |table_name|
30
31
  virtual_columns = virtual_columns_for(table_name) if supports_virtual_columns?
31
32
  ddl = +"CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
32
- columns = select_all(<<~SQL.squish, "columns at structure dump")
33
- SELECT column_name, data_type, data_length, char_used, char_length,
33
+ columns = select_all(<<~SQL.squish, "SCHEMA")
34
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ column_name, data_type, data_length, char_used, char_length,
34
35
  data_precision, data_scale, data_default, nullable
35
36
  FROM all_tab_columns
36
37
  WHERE table_name = '#{table_name}'
@@ -89,8 +90,8 @@ module ActiveRecord #:nodoc:
89
90
 
90
91
  def structure_dump_primary_key(table) #:nodoc:
91
92
  opts = { name: "", cols: [] }
92
- pks = select_all(<<~SQL.squish, "Primary Keys")
93
- SELECT a.constraint_name, a.column_name, a.position
93
+ pks = select_all(<<~SQL.squish, "SCHEMA")
94
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ a.constraint_name, a.column_name, a.position
94
95
  FROM all_cons_columns a
95
96
  JOIN all_constraints c
96
97
  ON a.constraint_name = c.constraint_name
@@ -108,8 +109,8 @@ module ActiveRecord #:nodoc:
108
109
 
109
110
  def structure_dump_unique_keys(table) #:nodoc:
110
111
  keys = {}
111
- uks = select_all(<<~SQL.squish, "Primary Keys")
112
- SELECT a.constraint_name, a.column_name, a.position
112
+ uks = select_all(<<~SQL.squish, "SCHEMA")
113
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ a.constraint_name, a.column_name, a.position
113
114
  FROM all_cons_columns a
114
115
  JOIN all_constraints c
115
116
  ON a.constraint_name = c.constraint_name
@@ -144,8 +145,8 @@ module ActiveRecord #:nodoc:
144
145
  end
145
146
 
146
147
  def structure_dump_fk_constraints #:nodoc:
147
- foreign_keys = select_all(<<~SQL.squish, "foreign keys at structure dump")
148
- SELECT table_name FROM all_tables
148
+ foreign_keys = select_all(<<~SQL.squish, "SCHEMA")
149
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ table_name FROM all_tables
149
150
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
150
151
  SQL
151
152
  fks = foreign_keys.map do |table|
@@ -172,8 +173,8 @@ module ActiveRecord #:nodoc:
172
173
 
173
174
  def structure_dump_column_comments(table_name)
174
175
  comments = []
175
- columns = select_values(<<~SQL.squish, "column comments at structure dump")
176
- SELECT column_name FROM user_tab_columns
176
+ columns = select_values(<<~SQL.squish, "SCHEMA")
177
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ column_name FROM user_tab_columns
177
178
  WHERE table_name = '#{table_name}' ORDER BY column_id
178
179
  SQL
179
180
 
@@ -206,8 +207,8 @@ module ActiveRecord #:nodoc:
206
207
  # Extract all stored procedures, packages, synonyms.
207
208
  def structure_dump_db_stored_code #:nodoc:
208
209
  structure = []
209
- all_source = select_all(<<~SQL.squish, "stored program at structure dump")
210
- SELECT DISTINCT name, type
210
+ all_source = select_all(<<~SQL.squish, "SCHEMA")
211
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ DISTINCT name, type
211
212
  FROM all_source
212
213
  WHERE type IN ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
213
214
  AND name NOT LIKE 'BIN$%'
@@ -216,7 +217,7 @@ module ActiveRecord #:nodoc:
216
217
  all_source.each do |source|
217
218
  ddl = +"CREATE OR REPLACE \n"
218
219
  texts = select_all(<<~SQL.squish, "all source at structure dump")
219
- SELECT text
220
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ text
220
221
  FROM all_source
221
222
  WHERE name = '#{source['name']}'
222
223
  AND type = '#{source['type']}'
@@ -238,8 +239,8 @@ module ActiveRecord #:nodoc:
238
239
 
239
240
  def structure_dump_views #:nodoc:
240
241
  structure = []
241
- views = select_all(<<~SQL.squish, "views at structure dump")
242
- SELECT view_name, text FROM all_views
242
+ views = select_all(<<~SQL.squish, "SCHEMA")
243
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ view_name, text FROM all_views
243
244
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY view_name ASC
244
245
  SQL
245
246
  views.each do |view|
@@ -250,8 +251,8 @@ module ActiveRecord #:nodoc:
250
251
 
251
252
  def structure_dump_synonyms #:nodoc:
252
253
  structure = []
253
- synonyms = select_all(<<~SQL.squish, "synonyms at structure dump")
254
- SELECT owner, synonym_name, table_name, table_owner
254
+ synonyms = select_all(<<~SQL.squish, "SCHEMA")
255
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ owner, synonym_name, table_name, table_owner
255
256
  FROM all_synonyms
256
257
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
257
258
  SQL
@@ -263,14 +264,15 @@ module ActiveRecord #:nodoc:
263
264
  end
264
265
 
265
266
  def structure_drop #:nodoc:
266
- sequences = select_values(<<~SQL.squish, "sequences to drop at structure dump")
267
- SELECT sequence_name FROM all_sequences where sequence_owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
267
+ sequences = select_values(<<~SQL.squish, "SCHEMA")
268
+ SELECT/*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
269
+ sequence_name FROM all_sequences where sequence_owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
268
270
  SQL
269
271
  statements = sequences.map do |seq|
270
272
  "DROP SEQUENCE \"#{seq}\""
271
273
  end
272
- tables = select_values(<<~SQL.squish, "tables to drop at structure dump")
273
- SELECT table_name from all_tables t
274
+ tables = select_values(<<~SQL.squish, "SCHEMA")
275
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ table_name from all_tables t
274
276
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
275
277
  AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
276
278
  WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
@@ -285,8 +287,8 @@ module ActiveRecord #:nodoc:
285
287
  end
286
288
 
287
289
  def temp_table_drop #:nodoc:
288
- temporary_tables = select_values(<<~SQL.squish, "temporary tables to drop at structure dump")
289
- SELECT table_name FROM all_tables
290
+ temporary_tables = select_values(<<~SQL.squish, "SCHEMA")
291
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ table_name FROM all_tables
290
292
  WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
291
293
  AND secondary = 'N' AND temporary = 'Y' ORDER BY 1
292
294
  SQL
@@ -316,12 +318,11 @@ module ActiveRecord #:nodoc:
316
318
  end
317
319
 
318
320
  private
319
-
320
321
  # Called only if `supports_virtual_columns?` returns true
321
322
  # return [{'column_name' => 'FOOS', 'data_default' => '...'}, ...]
322
323
  def virtual_columns_for(table)
323
- select_all(<<~SQL.squish, "virtual columns for")
324
- SELECT column_name, data_default
324
+ select_all(<<~SQL.squish, "SCHEMA")
325
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ column_name, data_default
325
326
  FROM all_tab_cols
326
327
  WHERE virtual_column = 'YES'
327
328
  AND owner = SYS_CONTEXT('userenv', 'current_schema')
@@ -331,8 +332,8 @@ module ActiveRecord #:nodoc:
331
332
 
332
333
  def drop_sql_for_feature(type)
333
334
  short_type = type == "materialized view" ? "mview" : type
334
- features = select_values(<<~SQL.squish, "features to drop")
335
- SELECT #{short_type}_name FROM all_#{short_type.tableize}
335
+ features = select_values(<<~SQL.squish, "SCHEMA")
336
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ #{short_type}_name FROM all_#{short_type.tableize}
336
337
  where owner = SYS_CONTEXT('userenv', 'current_schema')
337
338
  SQL
338
339
  statements = features.map do |name|
@@ -342,8 +343,8 @@ module ActiveRecord #:nodoc:
342
343
  end
343
344
 
344
345
  def drop_sql_for_object(type)
345
- objects = select_values(<<~SQL.squish, "objects to drop")
346
- SELECT object_name FROM all_objects
346
+ objects = select_values(<<~SQL.squish, "SCHEMA")
347
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ object_name FROM all_objects
347
348
  WHERE object_type = '#{type.upcase}' and owner = SYS_CONTEXT('userenv', 'current_schema')
348
349
  SQL
349
350
  statements = objects.map do |name|
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  module ConnectionAdapters #:nodoc:
5
5
  module OracleEnhanced
6
6
  class TypeMetadata < DelegateClass(ActiveRecord::ConnectionAdapters::SqlTypeMetadata) # :nodoc:
7
+ include Deduplicable
8
+
7
9
  attr_reader :virtual
8
10
 
9
11
  def initialize(type_metadata, virtual: nil)
@@ -23,7 +25,6 @@ module ActiveRecord
23
25
  end
24
26
 
25
27
  protected
26
-
27
28
  def attributes_for_hash
28
29
  [self.class, @type_metadata, virtual]
29
30
  end
@@ -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"
@@ -213,13 +216,24 @@ module ActiveRecord
213
216
  cattr_accessor :use_shorter_identifier
214
217
  self.use_shorter_identifier = false
215
218
 
219
+ ##
220
+ # :singleton-method:
221
+ # By default, OracleEnhanced adapter will grant unlimited tablespace, create session, create table, create view,
222
+ # and create sequence when running the rake task db:create.
223
+ #
224
+ # If you wish to change these permissions you can add the following line to your initializer file:
225
+ #
226
+ # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.permissions =
227
+ # ["create session", "create table", "create view", "create sequence", "create trigger", "ctxapp"]
228
+ cattr_accessor :permissions
229
+ self.permissions = ["create session", "create table", "create view", "create sequence"]
230
+
216
231
  ##
217
232
  # :singleton-method:
218
233
  # Specify default sequence start with value (by default 1 if not explicitly set), e.g.:
219
234
 
220
235
  class StatementPool < ConnectionAdapters::StatementPool
221
236
  private
222
-
223
237
  def dealloc(stmt)
224
238
  stmt.close
225
239
  end
@@ -238,6 +252,14 @@ module ActiveRecord
238
252
  ADAPTER_NAME
239
253
  end
240
254
 
255
+ # Oracle enhanced adapter has no implementation because
256
+ # Oracle Database cannot detect `NoDatabaseError`.
257
+ # Please refer to the following discussion for details.
258
+ # https://github.com/rsim/oracle-enhanced/pull/1900
259
+ def self.database_exists?(config)
260
+ raise NotImplementedError
261
+ end
262
+
241
263
  def arel_visitor # :nodoc:
242
264
  if supports_fetch_first_n_rows_and_offset?
243
265
  Arel::Visitors::Oracle12.new(self)
@@ -266,6 +288,10 @@ module ActiveRecord
266
288
  true
267
289
  end
268
290
 
291
+ def supports_common_table_expressions?
292
+ true
293
+ end
294
+
269
295
  def supports_views?
270
296
  true
271
297
  end
@@ -438,6 +464,7 @@ module ActiveRecord
438
464
  end
439
465
 
440
466
  def discard!
467
+ super
441
468
  @connection = nil
442
469
  end
443
470
 
@@ -449,9 +476,9 @@ module ActiveRecord
449
476
  # when inserting a new database record (see #prefetch_primary_key?).
450
477
  def next_sequence_value(sequence_name)
451
478
  # if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
452
- raise ArgumentError "Trigger based primary key is not supported" if sequence_name == AUTOGENERATED_SEQUENCE_NAME
479
+ raise ArgumentError.new "Trigger based primary key is not supported" if sequence_name == AUTOGENERATED_SEQUENCE_NAME
453
480
  # call directly connection method to avoid prepared statement which causes fetching of next sequence value twice
454
- select_value(<<~SQL.squish, "next sequence value")
481
+ select_value(<<~SQL.squish, "SCHEMA")
455
482
  SELECT #{quote_table_name(sequence_name)}.NEXTVAL FROM dual
456
483
  SQL
457
484
  end
@@ -489,7 +516,7 @@ module ActiveRecord
489
516
  end
490
517
 
491
518
  if primary_key && sequence_name
492
- new_start_value = select_value(<<~SQL.squish, "new start value")
519
+ new_start_value = select_value(<<~SQL.squish, "SCHEMA")
493
520
  select NVL(max(#{quote_column_name(primary_key)}),0) + 1 from #{quote_table_name(table_name)}
494
521
  SQL
495
522
 
@@ -500,33 +527,33 @@ module ActiveRecord
500
527
 
501
528
  # Current database name
502
529
  def current_database
503
- select_value(<<~SQL.squish, "current database on CDB ")
530
+ select_value(<<~SQL.squish, "SCHEMA")
504
531
  SELECT SYS_CONTEXT('userenv', 'con_name') FROM dual
505
532
  SQL
506
533
  rescue ActiveRecord::StatementInvalid
507
- select_value(<<~SQL.squish, "current database")
534
+ select_value(<<~SQL.squish, "SCHEMA")
508
535
  SELECT SYS_CONTEXT('userenv', 'db_name') FROM dual
509
536
  SQL
510
537
  end
511
538
 
512
539
  # Current database session user
513
540
  def current_user
514
- select_value(<<~SQL.squish, "current user")
541
+ select_value(<<~SQL.squish, "SCHEMA")
515
542
  SELECT SYS_CONTEXT('userenv', 'session_user') FROM dual
516
543
  SQL
517
544
  end
518
545
 
519
546
  # Current database session schema
520
547
  def current_schema
521
- select_value(<<~SQL.squish, "current schema")
548
+ select_value(<<~SQL.squish, "SCHEMA")
522
549
  SELECT SYS_CONTEXT('userenv', 'current_schema') FROM dual
523
550
  SQL
524
551
  end
525
552
 
526
553
  # Default tablespace name of current user
527
554
  def default_tablespace
528
- select_value(<<~SQL.squish, "default tablespace")
529
- SELECT LOWER(default_tablespace) FROM user_users
555
+ select_value(<<~SQL.squish, "SCHEMA")
556
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ LOWER(default_tablespace) FROM user_users
530
557
  WHERE username = SYS_CONTEXT('userenv', 'current_schema')
531
558
  SQL
532
559
  end
@@ -534,8 +561,8 @@ module ActiveRecord
534
561
  def column_definitions(table_name)
535
562
  (owner, desc_table_name) = @connection.describe(table_name)
536
563
 
537
- select_all(<<~SQL.squish, "Column definitions")
538
- SELECT cols.column_name AS name, cols.data_type AS sql_type,
564
+ select_all(<<~SQL.squish, "SCHEMA")
565
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cols.column_name AS name, cols.data_type AS sql_type,
539
566
  cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
540
567
  cols.data_type_owner AS sql_type_owner,
541
568
  DECODE(cols.data_type, 'NUMBER', data_precision,
@@ -566,16 +593,16 @@ module ActiveRecord
566
593
  def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil) #:nodoc:
567
594
  (owner, desc_table_name) = @connection.describe(table_name)
568
595
 
569
- seqs = select_values(<<~SQL.squish, "Sequence")
570
- select us.sequence_name
596
+ seqs = select_values(<<~SQL.squish, "SCHEMA")
597
+ select /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ us.sequence_name
571
598
  from all_sequences us
572
599
  where us.sequence_owner = '#{owner}'
573
600
  and us.sequence_name = upper(#{quote(default_sequence_name(desc_table_name))})
574
601
  SQL
575
602
 
576
603
  # changed back from user_constraints to all_constraints for consistency
577
- pks = select_values(<<~SQL.squish, "Primary Key")
578
- SELECT cc.column_name
604
+ pks = select_values(<<~SQL.squish, "SCHEMA")
605
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cc.column_name
579
606
  FROM all_constraints c, all_cons_columns cc
580
607
  WHERE c.owner = '#{owner}'
581
608
  AND c.table_name = #{quote(desc_table_name)}
@@ -608,8 +635,8 @@ module ActiveRecord
608
635
  def primary_keys(table_name) # :nodoc:
609
636
  (_owner, desc_table_name) = @connection.describe(table_name)
610
637
 
611
- pks = select_values(<<~SQL.squish, "Primary Keys", [bind_string("table_name", desc_table_name)])
612
- SELECT cc.column_name
638
+ pks = select_values(<<~SQL.squish, "SCHEMA", [bind_string("table_name", desc_table_name)])
639
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ cc.column_name
613
640
  FROM all_constraints c, all_cons_columns cc
614
641
  WHERE c.owner = SYS_CONTEXT('userenv', 'current_schema')
615
642
  AND c.table_name = :table_name
@@ -628,7 +655,7 @@ module ActiveRecord
628
655
  #
629
656
  # It does not construct DISTINCT clause. Just return column names for distinct.
630
657
  order_columns = orders.reject(&:blank?).map { |s|
631
- s = s.to_sql unless s.is_a?(String)
658
+ s = visitor.compile(s) unless s.is_a?(String)
632
659
  # remove any ASC/DESC modifiers
633
660
  s.gsub(/\s+(ASC|DESC)\s*?/i, "")
634
661
  }.reject(&:blank?).map.with_index { |column, i|
@@ -638,8 +665,9 @@ module ActiveRecord
638
665
  end
639
666
 
640
667
  def temporary_table?(table_name) #:nodoc:
641
- select_value(<<~SQL.squish, "temporary table", [bind_string("table_name", table_name.upcase)]) == "Y"
642
- SELECT temporary FROM all_tables WHERE table_name = :table_name and owner = SYS_CONTEXT('userenv', 'current_schema')
668
+ select_value(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name.upcase)]) == "Y"
669
+ SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
670
+ temporary FROM all_tables WHERE table_name = :table_name and owner = SYS_CONTEXT('userenv', 'current_schema')
643
671
  SQL
644
672
  end
645
673
 
@@ -746,3 +774,18 @@ require "active_record/connection_adapters/oracle_enhanced/version"
746
774
  module ActiveRecord
747
775
  autoload :OracleEnhancedProcedures, "active_record/connection_adapters/oracle_enhanced/procedures"
748
776
  end
777
+
778
+ # Workaround for https://github.com/jruby/jruby/issues/6267
779
+ if RUBY_ENGINE == "jruby"
780
+ require "jruby"
781
+
782
+ class org.jruby::RubyObjectSpace::WeakMap
783
+ field_reader :map
784
+ end
785
+
786
+ class ObjectSpace::WeakMap
787
+ def values
788
+ JRuby.ref(self).map.values.reject(&:nil?)
789
+ end
790
+ end
791
+ end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module OracleEnhanced
6
6
  class Boolean < ActiveModel::Type::Boolean # :nodoc:
7
7
  private
8
-
9
8
  def cast_value(value)
10
9
  # Kind of adding 'n' and 'N' to `FALSE_VALUES`
11
10
  if ["n", "N"].include?(value)
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module OracleEnhanced
6
6
  class Integer < ActiveModel::Type::Integer # :nodoc:
7
7
  private
8
-
9
8
  def max_value
10
9
  ("9" * 38).to_i
11
10
  end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class Oracle < Arel::Visitors::ToSql
6
+ private
7
+ def visit_Arel_Nodes_SelectStatement(o, collector)
8
+ o = order_hacks(o)
9
+
10
+ # if need to select first records without ORDER BY and GROUP BY and without DISTINCT
11
+ # then can use simple ROWNUM in WHERE clause
12
+ if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && !o.cores.first.set_quantifier.class.to_s.match?(/Distinct/)
13
+ o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
14
+ Nodes::SqlLiteral.new("ROWNUM"), o.limit.expr
15
+ )
16
+ return super
17
+ end
18
+
19
+ if o.limit && o.offset
20
+ o = o.dup
21
+ limit = o.limit.expr
22
+ offset = o.offset
23
+ o.offset = nil
24
+ collector << "
25
+ SELECT * FROM (
26
+ SELECT raw_sql_.*, rownum raw_rnum_
27
+ FROM ("
28
+
29
+ collector = super(o, collector)
30
+
31
+ if offset.expr.is_a? Nodes::BindParam
32
+ collector << ") raw_sql_ WHERE rownum <= ("
33
+ collector = visit offset.expr, collector
34
+ collector << " + "
35
+ collector = visit limit, collector
36
+ collector << ") ) WHERE raw_rnum_ > "
37
+ collector = visit offset.expr, collector
38
+ return collector
39
+ else
40
+ collector << ") raw_sql_
41
+ WHERE rownum <= #{offset.expr.to_i + limit}
42
+ )
43
+ WHERE "
44
+ return visit(offset, collector)
45
+ end
46
+ end
47
+
48
+ if o.limit
49
+ o = o.dup
50
+ limit = o.limit.expr
51
+ collector << "SELECT * FROM ("
52
+ collector = super(o, collector)
53
+ collector << ") WHERE ROWNUM <= "
54
+ return visit limit, collector
55
+ end
56
+
57
+ if o.offset
58
+ o = o.dup
59
+ offset = o.offset
60
+ o.offset = nil
61
+ collector << "SELECT * FROM (
62
+ SELECT raw_sql_.*, rownum raw_rnum_
63
+ FROM ("
64
+ collector = super(o, collector)
65
+ collector << ") raw_sql_
66
+ )
67
+ WHERE "
68
+ return visit offset, collector
69
+ end
70
+
71
+ super
72
+ end
73
+
74
+ def visit_Arel_Nodes_Limit(o, collector)
75
+ collector
76
+ end
77
+
78
+ def visit_Arel_Nodes_Offset(o, collector)
79
+ collector << "raw_rnum_ > "
80
+ visit o.expr, collector
81
+ end
82
+
83
+ def visit_Arel_Nodes_Except(o, collector)
84
+ collector << "( "
85
+ collector = infix_value o, collector, " MINUS "
86
+ collector << " )"
87
+ end
88
+
89
+ ##
90
+ # To avoid ORA-01795: maximum number of expressions in a list is 1000
91
+ # tell ActiveRecord to limit us to 1000 ids at a time
92
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
93
+ in_clause_length = @connection.in_clause_length
94
+ values = o.casted_values.map { |v| @connection.quote(v) }
95
+ column_name = quote_table_name(o.table_name) + "." + quote_column_name(o.column_name)
96
+ operator =
97
+ if o.type == :in
98
+ " IN ("
99
+ else
100
+ " NOT IN ("
101
+ end
102
+
103
+ if !Array === values || values.length <= in_clause_length
104
+ collector << column_name
105
+ collector << operator
106
+
107
+ expr =
108
+ if values.empty?
109
+ @connection.quote(nil)
110
+ else
111
+ values.join(",")
112
+ end
113
+
114
+ collector << expr
115
+ collector << ")"
116
+ else
117
+ separator =
118
+ if o.type == :in
119
+ " OR "
120
+ else
121
+ " AND "
122
+ end
123
+ collector << "("
124
+ values.each_slice(in_clause_length).each_with_index do |valuez, i|
125
+ collector << separator unless i == 0
126
+ collector << column_name
127
+ collector << operator
128
+ collector << valuez.join(",")
129
+ collector << ")"
130
+ end
131
+ collector << ")"
132
+ end
133
+
134
+ collector
135
+ end
136
+
137
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
138
+ # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
139
+ if o.orders.any? && o.limit.nil?
140
+ # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
141
+ # otherwise let the user deal with the error
142
+ o = o.dup
143
+ o.orders = []
144
+ end
145
+
146
+ super
147
+ end
148
+
149
+ ###
150
+ # Hacks for the order clauses specific to Oracle
151
+ def order_hacks(o)
152
+ return o if o.orders.empty?
153
+ return o unless o.cores.any? do |core|
154
+ core.projections.any? do |projection|
155
+ /FIRST_VALUE/ === projection
156
+ end
157
+ end
158
+ # Previous version with join and split broke ORDER BY clause
159
+ # if it contained functions with several arguments (separated by ',').
160
+ #
161
+ # orders = o.orders.map { |x| visit x }.join(', ').split(',')
162
+ orders = o.orders.map do |x|
163
+ string = visit(x, Arel::Collectors::SQLString.new).value
164
+ if string.include?(",")
165
+ split_order_string(string)
166
+ else
167
+ string
168
+ end
169
+ end.flatten
170
+ o.orders = []
171
+ orders.each_with_index do |order, i|
172
+ o.orders <<
173
+ Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i.match?(order)}")
174
+ end
175
+ o
176
+ end
177
+
178
+ # Split string by commas but count opening and closing brackets
179
+ # and ignore commas inside brackets.
180
+ def split_order_string(string)
181
+ array = []
182
+ i = 0
183
+ string.split(",").each do |part|
184
+ if array[i]
185
+ array[i] << "," << part
186
+ else
187
+ # to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
188
+ array[i] = part.to_s
189
+ end
190
+ i += 1 if array[i].count("(") == array[i].count(")")
191
+ end
192
+ array
193
+ end
194
+
195
+ def visit_Arel_Nodes_BindParam(o, collector)
196
+ collector.add_bind(o.value) { |i| ":a#{i}" }
197
+ end
198
+
199
+ def is_distinct_from(o, collector)
200
+ collector << "DECODE("
201
+ collector = visit [o.left, o.right, 0, 1], collector
202
+ collector << ")"
203
+ end
204
+
205
+ # Oracle will occur an error `ORA-00907: missing right parenthesis`
206
+ # when using `ORDER BY` in `UPDATE` or `DELETE`'s subquery.
207
+ #
208
+ # This method has been overridden based on the following code.
209
+ # https://github.com/rails/rails/blob/v6.1.0.rc1/activerecord/lib/arel/visitors/to_sql.rb#L815-L825
210
+ def build_subselect(key, o)
211
+ stmt = super
212
+ stmt.orders = [] # `orders` will never be set to prevent `ORA-00907`.
213
+ stmt
214
+ end
215
+ end
216
+ end
217
+ end