activerecord-oracle_enhanced-adapter 6.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +112 -0
- data/README.md +12 -1
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +3 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +0 -9
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +64 -47
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +4 -5
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +3 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +1 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +12 -7
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +0 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +38 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +16 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +65 -59
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +35 -34
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +64 -21
- data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
- data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
- data/lib/arel/visitors/oracle.rb +217 -0
- data/lib/arel/visitors/oracle12.rb +124 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +35 -3
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +6 -1
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +0 -1
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +28 -1
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +2 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +122 -0
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +4 -2
- data/spec/spec_config.yaml.template +2 -2
- data/spec/spec_helper.rb +13 -4
- 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, "
|
12
|
-
SELECT
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
267
|
-
SELECT
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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, "
|
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
|
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, "
|
642
|
-
SELECT
|
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
|
@@ -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
|