activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +390 -21
  3. data/README.md +35 -8
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
  24. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
  25. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
  26. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
  27. data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
  28. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  29. data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
  30. data/lib/arel/visitors/oracle.rb +221 -0
  31. data/lib/arel/visitors/oracle12.rb +128 -0
  32. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
  34. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
  35. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
  37. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
  38. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
  39. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
  40. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
  41. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
  42. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
  43. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
  44. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
  45. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
  46. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  47. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  48. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
  49. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
  50. data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
  51. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
  52. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
  53. data/spec/spec_config.yaml.template +2 -2
  54. data/spec/spec_helper.rb +13 -2
  55. metadata +52 -30
  56. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest/sha1"
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 #:nodoc:
14
- select_values(<<-SQL.strip.gsub(/\s+/, " "), "tables")
15
- SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name)
16
- FROM all_tables
17
- WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
18
- AND secondary = 'N'
19
- SQL
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(<<-SQL.strip.gsub(/\s+/, " "), "table exists", [bind_string("owner", table_owner), bind_string("table_name", table_name)]).any?
43
- SELECT owner, table_name
44
- FROM all_tables
45
- WHERE owner = :owner
46
- AND table_name = :table_name
47
- SQL
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, table_name, _db_link) = @connection.describe(table_name)
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(<<-SQL.strip.gsub(/\s+/, " "), "views")
59
- SELECT LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
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 #:nodoc:
64
- select_values(<<-SQL.strip.gsub(/\s+/, " "), "materialized views")
65
- SELECT LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
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(<<-SQL.strip.gsub(/\s+/, " "), "synonyms")
72
- SELECT synonym_name, table_owner, table_name, db_link
73
- FROM all_synonyms where owner = SYS_CONTEXT('userenv', 'session_user')
74
- SQL
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"]), oracle_downcase(row["db_link"]))
85
+ oracle_downcase(row["table_owner"]), oracle_downcase(row["table_name"]))
79
86
  end
80
87
  end
81
88
 
82
- def indexes(table_name) #:nodoc:
83
- (owner, table_name, db_link) = @connection.describe(table_name)
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(<<-SQL.strip.gsub(/\s+/, " "), "indexes", [bind_string("owner", owner), bind_string("owner", owner), bind_string("table_name", table_name)])
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#{db_link} i
93
- JOIN all_ind_columns#{db_link} c ON c.index_name = i.index_name AND c.index_owner = i.owner
94
- LEFT OUTER JOIN all_ind_expressions#{db_link} e ON e.index_name = i.index_name AND
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#{db_link} atc ON i.table_name = atc.table_name AND
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 = :owner
99
- AND i.table_owner = :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(<<-SQL.strip.gsub(/\s+/, " "), "procedure", [bind_string("owner", owner), bind_string("procedure_name", procedure_name.upcase)]).join
117
- SELECT text
118
- FROM all_source#{db_link}
119
- WHERE owner = :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, comment: nil, **options)
196
- create_sequence = options[:id] != false
197
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], options[:tablespace], options[:organization], comment: comment
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 options[:id] != false && !options[:as]
200
- pk = options.fetch(:primary_key) do
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, options.fetch(:id, :primary_key), options
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 options[:force] && data_source_exists?(table_name)
226
- drop_table(table_name, options)
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
- change_table_comment(table_name, comment) if comment
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) #:nodoc:
245
- if new_name.to_s.length > table_name_length
246
- raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
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 = {}) #:nodoc:
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".dup) { |sql, version|
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 = {}) #:nodoc:
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 quoted_column_names =~ /\(.*\)/
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) #:nodoc:
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
- max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
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
- if index_name.to_s.length > max_index_length
308
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
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, options = {}) #:nodoc:
321
- index_name = index_name_for_remove(table_name, options)
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) #:nodoc:
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
- (owner, table_name, db_link) = @connection.describe(table_name)
358
- result = select_value(<<-SQL.strip.gsub(/\s+/, " "), "index name exists")
359
- SELECT 1 FROM all_indexes#{db_link} i
360
- WHERE i.owner = '#{owner}'
361
- AND i.table_owner = '#{owner}'
362
- AND i.table_name = '#{table_name}'
363
- AND i.index_name = '#{index_name.to_s.upcase}'
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) #:nodoc:
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@dblink", :force => true
393
+ # add_synonym :employees, "hr.employees", :force => true
380
394
  #
381
395
  def add_synonym(name, table_name, options = {})
382
- sql = "CREATE".dup
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, *args)
400
- OracleEnhanced::ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
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 = {}) #:nodoc:
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) #:nodoc:
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) #:nodoc:
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 = {}) #:nodoc:
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) #:nodoc:
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 = {}) #:nodoc:
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 change_table_comment(table_name, comment)
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, comment) #:nodoc:
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) #:nodoc:
489
- (owner, table_name, db_link) = @connection.describe(table_name)
490
- select_value(<<-SQL.strip.gsub(/\s+/, " "), "Table comment", [bind_string("owner", owner), bind_string("table_name", table_name)])
491
- SELECT comments FROM all_tab_comments#{db_link}
492
- WHERE owner = :owner
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) #:nodoc:
528
+ def column_comment(table_name, column_name) # :nodoc:
504
529
  # TODO: it does not exist in Abstract adapter
505
- (owner, table_name, db_link) = @connection.describe(table_name)
506
- select_value(<<-SQL.strip.gsub(/\s+/, " "), "Column comment", [bind_string("owner", owner), bind_string("table_name", table_name), bind_string("column_name", column_name.upcase)])
507
- SELECT comments FROM all_col_comments#{db_link}
508
- WHERE owner = :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, **) #:nodoc:
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(<<-SQL.strip.gsub(/\s+/, " "), "tablespace")
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', 'session_user')
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) #:nodoc:
533
- (owner, desc_table_name, db_link) = @connection.describe(table_name)
557
+ def foreign_keys(table_name) # :nodoc:
558
+ (_owner, desc_table_name) = @connection.describe(table_name)
534
559
 
535
- fk_info = select_all(<<-SQL.strip.gsub(/\s+/, " "), "Foreign Keys", [bind_string("owner", owner), bind_string("desc_table_name", desc_table_name)])
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#{db_link} c, all_cons_columns#{db_link} cc,
542
- all_constraints#{db_link} r, all_cons_columns#{db_link} rc
543
- WHERE c.owner = :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) #:nodoc:
577
- old_constraints = select_all(<<-SQL.strip.gsub(/\s+/, " "), "Foreign Keys to disable and enable")
578
- SELECT constraint_name, owner, table_name
579
- FROM all_constraints
580
- WHERE constraint_type = 'R'
581
- AND status = 'ENABLED'
582
- AND owner = SYS_CONTEXT('userenv', 'session_user')
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, false, {})
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(*args)
615
- OracleEnhanced::TableDefinition.new(*args)
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 field["data_default"] =~ /^(null|empty_[bc]lob\(\))$/i
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 (field["data_default"] == "N" && OracleEnhancedAdapter.emulate_booleans_from_strings)
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
- table_name,
650
- field["column_comment"]
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 = "".dup
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(<<-SQL.strip.gsub(/\s+/, " "), "Index name for primary key", [bind_string("table_name", table_name.upcase)])
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')