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.
- checksums.yaml +4 -4
- data/History.md +390 -21
- data/README.md +35 -8
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
- data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
- data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
- data/lib/arel/visitors/oracle.rb +221 -0
- data/lib/arel/visitors/oracle12.rb +128 -0
- data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
- data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
- data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
- data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
- data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
- data/spec/spec_config.yaml.template +2 -2
- data/spec/spec_helper.rb +13 -2
- metadata +52 -30
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "openssl"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
@@ -10,13 +10,18 @@ module ActiveRecord
|
|
10
10
|
#
|
11
11
|
# see: abstract/schema_statements.rb
|
12
12
|
|
13
|
-
def tables
|
14
|
-
select_values(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
def tables # :nodoc:
|
14
|
+
select_values(<<~SQL.squish, "SCHEMA")
|
15
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
|
16
|
+
DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name)
|
17
|
+
FROM all_tables
|
18
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
19
|
+
AND secondary = 'N'
|
20
|
+
minus
|
21
|
+
SELECT DECODE(mview_name, UPPER(mview_name), LOWER(mview_name), mview_name)
|
22
|
+
FROM all_mviews
|
23
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
24
|
+
SQL
|
20
25
|
end
|
21
26
|
|
22
27
|
def data_sources
|
@@ -39,64 +44,66 @@ module ActiveRecord
|
|
39
44
|
table_owner, table_name = default_owner, real_name
|
40
45
|
end
|
41
46
|
|
42
|
-
select_values(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
select_values(<<~SQL.squish, "SCHEMA", [bind_string("owner", table_owner), bind_string("table_name", table_name)]).any?
|
48
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ owner, table_name
|
49
|
+
FROM all_tables
|
50
|
+
WHERE owner = :owner
|
51
|
+
AND table_name = :table_name
|
52
|
+
SQL
|
48
53
|
end
|
49
54
|
|
50
55
|
def data_source_exists?(table_name)
|
51
|
-
(_owner,
|
56
|
+
(_owner, _table_name) = @connection.describe(table_name)
|
52
57
|
true
|
53
58
|
rescue
|
54
59
|
false
|
55
60
|
end
|
56
61
|
|
57
62
|
def views # :nodoc:
|
58
|
-
select_values(
|
59
|
-
SELECT
|
63
|
+
select_values(<<~SQL.squish, "SCHEMA")
|
64
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
|
65
|
+
LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
60
66
|
SQL
|
61
67
|
end
|
62
68
|
|
63
|
-
def materialized_views
|
64
|
-
select_values(
|
65
|
-
SELECT
|
69
|
+
def materialized_views # :nodoc:
|
70
|
+
select_values(<<~SQL.squish, "SCHEMA")
|
71
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */
|
72
|
+
LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
66
73
|
SQL
|
67
74
|
end
|
68
75
|
|
69
76
|
# get synonyms for schema dump
|
70
77
|
def synonyms
|
71
|
-
result = select_all(
|
72
|
-
|
73
|
-
|
74
|
-
|
78
|
+
result = select_all(<<~SQL.squish, "SCHEMA")
|
79
|
+
SELECT synonym_name, table_owner, table_name
|
80
|
+
FROM all_synonyms where owner = SYS_CONTEXT('userenv', 'current_schema')
|
81
|
+
SQL
|
75
82
|
|
76
83
|
result.collect do |row|
|
77
84
|
OracleEnhanced::SynonymDefinition.new(oracle_downcase(row["synonym_name"]),
|
78
|
-
oracle_downcase(row["table_owner"]), oracle_downcase(row["table_name"])
|
85
|
+
oracle_downcase(row["table_owner"]), oracle_downcase(row["table_name"]))
|
79
86
|
end
|
80
87
|
end
|
81
88
|
|
82
|
-
def indexes(table_name)
|
83
|
-
(
|
89
|
+
def indexes(table_name) # :nodoc:
|
90
|
+
(_owner, table_name) = @connection.describe(table_name)
|
84
91
|
default_tablespace_name = default_tablespace
|
85
92
|
|
86
|
-
result = select_all(
|
87
|
-
SELECT LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
93
|
+
result = select_all(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name)])
|
94
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
88
95
|
i.index_type, i.ityp_owner, i.ityp_name, i.parameters,
|
89
96
|
LOWER(i.tablespace_name) AS tablespace_name,
|
90
97
|
LOWER(c.column_name) AS column_name, e.column_expression,
|
91
98
|
atc.virtual_column
|
92
|
-
FROM all_indexes
|
93
|
-
JOIN all_ind_columns
|
94
|
-
LEFT OUTER JOIN all_ind_expressions
|
99
|
+
FROM all_indexes i
|
100
|
+
JOIN all_ind_columns c ON c.index_name = i.index_name AND c.index_owner = i.owner
|
101
|
+
LEFT OUTER JOIN all_ind_expressions e ON e.index_name = i.index_name AND
|
95
102
|
e.index_owner = i.owner AND e.column_position = c.column_position
|
96
|
-
LEFT OUTER JOIN all_tab_cols
|
103
|
+
LEFT OUTER JOIN all_tab_cols atc ON i.table_name = atc.table_name AND
|
97
104
|
c.column_name = atc.column_name AND i.owner = atc.owner AND atc.hidden_column = 'NO'
|
98
|
-
WHERE i.owner =
|
99
|
-
AND i.table_owner =
|
105
|
+
WHERE i.owner = SYS_CONTEXT('userenv', 'current_schema')
|
106
|
+
AND i.table_owner = SYS_CONTEXT('userenv', 'current_schema')
|
100
107
|
AND i.table_name = :table_name
|
101
108
|
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
102
109
|
WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
|
@@ -113,10 +120,10 @@ module ActiveRecord
|
|
113
120
|
statement_parameters = nil
|
114
121
|
if row["index_type"] == "DOMAIN" && row["ityp_owner"] == "CTXSYS" && row["ityp_name"] == "CONTEXT"
|
115
122
|
procedure_name = default_datastore_procedure(row["index_name"])
|
116
|
-
source = select_values(
|
117
|
-
SELECT text
|
118
|
-
FROM all_source
|
119
|
-
WHERE owner =
|
123
|
+
source = select_values(<<~SQL.squish, "SCHEMA", [bind_string("procedure_name", procedure_name.upcase)]).join
|
124
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ text
|
125
|
+
FROM all_source
|
126
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
120
127
|
AND name = :procedure_name
|
121
128
|
ORDER BY line
|
122
129
|
SQL
|
@@ -192,19 +199,19 @@ module ActiveRecord
|
|
192
199
|
# t.string :last_name, :comment => “Surname”
|
193
200
|
# end
|
194
201
|
|
195
|
-
def create_table(table_name,
|
196
|
-
create_sequence =
|
197
|
-
td = create_table_definition
|
202
|
+
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
203
|
+
create_sequence = id != false
|
204
|
+
td = create_table_definition(
|
205
|
+
table_name, **options.extract!(:temporary, :options, :as, :comment, :tablespace, :organization)
|
206
|
+
)
|
198
207
|
|
199
|
-
if
|
200
|
-
pk =
|
201
|
-
Base.get_primary_key table_name.to_s.singularize
|
202
|
-
end
|
208
|
+
if id && !td.as
|
209
|
+
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
203
210
|
|
204
211
|
if pk.is_a?(Array)
|
205
212
|
td.primary_keys pk
|
206
213
|
else
|
207
|
-
td.primary_key pk,
|
214
|
+
td.primary_key pk, id, **options
|
208
215
|
end
|
209
216
|
end
|
210
217
|
|
@@ -222,8 +229,10 @@ module ActiveRecord
|
|
222
229
|
yield td if block_given?
|
223
230
|
create_sequence = create_sequence || td.create_sequence
|
224
231
|
|
225
|
-
if
|
226
|
-
drop_table(table_name,
|
232
|
+
if force && data_source_exists?(table_name)
|
233
|
+
drop_table(table_name, force: force, if_exists: true)
|
234
|
+
else
|
235
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
227
236
|
end
|
228
237
|
|
229
238
|
execute schema_creation.accept td
|
@@ -231,27 +240,32 @@ module ActiveRecord
|
|
231
240
|
create_sequence_and_trigger(table_name, options) if create_sequence
|
232
241
|
|
233
242
|
if supports_comments? && !supports_comments_in_create?
|
234
|
-
|
243
|
+
if table_comment = td.comment.presence
|
244
|
+
change_table_comment(table_name, table_comment)
|
245
|
+
end
|
235
246
|
td.columns.each do |column|
|
236
247
|
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
237
248
|
end
|
238
249
|
end
|
239
|
-
td.indexes.each { |c, o| add_index table_name, c, o }
|
250
|
+
td.indexes.each { |c, o| add_index table_name, c, **o }
|
240
251
|
|
241
252
|
rebuild_primary_key_index_to_default_tablespace(table_name, options)
|
242
253
|
end
|
243
254
|
|
244
|
-
def rename_table(table_name, new_name)
|
245
|
-
if new_name.to_s.length >
|
246
|
-
raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{
|
255
|
+
def rename_table(table_name, new_name) # :nodoc:
|
256
|
+
if new_name.to_s.length > DatabaseLimits::IDENTIFIER_MAX_LENGTH
|
257
|
+
raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{DatabaseLimits::IDENTIFIER_MAX_LENGTH} characters"
|
247
258
|
end
|
259
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
260
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
248
261
|
execute "RENAME #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
249
262
|
execute "RENAME #{quote_table_name("#{table_name}_seq")} TO #{default_sequence_name(new_name)}" rescue nil
|
250
263
|
|
251
264
|
rename_table_indexes(table_name, new_name)
|
252
265
|
end
|
253
266
|
|
254
|
-
def drop_table(table_name, options
|
267
|
+
def drop_table(table_name, **options) # :nodoc:
|
268
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
255
269
|
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE CONSTRAINTS' if options[:force] == :cascade}"
|
256
270
|
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
257
271
|
execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
|
@@ -265,7 +279,7 @@ module ActiveRecord
|
|
265
279
|
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
|
266
280
|
|
267
281
|
if supports_multi_insert?
|
268
|
-
versions.inject("INSERT ALL\n"
|
282
|
+
versions.inject(+"INSERT ALL\n") { |sql, version|
|
269
283
|
sql << "INTO #{sm_table} (version) VALUES (#{quote(version)})\n"
|
270
284
|
} << "SELECT * FROM DUAL\n"
|
271
285
|
else
|
@@ -281,17 +295,17 @@ module ActiveRecord
|
|
281
295
|
end
|
282
296
|
end
|
283
297
|
|
284
|
-
def add_index(table_name, column_name, options
|
285
|
-
index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
|
298
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
299
|
+
index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, **options)
|
286
300
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
|
287
301
|
if index_type == "UNIQUE"
|
288
|
-
unless
|
302
|
+
unless /\(.*\)/.match?(quoted_column_names)
|
289
303
|
execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
|
290
304
|
end
|
291
305
|
end
|
292
306
|
end
|
293
307
|
|
294
|
-
def add_index_options(table_name, column_name, comment: nil, **options)
|
308
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
295
309
|
column_names = Array(column_name)
|
296
310
|
index_name = index_name(table_name, column: column_names)
|
297
311
|
|
@@ -300,13 +314,11 @@ module ActiveRecord
|
|
300
314
|
index_type = options[:unique] ? "UNIQUE" : ""
|
301
315
|
index_name = options[:name].to_s if options.key?(:name)
|
302
316
|
tablespace = tablespace_for(:index, options[:tablespace])
|
303
|
-
|
304
|
-
# TODO: This option is used for NOLOGGING, needs better argumetn name
|
317
|
+
# TODO: This option is used for NOLOGGING, needs better argument name
|
305
318
|
index_options = options[:options]
|
306
319
|
|
307
|
-
|
308
|
-
|
309
|
-
end
|
320
|
+
validate_index_length!(table_name, index_name, options.fetch(:internal, false))
|
321
|
+
|
310
322
|
if table_exists?(table_name) && index_name_exists?(table_name, index_name)
|
311
323
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
312
324
|
end
|
@@ -317,15 +329,17 @@ module ActiveRecord
|
|
317
329
|
|
318
330
|
# Remove the given index from the table.
|
319
331
|
# Gives warning if index does not exist
|
320
|
-
def remove_index(table_name,
|
321
|
-
|
332
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
333
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
334
|
+
|
335
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
322
336
|
# TODO: It should execute only when index_type == "UNIQUE"
|
323
337
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
|
324
338
|
execute "DROP INDEX #{quote_column_name(index_name)}"
|
325
339
|
end
|
326
340
|
|
327
341
|
# returned shortened index name if default is too large
|
328
|
-
def index_name(table_name, options)
|
342
|
+
def index_name(table_name, options) # :nodoc:
|
329
343
|
default_name = super(table_name, options).to_s
|
330
344
|
# sometimes options can be String or Array with column names
|
331
345
|
options = {} unless options.is_a?(Hash)
|
@@ -341,7 +355,7 @@ module ActiveRecord
|
|
341
355
|
end
|
342
356
|
# generate unique name using hash function
|
343
357
|
if shortened_name.length > identifier_max_length
|
344
|
-
shortened_name = "i" + Digest::SHA1.hexdigest(default_name)[0, identifier_max_length - 1]
|
358
|
+
shortened_name = "i" + OpenSSL::Digest::SHA1.hexdigest(default_name)[0, identifier_max_length - 1]
|
345
359
|
end
|
346
360
|
@logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
|
347
361
|
shortened_name
|
@@ -354,18 +368,18 @@ module ActiveRecord
|
|
354
368
|
#
|
355
369
|
# Will always query database and not index cache.
|
356
370
|
def index_name_exists?(table_name, index_name)
|
357
|
-
(
|
358
|
-
result = select_value(
|
359
|
-
SELECT 1 FROM all_indexes
|
360
|
-
WHERE i.owner = '
|
361
|
-
AND i.table_owner = '
|
362
|
-
AND i.table_name =
|
363
|
-
AND i.index_name =
|
371
|
+
(_owner, table_name) = @connection.describe(table_name)
|
372
|
+
result = select_value(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name), bind_string("index_name", index_name.to_s.upcase)])
|
373
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ 1 FROM all_indexes i
|
374
|
+
WHERE i.owner = SYS_CONTEXT('userenv', 'current_schema')
|
375
|
+
AND i.table_owner = SYS_CONTEXT('userenv', 'current_schema')
|
376
|
+
AND i.table_name = :table_name
|
377
|
+
AND i.index_name = :index_name
|
364
378
|
SQL
|
365
379
|
result == 1
|
366
380
|
end
|
367
381
|
|
368
|
-
def rename_index(table_name, old_name, new_name)
|
382
|
+
def rename_index(table_name, old_name, new_name) # :nodoc:
|
369
383
|
validate_index_length!(table_name, new_name)
|
370
384
|
execute "ALTER INDEX #{quote_column_name(old_name)} rename to #{quote_column_name(new_name)}"
|
371
385
|
end
|
@@ -376,10 +390,10 @@ module ActiveRecord
|
|
376
390
|
#
|
377
391
|
# add_synonym :posts, "blog.posts"
|
378
392
|
# add_synonym :posts_seq, "blog.posts_seq"
|
379
|
-
# add_synonym :employees, "hr.employees
|
393
|
+
# add_synonym :employees, "hr.employees", :force => true
|
380
394
|
#
|
381
395
|
def add_synonym(name, table_name, options = {})
|
382
|
-
sql = "CREATE"
|
396
|
+
sql = +"CREATE"
|
383
397
|
if options[:force] == true
|
384
398
|
sql << " OR REPLACE"
|
385
399
|
end
|
@@ -396,14 +410,14 @@ module ActiveRecord
|
|
396
410
|
execute "DROP SYNONYM #{quote_table_name(name)}"
|
397
411
|
end
|
398
412
|
|
399
|
-
def add_reference(table_name,
|
400
|
-
OracleEnhanced::ReferenceDefinition.new(
|
413
|
+
def add_reference(table_name, ref_name, **options)
|
414
|
+
OracleEnhanced::ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
|
401
415
|
end
|
402
416
|
|
403
|
-
def add_column(table_name, column_name, type, options
|
417
|
+
def add_column(table_name, column_name, type, **options) # :nodoc:
|
404
418
|
type = aliased_types(type.to_s, type)
|
405
419
|
at = create_alter_table table_name
|
406
|
-
at.add_column(column_name, type, options)
|
420
|
+
at.add_column(column_name, type, **options)
|
407
421
|
add_column_sql = schema_creation.accept at
|
408
422
|
add_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, table_name, column_name)
|
409
423
|
execute add_column_sql
|
@@ -417,14 +431,14 @@ module ActiveRecord
|
|
417
431
|
fallback
|
418
432
|
end
|
419
433
|
|
420
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
434
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
421
435
|
default = extract_new_default_value(default_or_changes)
|
422
436
|
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
423
437
|
ensure
|
424
438
|
clear_table_columns_cache(table_name)
|
425
439
|
end
|
426
440
|
|
427
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
441
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
428
442
|
column = column_for(table_name, column_name)
|
429
443
|
|
430
444
|
unless null || default.nil?
|
@@ -434,7 +448,7 @@ module ActiveRecord
|
|
434
448
|
change_column table_name, column_name, column.sql_type, null: null
|
435
449
|
end
|
436
450
|
|
437
|
-
def change_column(table_name, column_name, type, options
|
451
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
438
452
|
column = column_for(table_name, column_name)
|
439
453
|
|
440
454
|
# remove :null option if its value is the same as current column definition
|
@@ -447,7 +461,7 @@ module ActiveRecord
|
|
447
461
|
end
|
448
462
|
|
449
463
|
td = create_table_definition(table_name)
|
450
|
-
cd = td.new_column_definition(column.name, type, options)
|
464
|
+
cd = td.new_column_definition(column.name, type, **options)
|
451
465
|
change_column_stmt = schema_creation.accept cd
|
452
466
|
change_column_stmt << tablespace_for((type_to_sql(type).downcase.to_sym), nil, options[:table_name], options[:column_name]) if type
|
453
467
|
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{change_column_stmt}"
|
@@ -458,21 +472,30 @@ module ActiveRecord
|
|
458
472
|
clear_table_columns_cache(table_name)
|
459
473
|
end
|
460
474
|
|
461
|
-
def rename_column(table_name, column_name, new_column_name)
|
475
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
462
476
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
|
463
477
|
rename_column_indexes(table_name, column_name, new_column_name)
|
464
478
|
ensure
|
465
479
|
clear_table_columns_cache(table_name)
|
466
480
|
end
|
467
481
|
|
468
|
-
def remove_column(table_name, column_name, type = nil, options = {})
|
482
|
+
def remove_column(table_name, column_name, type = nil, options = {}) # :nodoc:
|
469
483
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)} CASCADE CONSTRAINTS"
|
470
484
|
ensure
|
471
485
|
clear_table_columns_cache(table_name)
|
472
486
|
end
|
473
487
|
|
474
|
-
def
|
488
|
+
def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
|
489
|
+
quoted_column_names = column_names.map { |column_name| quote_column_name(column_name) }.join(", ")
|
490
|
+
|
491
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP (#{quoted_column_names}) CASCADE CONSTRAINTS"
|
492
|
+
ensure
|
493
|
+
clear_table_columns_cache(table_name)
|
494
|
+
end
|
495
|
+
|
496
|
+
def change_table_comment(table_name, comment_or_changes)
|
475
497
|
clear_cache!
|
498
|
+
comment = extract_new_comment_value(comment_or_changes)
|
476
499
|
if comment.nil?
|
477
500
|
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS ''"
|
478
501
|
else
|
@@ -480,16 +503,18 @@ module ActiveRecord
|
|
480
503
|
end
|
481
504
|
end
|
482
505
|
|
483
|
-
def change_column_comment(table_name, column_name,
|
506
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
484
507
|
clear_cache!
|
508
|
+
comment = extract_new_comment_value(comment_or_changes)
|
485
509
|
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS '#{comment}'"
|
486
510
|
end
|
487
511
|
|
488
|
-
def table_comment(table_name)
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
512
|
+
def table_comment(table_name) # :nodoc:
|
513
|
+
# TODO
|
514
|
+
(_owner, table_name) = @connection.describe(table_name)
|
515
|
+
select_value(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name)])
|
516
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ comments FROM all_tab_comments
|
517
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
493
518
|
AND table_name = :table_name
|
494
519
|
SQL
|
495
520
|
end
|
@@ -500,19 +525,19 @@ module ActiveRecord
|
|
500
525
|
end
|
501
526
|
end
|
502
527
|
|
503
|
-
def column_comment(table_name, column_name)
|
528
|
+
def column_comment(table_name, column_name) # :nodoc:
|
504
529
|
# TODO: it does not exist in Abstract adapter
|
505
|
-
(
|
506
|
-
select_value(
|
507
|
-
SELECT comments FROM all_col_comments
|
508
|
-
WHERE owner =
|
530
|
+
(_owner, table_name) = @connection.describe(table_name)
|
531
|
+
select_value(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name), bind_string("column_name", column_name.upcase)])
|
532
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ comments FROM all_col_comments
|
533
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
509
534
|
AND table_name = :table_name
|
510
535
|
AND column_name = :column_name
|
511
536
|
SQL
|
512
537
|
end
|
513
538
|
|
514
539
|
# Maps logical Rails types to Oracle-specific data types.
|
515
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
|
540
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
|
516
541
|
# Ignore options for :text, :ntext and :binary columns
|
517
542
|
return super(type) if ["text", "ntext", "binary"].include?(type.to_s)
|
518
543
|
|
@@ -520,27 +545,27 @@ module ActiveRecord
|
|
520
545
|
end
|
521
546
|
|
522
547
|
def tablespace(table_name)
|
523
|
-
select_value(
|
524
|
-
SELECT tablespace_name
|
548
|
+
select_value(<<~SQL.squish, "SCHEMA")
|
549
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ tablespace_name
|
525
550
|
FROM all_tables
|
526
551
|
WHERE table_name='#{table_name.to_s.upcase}'
|
527
|
-
AND owner = SYS_CONTEXT('userenv', '
|
552
|
+
AND owner = SYS_CONTEXT('userenv', 'current_schema')
|
528
553
|
SQL
|
529
554
|
end
|
530
555
|
|
531
556
|
# get table foreign keys for schema dump
|
532
|
-
def foreign_keys(table_name)
|
533
|
-
(
|
557
|
+
def foreign_keys(table_name) # :nodoc:
|
558
|
+
(_owner, desc_table_name) = @connection.describe(table_name)
|
534
559
|
|
535
|
-
fk_info = select_all(
|
536
|
-
SELECT r.table_name to_table
|
560
|
+
fk_info = select_all(<<~SQL.squish, "SCHEMA", [bind_string("desc_table_name", desc_table_name)])
|
561
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ r.table_name to_table
|
537
562
|
,rc.column_name references_column
|
538
563
|
,cc.column_name
|
539
564
|
,c.constraint_name name
|
540
565
|
,c.delete_rule
|
541
|
-
FROM all_constraints
|
542
|
-
all_constraints
|
543
|
-
WHERE c.owner =
|
566
|
+
FROM all_constraints c, all_cons_columns cc,
|
567
|
+
all_constraints r, all_cons_columns rc
|
568
|
+
WHERE c.owner = SYS_CONTEXT('userenv', 'current_schema')
|
544
569
|
AND c.table_name = :desc_table_name
|
545
570
|
AND c.constraint_type = 'R'
|
546
571
|
AND cc.owner = c.owner
|
@@ -573,13 +598,13 @@ module ActiveRecord
|
|
573
598
|
|
574
599
|
# REFERENTIAL INTEGRITY ====================================
|
575
600
|
|
576
|
-
def disable_referential_integrity(&block)
|
577
|
-
old_constraints = select_all(
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
601
|
+
def disable_referential_integrity(&block) # :nodoc:
|
602
|
+
old_constraints = select_all(<<~SQL.squish, "SCHEMA")
|
603
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ constraint_name, owner, table_name
|
604
|
+
FROM all_constraints
|
605
|
+
WHERE constraint_type = 'R'
|
606
|
+
AND status = 'ENABLED'
|
607
|
+
AND owner = SYS_CONTEXT('userenv', 'current_schema')
|
583
608
|
SQL
|
584
609
|
begin
|
585
610
|
old_constraints.each do |constraint|
|
@@ -594,7 +619,7 @@ module ActiveRecord
|
|
594
619
|
end
|
595
620
|
|
596
621
|
def create_alter_table(name)
|
597
|
-
OracleEnhanced::AlterTable.new create_table_definition(name
|
622
|
+
OracleEnhanced::AlterTable.new create_table_definition(name)
|
598
623
|
end
|
599
624
|
|
600
625
|
def update_table_definition(table_name, base)
|
@@ -606,13 +631,12 @@ module ActiveRecord
|
|
606
631
|
end
|
607
632
|
|
608
633
|
private
|
609
|
-
|
610
634
|
def schema_creation
|
611
635
|
OracleEnhanced::SchemaCreation.new self
|
612
636
|
end
|
613
637
|
|
614
|
-
def create_table_definition(
|
615
|
-
OracleEnhanced::TableDefinition.new(
|
638
|
+
def create_table_definition(name, **options)
|
639
|
+
OracleEnhanced::TableDefinition.new(self, name, **options)
|
616
640
|
end
|
617
641
|
|
618
642
|
def new_column_from_field(table_name, field)
|
@@ -634,9 +658,9 @@ module ActiveRecord
|
|
634
658
|
# If a default contains a newline these cleanup regexes need to
|
635
659
|
# match newlines.
|
636
660
|
field["data_default"].sub!(/^'(.*)'$/m, '\1')
|
637
|
-
field["data_default"] = nil if
|
661
|
+
field["data_default"] = nil if /^(null|empty_[bc]lob\(\))$/i.match?(field["data_default"])
|
638
662
|
# TODO: Needs better fix to fallback "N" to false
|
639
|
-
field["data_default"] = false if
|
663
|
+
field["data_default"] = false if field["data_default"] == "N" && OracleEnhancedAdapter.emulate_booleans_from_strings
|
640
664
|
end
|
641
665
|
|
642
666
|
type_metadata = fetch_type_metadata(field["sql_type"], is_virtual)
|
@@ -646,9 +670,8 @@ module ActiveRecord
|
|
646
670
|
default_value,
|
647
671
|
type_metadata,
|
648
672
|
field["nullable"] == "Y",
|
649
|
-
|
650
|
-
|
651
|
-
)
|
673
|
+
comment: field["column_comment"]
|
674
|
+
)
|
652
675
|
end
|
653
676
|
|
654
677
|
def fetch_type_metadata(sql_type, virtual = nil)
|
@@ -656,7 +679,7 @@ module ActiveRecord
|
|
656
679
|
end
|
657
680
|
|
658
681
|
def tablespace_for(obj_type, tablespace_option, table_name = nil, column_name = nil)
|
659
|
-
tablespace_sql = ""
|
682
|
+
tablespace_sql = +""
|
660
683
|
if tablespace = (tablespace_option || default_tablespace_for(obj_type))
|
661
684
|
if [:blob, :clob, :nclob].include?(obj_type.to_sym)
|
662
685
|
tablespace_sql << " LOB (#{quote_column_name(column_name)}) STORE AS #{column_name.to_s[0..10]}_#{table_name.to_s[0..14]}_ls (TABLESPACE #{tablespace})"
|
@@ -679,33 +702,11 @@ module ActiveRecord
|
|
679
702
|
end
|
680
703
|
|
681
704
|
def create_sequence_and_trigger(table_name, options)
|
705
|
+
# TODO: Needs rename since no triggers created
|
706
|
+
# This method will be removed since sequence will not be created separately
|
682
707
|
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
683
708
|
seq_start_value = options[:sequence_start_value] || default_sequence_start_value
|
684
709
|
execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{seq_start_value}"
|
685
|
-
|
686
|
-
create_primary_key_trigger(table_name, options) if options[:primary_key_trigger]
|
687
|
-
end
|
688
|
-
|
689
|
-
def create_primary_key_trigger(table_name, options)
|
690
|
-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
691
|
-
trigger_name = options[:trigger_name] || default_trigger_name(table_name)
|
692
|
-
primary_key = options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)
|
693
|
-
execute <<-SQL
|
694
|
-
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
|
695
|
-
BEFORE INSERT ON #{quote_table_name(table_name)} FOR EACH ROW
|
696
|
-
BEGIN
|
697
|
-
IF inserting THEN
|
698
|
-
IF :new.#{quote_column_name(primary_key)} IS NULL THEN
|
699
|
-
SELECT #{quote_table_name(seq_name)}.NEXTVAL INTO :new.#{quote_column_name(primary_key)} FROM dual;
|
700
|
-
END IF;
|
701
|
-
END IF;
|
702
|
-
END;
|
703
|
-
SQL
|
704
|
-
end
|
705
|
-
|
706
|
-
def default_trigger_name(table_name)
|
707
|
-
# truncate table name if necessary to fit in max length of identifier
|
708
|
-
"#{table_name.to_s[0, table_name_length - 4]}_pkt"
|
709
710
|
end
|
710
711
|
|
711
712
|
def rebuild_primary_key_index_to_default_tablespace(table_name, options)
|
@@ -713,8 +714,8 @@ module ActiveRecord
|
|
713
714
|
|
714
715
|
return unless tablespace
|
715
716
|
|
716
|
-
index_name = select_value(
|
717
|
-
SELECT index_name FROM all_constraints
|
717
|
+
index_name = select_value(<<~SQL.squish, "Index name for primary key", [bind_string("table_name", table_name.upcase)])
|
718
|
+
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('11.2.0.2') */ index_name FROM all_constraints
|
718
719
|
WHERE table_name = :table_name
|
719
720
|
AND constraint_type = 'P'
|
720
721
|
AND owner = SYS_CONTEXT('userenv', 'current_schema')
|