activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +390 -21
  3. data/README.md +35 -8
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
  24. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
  25. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
  26. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
  27. data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
  28. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  29. data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
  30. data/lib/arel/visitors/oracle.rb +221 -0
  31. data/lib/arel/visitors/oracle12.rb +128 -0
  32. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
  34. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
  35. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
  37. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
  38. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
  39. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
  40. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
  41. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
  42. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
  43. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
  44. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
  45. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
  46. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  47. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  48. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
  49. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
  50. data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
  51. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
  52. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
  53. data/spec/spec_config.yaml.template +2 -2
  54. data/spec/spec_helper.rb +13 -2
  55. metadata +52 -30
  56. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -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')